From 538c15bd1b63e406f6333e0eabc40fb322be3221 Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 22:40:32 -0600 Subject: [PATCH 01/26] Add the agent-framework dependency group and lock it --- pyproject.toml | 14 ++ uv.lock | 439 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 379 insertions(+), 74 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 744e0eb3..c45e202f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,16 @@ dev = [ "pytest-pretty>=1.3.0", "poethepoet>=0.36.0", ] +agent-framework = [ + # No lower bound while [tool.uv.sources] pins the unreleased plugin; set + # ">=0.1.0" when that entry goes. The mcp sample is its own uv project + # (agent_framework_plugin/mcp) because it needs MCP SDK v2 and other + # groups here still pin v1. + "temporalio-agent-framework ; python_version >= '3.11'", + "agent-framework-openai>=1.17,<2 ; python_version >= '3.11'", + "temporalio[opentelemetry]>=1.36.0,<2 ; python_version >= '3.11'", + "opentelemetry-exporter-otlp-proto-grpc>=1.30.0,<2 ; python_version >= '3.11'", +] bedrock = ["boto3>=1.34.92,<2"] claude-agent-sdk = [ "temporalio-claude-agent-sdk>=0.0.2", @@ -113,6 +123,10 @@ constraint-dependencies = [ "yarl!=1.24.0", ] +[tool.uv.sources] +# Temporary while the plugin is unreleased: remove once temporalio-agent-framework is on PyPI. +temporalio-agent-framework = { git = "https://github.com/temporalio/ai-integrations", subdirectory = "python/agent_framework", branch = "dabh/agent-framework-python" } + [tool.setuptools.packages.find] exclude = ["lambda_worker*"] diff --git a/uv.lock b/uv.lock index 4a075220..14f82451 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 5 +revision = 3 requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.15'", @@ -13,6 +13,37 @@ resolution-markers = [ [manifest] constraints = [{ name = "yarl", specifier = "!=1.24.0" }] +[[package]] +name = "agent-framework-core" +version = "1.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "msgspec" }, + { name = "opentelemetry-api" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/b0/632e1bd56c8d6202b146d81767ef3dc5dff0e25a05442aa0c571ac973612/agent_framework_core-1.26.0.tar.gz", hash = "sha256:7ecd792184c8e706220ea3994fba71aa422a3e8be396a510ec2c4dd014289bca", size = 827413, upload-time = "2026-11-12T18:25:08.694Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/2a/5cc1d3b5274489355e57e273df3c5e330613a0b3e5498bd2bf6265500ed5/agent_framework_core-1.26.0-py3-none-any.whl", hash = "sha256:175b75f07e15d1bb7b6cea9cb35b965ac2528cd96e3eae8f791c85c48c7232b3", size = 889044, upload-time = "2026-11-12T18:25:48.022Z" }, +] + +[[package]] +name = "agent-framework-openai" +version = "1.18.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "openai" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/d9/d09025590ea8138d7dbe364133c4de4981878d4e5254a144005ac4d18924/agent_framework_openai-1.18.2.tar.gz", hash = "sha256:e88cd384af6cdc12a855425fd92d6131519550cc6157afc24796250af8ebb33e", size = 79711, upload-time = "2026-11-12T18:25:28.587Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/e6/cb4bff3b07a50cec20585767c2e1034a1561b103dd290c5ac64f9ba8e87e/agent_framework_openai-1.18.2-py3-none-any.whl", hash = "sha256:f0522f10ced39f9956cdb55956582f1abcbfdda528cf7d9a02c46f2f73e09460", size = 84617, upload-time = "2026-11-12T18:26:08.062Z" }, +] + [[package]] name = "aioboto3" version = "15.1.0" @@ -202,7 +233,8 @@ version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } wheels = [ @@ -251,7 +283,7 @@ dependencies = [ { name = "jiter" }, { name = "pydantic" }, { name = "sniffio" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/fb/57/0b758b08cf4606c94d63a997d67a0063f7438efbaf81cfedd0d7c0c69d67/anthropic-0.103.1.tar.gz", hash = "sha256:21c12f4fc0fdd87a2e80d58479cd0af640062b3cfb82bbfa01c7977acd4defeb", size = 848877, upload-time = "2026-05-19T15:43:27.698Z" } wheels = [ @@ -277,7 +309,7 @@ dependencies = [ { name = "jiter" }, { name = "pydantic" }, { name = "sniffio" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/0f/ca/3cb2c20ee729736fbd4546d5d8b67e818288529fe70cb7a80dbf80aef70b/anthropic-0.121.0.tar.gz", hash = "sha256:e79d6e08ab3376602fc9a70d4d5ea3540817c76cf7e16658bed790834e1833d6", size = 1013292, upload-time = "2026-08-07T17:11:07.241Z" } wheels = [ @@ -301,7 +333,8 @@ dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna", version = "3.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "idna", version = "3.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } wheels = [ @@ -358,7 +391,8 @@ dependencies = [ { name = "boto3" }, { name = "jsonschema" }, { name = "pydantic" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d3/52/feef23ec9392e2321ab889fa491a1a86d5818d35948bc331cd92dae0087c/aws_sam_translator-1.106.0.tar.gz", hash = "sha256:87712ced7eb6835fea2d4e9674ba7268494aa98f5b186ec5ad684245e2707ef7", size = 355440, upload-time = "2025-12-17T19:07:05.078Z" } wheels = [ @@ -384,7 +418,8 @@ version = "4.14.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "soupsieve" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } wheels = [ @@ -540,7 +575,8 @@ dependencies = [ { name = "pyyaml" }, { name = "regex" }, { name = "sympy" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/10/34/e66811016e7709cab78b0cf896437b922d7537986ac727344663b6cc2044/cfn_lint-1.47.1.tar.gz", hash = "sha256:b2eedbcee3aa104602f79933e3ad74c01f0fa1e226b70327118926fd78d8d3f1", size = 3672271, upload-time = "2026-03-24T15:59:34.526Z" } wheels = [ @@ -661,7 +697,7 @@ dependencies = [ { name = "jsonschema" }, { name = "mcp" }, { name = "sniffio" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7f/45/10f00a408b480926b5e340e3507562ede1e30d4ce5b73eea9ccc3468a9e6/claude_agent_sdk-0.2.165.tar.gz", hash = "sha256:1bfa8e7a6bb36e82de9a12324c9bcfa6eebd4f96ee5890f0c656d50445240ff8", size = 365169, upload-time = "2026-10-08T18:18:49.528Z" } wheels = [ @@ -699,7 +735,7 @@ version = "50.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9d/af/182eb91b0df3fe75c4d9f26fe70684569566745f6ba7e5c9c73a862c5252/cryptography-50.0.2.tar.gz", hash = "sha256:7b46165bb56eb4704e2eaaf86f3c940d19154535d9b0ca7d6d590b04060e00d5", size = 880623, upload-time = "2026-09-30T15:30:04.884Z" } wheels = [ @@ -836,7 +872,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -850,7 +886,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "redis" }, { name = "sortedcontainers" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/43/50/b748233c02fa77e5105238190cc9bb58b852eb1c8b1d0763230d3a5b745a/fakeredis-2.35.1.tar.gz", hash = "sha256:5bae5eba7b9d93cb968944ac40936373cf2397ff71667d4b595df65c3d2e413f", size = 189118, upload-time = "2026-04-12T17:05:58.539Z" } wheels = [ @@ -865,7 +901,8 @@ dependencies = [ { name = "annotated-doc" }, { name = "pydantic" }, { name = "starlette" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "typing-inspection" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d5/b1/e5b92c59d2c37817e77c1a8c2fc1f79cdcc04c68253e5406b43e3204cba7/fastapi-0.137.1.tar.gz", hash = "sha256:822360704230d9533d8d9475399613525968aa2f0b5bd2a3ccc9f18c88fd541c", size = 408293, upload-time = "2026-06-15T11:28:20.79Z" } @@ -1186,7 +1223,8 @@ dependencies = [ { name = "requests" }, { name = "starlette" }, { name = "tenacity" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "uvicorn" }, { name = "watchdog" }, { name = "websockets" }, @@ -1237,7 +1275,8 @@ dependencies = [ { name = "requests" }, { name = "sniffio" }, { name = "tenacity" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "websockets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/21/2b/977616ef350e51d3d263eb43be23a91be793544cd937b51c0fac313be233/google_genai-2.29.0.tar.gz", hash = "sha256:124d3eef9ff6b9e4ea548f95ff5b7721683e7990c0a936b006d9e2ab61ea897d", size = 706902, upload-time = "2026-10-07T22:22:57.153Z" } @@ -1355,7 +1394,8 @@ name = "grpcio" version = "1.80.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b7/48/af6173dbca4454f4637a4678b67f52ca7e0c1ed7d5894d89d434fecede05/grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257", size = 12978905, upload-time = "2026-03-30T08:49:10.502Z" } wheels = [ @@ -1513,7 +1553,7 @@ dependencies = [ { name = "httpx2-jsfetch", marker = "python_full_version >= '3.12' and sys_platform == 'emscripten'" }, { name = "idna", version = "3.20", source = { registry = "https://pypi.org/simple" } }, { name = "truststore", marker = "sys_platform != 'emscripten'" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d5/44/474bef2a0e9d90f1715d32cb98b0738695ca17ba324095fb2497ed7fbd59/httpx2-2.13.1.tar.gz", hash = "sha256:e48744a19e3af5ee48313d0ce5fe941d5422fae5705ea922a4aabf94d7800dfa", size = 100405, upload-time = "2026-09-23T07:47:23.052Z" } wheels = [ @@ -1542,7 +1582,8 @@ dependencies = [ { name = "pyyaml" }, { name = "tqdm" }, { name = "typer" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/bb/b6/e22bd20a25299c34b8c5922c1545a6320825b13906eb0f7298edfd034a0b/huggingface_hub-1.15.0.tar.gz", hash = "sha256:28abfdddda3927fd4de6a63cf26ab012498a2c24dae52baf150c5c6edf98a1d5", size = 784100, upload-time = "2026-05-15T11:42:52.149Z" } wheels = [ @@ -1905,7 +1946,7 @@ dependencies = [ { name = "pydantic" }, { name = "pyyaml" }, { name = "tenacity" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } }, { name = "uuid-utils" }, ] sdist = { url = "https://files.pythonhosted.org/packages/59/de/679a53472c25860837e32c0442c962fa86e95317a36460e2c9d5c91b17c2/langchain_core-1.4.0.tar.gz", hash = "sha256:1dc341eed802ed9c117c0df3923c991e5e9e226571e5725c194eeb5bd93d1a7f", size = 920260, upload-time = "2026-05-11T18:42:35.919Z" } @@ -1932,7 +1973,7 @@ dependencies = [ { name = "pydantic" }, { name = "pyyaml" }, { name = "tenacity" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" } }, { name = "uuid-utils" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9e/18/20c3eec05ccf2fff8e553866bec3bb2f92880aea3cb878603e5a854bd5c0/langchain_core-1.5.4.tar.gz", hash = "sha256:aa76104f30b6c7305f292cb2c364e67cb52c321940ae812d7969471dce32a89a", size = 980540, upload-time = "2026-08-11T18:02:52.239Z" } @@ -1963,7 +2004,7 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/4f/24/9777489d6fbbee64af0c8f96d4f840239c408cf694f3394672807dafc490/langchain_protocol-0.0.15.tar.gz", hash = "sha256:9ab2d11ee73944754f10e037e717098d3a6796f0e58afa9cadda6154e7655ade", size = 5862, upload-time = "2026-05-01T22:30:04.748Z" } wheels = [ @@ -1982,7 +2023,7 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/d2/59/b5959aea96faa9146e2e49a7a22882b3528c62efafe9a6a95beab30c2305/langchain_protocol-0.0.18.tar.gz", hash = "sha256:ec3e11782f1ed0c9db38e5a9ed01b0e7a0d3fba406faa8aef6594b73c56a63e6", size = 6150, upload-time = "2026-06-18T17:08:26.959Z" } wheels = [ @@ -2145,7 +2186,7 @@ dependencies = [ { name = "requests" }, { name = "requests-toolbelt" }, { name = "sniffio" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" } }, { name = "uuid-utils" }, { name = "websockets" }, { name = "xxhash" }, @@ -2435,7 +2476,8 @@ dependencies = [ { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "sse-starlette" }, { name = "starlette" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "typing-inspection" }, { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] @@ -2498,12 +2540,101 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, ] +[[package]] +name = "msgspec" +version = "0.22.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/e6/6dcf9306ff3c5e486578f3bf29ed11dfbdbbc2a8bf0caf7e07d392887fda/msgspec-0.22.0.tar.gz", hash = "sha256:0a13624a4969159fe35d8c2a3d377b2b61bbd8585e327440d5e52725affcce38", size = 343188, upload-time = "2026-09-29T14:14:11.422Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/5e/78d4fa2073bb3a891753e7f915d51094e2ded5aa5e9b20402518929b373e/msgspec-0.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f3413e3647275f787b21b4dfb4836a59a1a5acf1018ab1d45843b1d7edf15c22", size = 200076, upload-time = "2026-09-29T14:12:07.599Z" }, + { url = "https://files.pythonhosted.org/packages/38/f8/59701da04584af4ccd55f42200da303ebf146cd6867186a8b9b1e127a4a2/msgspec-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:38c5b9bd347bc9abbcee40752be3c5117854e891ea7a1881a56d4b3dec58c5e7", size = 192337, upload-time = "2026-09-29T14:12:09.198Z" }, + { url = "https://files.pythonhosted.org/packages/eb/dd/bd4131da741aa349656fe32a5cca0c4266c58d7b5ad75485bed29565f7cd/msgspec-0.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57c282f474e17acf6bcf84f393c73afd45d6eba47cccff8b76b79c4fbb8a3b54", size = 222888, upload-time = "2026-09-29T14:12:10.691Z" }, + { url = "https://files.pythonhosted.org/packages/c6/46/01fe71c42b3342f00e2dd6c5a8837f5dc4d0e1596b4c74c054fb13075201/msgspec-0.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12a887c4c06e4a771a2db32c9a80c7bb21866b12458025f636dcdc2253331c28", size = 227838, upload-time = "2026-09-29T14:12:12.178Z" }, + { url = "https://files.pythonhosted.org/packages/62/8f/1a459825e0a5510de882af461459bd7f0525342b3c0bf1000e27be7aeef5/msgspec-0.22.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6c8a3f210421e29d8f7e9815f106cf59d758665b7fe5428e61152ce24fe65d7", size = 235818, upload-time = "2026-09-29T14:12:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/9d37b6f1190101b452f6c455e8715cc9960afad231e18cf9545af58710b9/msgspec-0.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ebd211d7af79ed8710c64e9e8d4c0d02749bc20170e7ab4e1c5801ca7c99d25b", size = 228019, upload-time = "2026-09-29T14:12:15.156Z" }, + { url = "https://files.pythonhosted.org/packages/c1/d5/33723137c96b8f244d8e6fc57a0a8d3b57b3599ce9b4a4dd58dc55a46d1c/msgspec-0.22.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:27d9ef46c80884f9c4f323e0b18bec464287e872121e70f2cbe47335780bf597", size = 236406, upload-time = "2026-09-29T14:12:16.908Z" }, + { url = "https://files.pythonhosted.org/packages/44/4a/f0e4a9ab970ce0a31f191acb772d3e1af67eeb73e1d73b70c079252aed02/msgspec-0.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ec108e96fdaa8fdbe5bb993ec97a9d1faa69b3a521eecd71a6e5acbe0e29ae69", size = 231028, upload-time = "2026-09-29T14:12:18.497Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e8/3de7345a8944a5bcfc9dd861d30fcea5f20f51057bcafacbbff9164e55fc/msgspec-0.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:21c887d4de397355f6635c2a037b1c067882dac5d132a1793d63bbf7cf5ca78e", size = 190753, upload-time = "2026-09-29T14:12:20.291Z" }, + { url = "https://files.pythonhosted.org/packages/66/c9/f0d3bd2dfc3753806ab70b8d00a1613019c39148a87da797771d7f72a0a9/msgspec-0.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a663a8d7f6ad56ac1dbcba91e046ba8ebab7773ae72ef3dd3c47f8226919184", size = 188939, upload-time = "2026-09-29T14:12:21.645Z" }, + { url = "https://files.pythonhosted.org/packages/9d/22/45c17acb1a85360b10afb95f66777f76bc2634993c66db8b7833832bd343/msgspec-0.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb1e129b81ac8fcf9ec649b081c6c8da1c7ea6f87cab336d46386abc2cd855c1", size = 198231, upload-time = "2026-09-29T14:12:23.016Z" }, + { url = "https://files.pythonhosted.org/packages/34/79/1cf725694125051e866066d74e6199206838d1465cbfc35081dc29b6e366/msgspec-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dce29a04966e31abf9b83b697c6d672486526dc5d03fcd6970cb56d5dc1fbeea", size = 190911, upload-time = "2026-09-29T14:12:24.636Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b2/e0ace038031a2988aa2e85c431c4d7aef734fbba4749ace6bc5bf310b769/msgspec-0.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b962000e11dd34fb210a5a2c57a8a62b2d92b381c8cb3b05c075a83e38f8d645", size = 220343, upload-time = "2026-09-29T14:12:26.111Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e6/16ddb09185d79dc00177994cf0bdb1cd8e5cc44a1d1bfba61bdda5f382cb/msgspec-0.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6db3806b3b76ca78064255eac6fa101a8a64fe6f698d80fbaf81fdfa21217d4", size = 225251, upload-time = "2026-09-29T14:12:27.559Z" }, + { url = "https://files.pythonhosted.org/packages/16/c2/a6af0d38fb0e72f02851ed084c4b8175140cfaf3eaf48b38da0c3941db26/msgspec-0.22.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a88d939d3fe4b8c7314645ebcd6e86c8c8a512ea7820d6550355973e803bc0f1", size = 233488, upload-time = "2026-09-29T14:12:28.996Z" }, + { url = "https://files.pythonhosted.org/packages/0b/9b/b1c4208cdf487e2ba7af145f721b279444ff76af05a9f8fce992ed0588ee/msgspec-0.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0b31746da07cba0e330c6433a94a4699ad77d3aeb9638d1a320a7686b69f6249", size = 225688, upload-time = "2026-09-29T14:12:30.351Z" }, + { url = "https://files.pythonhosted.org/packages/83/54/b9240d908674ef7c41d02cb909731ad6d9931c23bd6a27d8d10776c6f964/msgspec-0.22.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:6ae370f92f3517f0e6f209ba7cc649c957b444868439197e046be07154667551", size = 234250, upload-time = "2026-09-29T14:12:31.887Z" }, + { url = "https://files.pythonhosted.org/packages/df/c0/d498798aaab3bd191a33955de47b40f07fae7667d86a33b705443a7e9491/msgspec-0.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a696f23f7c1ffb31fae308502e01a3965c3891d5c400f01d0d1096dbe77519e", size = 228337, upload-time = "2026-09-29T14:12:33.365Z" }, + { url = "https://files.pythonhosted.org/packages/fa/51/5e9ae5a5ddc254e15435749328161e95598750e5df644bb00fa9e2297122/msgspec-0.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:024138c51afd335d0b4dce401be33902caafac2b64f8c9f2509a378986175d98", size = 190962, upload-time = "2026-09-29T14:12:34.847Z" }, + { url = "https://files.pythonhosted.org/packages/12/38/fb64a18543bcbebc53a375cb00b1c93bf264a0b6c7bbe9e38b37cc5f0768/msgspec-0.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:4600dbec738ed74e4c9bd35503e84701200ea7db344cfdeda80677b3ee53eb64", size = 189458, upload-time = "2026-09-29T14:12:36.277Z" }, + { url = "https://files.pythonhosted.org/packages/a4/87/3e017dca361d09ed1cd09dc981a6df21b32e830fbec3470f7486d38b6be5/msgspec-0.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab1e9e7531e353653b906cdd12a0220cc288a1e8e3436aabc65f4508d91b14d9", size = 201301, upload-time = "2026-09-29T14:12:38.048Z" }, + { url = "https://files.pythonhosted.org/packages/fb/02/109165edaafb895668d87177972a32ade9126a54f3736123d8e44be9096d/msgspec-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b60b43425a47eb9cfe987f6874e354ca7c760e58e295b4e2273ff03574df28a1", size = 193044, upload-time = "2026-09-29T14:12:39.46Z" }, + { url = "https://files.pythonhosted.org/packages/54/a5/65de05f8804492f76ea121b21a125cdf1d97ec461c677bfa0ba354d6fbdd/msgspec-0.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5a169b5b03f0f2c7a296c002647db1dab75d2cd501bca34e32b71cab0261b56", size = 224035, upload-time = "2026-09-29T14:12:40.876Z" }, + { url = "https://files.pythonhosted.org/packages/4a/cc/aa1a47f8c92280d37498a5ea56a2a36606d034383e3e6472d64cbb56cf85/msgspec-0.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99c401861c5bb3a57f7d6423ea7ed4352cd57aa3f04f4fbe9f3e3e4564a10f08", size = 230377, upload-time = "2026-09-29T14:12:42.796Z" }, + { url = "https://files.pythonhosted.org/packages/61/50/f8bcdb3d613a4a4b92704297a12eba5c985cf572a64ee1a004d265759c69/msgspec-0.22.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08826f5e5b0fa2f7a88592c396a243cfcc63d37e19f9d4fbe3b3f1be2fbdc404", size = 237390, upload-time = "2026-09-29T14:12:44.282Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8a/473fa423f8fdd1b810b8652594323d7301df6920b62844d860daa0feff34/msgspec-0.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21460f54cee9208239b1a8421fdf25bffc77293e1daba88f585711ad839b9758", size = 227733, upload-time = "2026-09-29T14:12:45.839Z" }, + { url = "https://files.pythonhosted.org/packages/03/1d/272ce23adae6c71b3f763aed3ee6e115cccc56124ed8ee0e3e3d2681e2c8/msgspec-0.22.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cfc3d9557de9c806318725b702f3e664db33167bb42892079b693c69893fd33b", size = 236783, upload-time = "2026-09-29T14:12:47.234Z" }, + { url = "https://files.pythonhosted.org/packages/f6/26/29e0b9a8605c8819a3c718158e345a616ac42c092dd7d7ab248c2f2b0a72/msgspec-0.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0b25dcbc108783cb72503ed705b9fbb8c3cb02ee5801923f44b5f038c91cc365", size = 232728, upload-time = "2026-09-29T14:12:48.792Z" }, + { url = "https://files.pythonhosted.org/packages/e1/a6/99597c281d716da6c662b48dcc3f734669f716b41d5df2af367dac9e7c21/msgspec-0.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:6ad64f5c260866b0d543f89f50cee43628989c1433c5de7ce820281fa28a2611", size = 192885, upload-time = "2026-09-29T14:12:50.274Z" }, + { url = "https://files.pythonhosted.org/packages/46/80/85fff923d448b886ec3a85900c578d9367f08dad54fe48879495b4c6d055/msgspec-0.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:0922714feff5300aacd8ecd65fa828317ce4bf5212b3139258c0bfc0253cd80e", size = 191223, upload-time = "2026-09-29T14:12:51.699Z" }, + { url = "https://files.pythonhosted.org/packages/7f/62/5374fba2ede0408f4bd8b9b3a6c8464f8d0ea7ae9a2a064bd81ca492bd1e/msgspec-0.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f13c127a945479bc9db057eb253b8851075c8e1ae07ffc967bfa1c5676203a86", size = 201355, upload-time = "2026-09-29T14:12:53.145Z" }, + { url = "https://files.pythonhosted.org/packages/cc/e3/357baa8d2a9164a98dfd7ef9d3a58125df0ed981be909945bdd337be7194/msgspec-0.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5aa24eb475d070ecbbe5b21080fc3ce4b0b76c60de25cfe0c9678d8fb44bb42f", size = 193097, upload-time = "2026-09-29T14:12:54.52Z" }, + { url = "https://files.pythonhosted.org/packages/fa/1b/9cc07718d1dee8ed5e89a265801d565bc0f15ead435ccb198f9c7bf92574/msgspec-0.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:627bfdfe5a4b3d916b3360b30f4cddeee3a084f56593e33527c6872fa8322ff9", size = 224112, upload-time = "2026-09-29T14:12:55.983Z" }, + { url = "https://files.pythonhosted.org/packages/46/64/f33fdfe95aca76601194a7064d14816c7c22c4eccc1b03a5335785895fa3/msgspec-0.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6c310ef83e7e291b01a63298828f848348bb99e84a1098c4b3923c05674d032", size = 230472, upload-time = "2026-09-29T14:12:57.648Z" }, + { url = "https://files.pythonhosted.org/packages/8e/b3/8ceaa9981c230adf43c45a6e8da25da23a381eddc7ed05aeaca1d5e7928b/msgspec-0.22.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c1e76c6bd523141b9c05c2f8a70979cd0efedbd68855a66f292f8892c0b8fc7", size = 237382, upload-time = "2026-09-29T14:12:59.414Z" }, + { url = "https://files.pythonhosted.org/packages/88/a6/7b5c4fb39e0bf2dabc8be923c33c39b07ba769a0ce6f0afbbdfaadb1f2f2/msgspec-0.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc374dedd5f85a5f4de2386dc5f737894ccb8c1ac18e9566ce66fd9839e6285d", size = 227717, upload-time = "2026-09-29T14:13:00.88Z" }, + { url = "https://files.pythonhosted.org/packages/b8/5b/2334ee638880e756c8bc54a1177bd65877c786433693a43594ef5ecbe2d8/msgspec-0.22.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:feafe612034d49e9144340c0b5168ee4e22c2af4aaa2c1db11ae84e1aac9543b", size = 236781, upload-time = "2026-09-29T14:13:02.468Z" }, + { url = "https://files.pythonhosted.org/packages/6c/e5/b4c5323b17ecfce45350695d40fc93e16856db957a53cbcf2f53007d6e12/msgspec-0.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f48317f05312bfdf78248f53933f830f07ab75cc1c813ac3ca4220cb3b5b019", size = 232777, upload-time = "2026-09-29T14:13:04.025Z" }, + { url = "https://files.pythonhosted.org/packages/01/33/e591f9d3d8d6c9cfc02ae95f3e3c44920f2d18050f3f252c244e0f293a0e/msgspec-0.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:0739b068f31f2004a364f97679ba91f2f5ecd6ec2a5b4b890188ab5c57d20672", size = 192829, upload-time = "2026-09-29T14:13:05.519Z" }, + { url = "https://files.pythonhosted.org/packages/d1/cd/a011a5b8732cd781e2ea6da5b38d71ae4a9a329338411d1f008a58f5edbf/msgspec-0.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:508278300dd4efbd21cd3a4b2b016160a5feac98bc880d3673f6c06697baaf62", size = 191258, upload-time = "2026-09-29T14:13:06.909Z" }, + { url = "https://files.pythonhosted.org/packages/53/f9/ac027b35477e6b83bcee32b3d9675b37abfa130f098dd6500fa67d768852/msgspec-0.22.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:221cbcbfa4478152b91d37dcfd4830e2be92773e8139e883f43773450ebacef8", size = 201276, upload-time = "2026-09-29T14:13:08.311Z" }, + { url = "https://files.pythonhosted.org/packages/13/6b/2bffffa31662b1353a62e672442865d51c291ad778352fd490de16361dc6/msgspec-0.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dd9568695911055440d2bb7099ed9098fc181d335daa772d0eb3fe8f31ba4efb", size = 193233, upload-time = "2026-09-29T14:13:09.943Z" }, + { url = "https://files.pythonhosted.org/packages/14/bc/4066416ff6aa918d1ef9295edee0041e4629e4079ad3839bdd8a68fd87f0/msgspec-0.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f039ef5207b847f075a0a43020ee6140cd47505f890e47e157f2deb485c2dc96", size = 225101, upload-time = "2026-09-29T14:13:11.391Z" }, + { url = "https://files.pythonhosted.org/packages/63/ba/a8d390d5bd4c7d9ccde87c95cf071ada934cc9ca2c6af4d3d50b38f2d718/msgspec-0.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4f7e09cceac7dbf4c0761b8ae7df51c55b5df5e9af7aff2c895aac1ebea015", size = 230505, upload-time = "2026-09-29T14:13:12.869Z" }, + { url = "https://files.pythonhosted.org/packages/9c/89/979664fdc913c624ef88a139b40e3a95ddf2a47c89e8b5c4147f69ee9c48/msgspec-0.22.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:614e2c827e0a3f934f3cf0cf4ba65210df8132b75a69a8a1f51bb3b2caf0ac5a", size = 237382, upload-time = "2026-09-29T14:13:14.317Z" }, + { url = "https://files.pythonhosted.org/packages/07/3f/7d44c614376ae008ac6099be5f589b322c4ad44e32c6dbb0edd256215028/msgspec-0.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3689b9dfcc663358ef23ba4299d7460f01108515b041a7d30d05908ac9c32f", size = 228962, upload-time = "2026-09-29T14:13:15.763Z" }, + { url = "https://files.pythonhosted.org/packages/0b/59/bf8504e6f63f6769d01fb66f8bd856cf0ed39a07fde354f440d711640054/msgspec-0.22.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d2f950239ff1fc7322c6f9634807310265149cb168270d3ddcdda5b6ada13a28", size = 236691, upload-time = "2026-09-29T14:13:17.195Z" }, + { url = "https://files.pythonhosted.org/packages/2b/40/5a9d2bde12af16a22ddbf371990a81d3e3c0dcd4bb4ef3b3f9616b033c14/msgspec-0.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3c789b5ccd07c0a3c09767108ee06e089b2875f2309a4569c2648f30a8d31dfa", size = 232750, upload-time = "2026-09-29T14:13:18.691Z" }, + { url = "https://files.pythonhosted.org/packages/75/5d/c0e6bdb81a87f6bd56a663a330c271af7670490c80d8d635d9fa21ad1adf/msgspec-0.22.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:a66b1766311e42371e509c996c3933b161c7ae0eabdf361af5316dec197e1022", size = 136814, upload-time = "2026-09-29T14:13:20.415Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c0/b0cfc6d33608e5ea8871f3be31f9146c56699e737a7d8862bf018484f278/msgspec-0.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:749899563d26b211379f142b8ffd7e2d7da149a51717798f0ce994dce50324f0", size = 197097, upload-time = "2026-09-29T14:13:21.869Z" }, + { url = "https://files.pythonhosted.org/packages/42/1f/571f7fe7c725380605d680fc4c0084212b23d2dfcf6be0f2277f14462c56/msgspec-0.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:10d0d1d464960d99a949f7ca01ef8928e51c472433a5f5ab74b2d695fb830652", size = 196779, upload-time = "2026-09-29T14:13:23.62Z" }, + { url = "https://files.pythonhosted.org/packages/ab/f3/3c87372bac651b37911e0dc6926c3958949d3fcb8cec1016adbc44d948b2/msgspec-0.22.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:e79725246291516a7359caad5fb743ddc0ec66ed40d2381fb846325b5031504e", size = 205214, upload-time = "2026-09-29T14:13:25.158Z" }, + { url = "https://files.pythonhosted.org/packages/43/4c/fbccd6e0fbbdf10c4d9b6bac8a26148dd5483b3ffff6d6c5a376ff1f5cb1/msgspec-0.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:38f7022fbe91954b31afe3888a0af1b652e0f370fafdeb1d425f4a814d789c9f", size = 196941, upload-time = "2026-09-29T14:13:26.637Z" }, + { url = "https://files.pythonhosted.org/packages/55/04/8db7186d3ae8818356bc623cc132db8b77da37ce4b1345f35719c8ad5726/msgspec-0.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b6d3ca19a8ff28d0a67a1824e2bff7ec649ec795c80a265f20ade4caa63080de", size = 229934, upload-time = "2026-09-29T14:13:28.285Z" }, + { url = "https://files.pythonhosted.org/packages/17/24/a249f3491cabbe77cc65a1a6f87c128582aa39357227149be61cac8e554f/msgspec-0.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8b98ae215a102cbf6635f7df45f5c4af12f77fad1f7b71b9808fcf868a5735d", size = 234378, upload-time = "2026-09-29T14:13:29.821Z" }, + { url = "https://files.pythonhosted.org/packages/87/ee/6dbcb1b5de8e9d47e8f0fde9a288628dc178c1749a570b98251218fa10c4/msgspec-0.22.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e0aa0cc3f18c35bab79bd7b87fde95d6274a9deddeebd1ea541f8066a5073165", size = 243118, upload-time = "2026-09-29T14:13:31.544Z" }, + { url = "https://files.pythonhosted.org/packages/79/03/7dd2d0ca988600e01fc00ad0cf20d1d44bc59369a913c988654c65f6582b/msgspec-0.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8c8e84789918fbc15a503b92a829115ddd7567ecd3e4778bd418c56abbb86c11", size = 234557, upload-time = "2026-09-29T14:13:33.068Z" }, + { url = "https://files.pythonhosted.org/packages/74/e2/43f3c63bff1650efcaaea31466246e28b46927323fc9ff416c68cc6e4047/msgspec-0.22.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:3ca7d4cd69fbb66bd2da6211d3e79d40542d196c16c6d99bf838f76767ad35be", size = 241288, upload-time = "2026-09-29T14:13:34.532Z" }, + { url = "https://files.pythonhosted.org/packages/8b/70/11b93815a59674f33182dc3e873d343ca0b37e25be52ecb28f52092f1fed/msgspec-0.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:28f53f3604dd3e70225f7563c831628dbb03299b428f8e62aadb4b628e386874", size = 236432, upload-time = "2026-09-29T14:13:36.083Z" }, + { url = "https://files.pythonhosted.org/packages/b7/82/7aad0f033f8dcb3f23868773c2ede803ae162a784828ccde75aa3f9b2f9d/msgspec-0.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:7293dee54de040cfa225c22151cc3d72f17cd674b5ebcb52f38fb9f5701592e6", size = 202062, upload-time = "2026-09-29T14:13:37.955Z" }, + { url = "https://files.pythonhosted.org/packages/e3/45/cf52577926d73e2369e25927e389cb4ea1461169c489f46d3248159b5be7/msgspec-0.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:c3c510aba9015c085e514b75a9b3f1ed7c4591ae5e379655821b8bba51f30cc7", size = 201686, upload-time = "2026-09-29T14:13:39.42Z" }, + { url = "https://files.pythonhosted.org/packages/c8/63/d93937e2aae34ff1ea33b62799d1963cacc1bf432d196d6130039657a122/msgspec-0.22.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:263e110955ed76fe0af2d79f819903b50a70dc0e7a752eb7aabe79d2e0a084fb", size = 202241, upload-time = "2026-09-29T14:13:40.919Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e2/46ece11a244cd56432eb2362ffbb8014f3f02963136d84d941f71fdc2a3f/msgspec-0.22.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:c6f06576eced70462179a4b4638e84cf69fdbba37f44d13a64a21739c131a830", size = 194232, upload-time = "2026-09-29T14:13:42.454Z" }, + { url = "https://files.pythonhosted.org/packages/cf/b1/1c385f2f93006cdc2af1511cc512c347cb22e2d4f11952c205230aedf586/msgspec-0.22.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8d67582478b0eaabb899f2fb255c878ee7de57dff80eb73ab24f1865524ec441", size = 226524, upload-time = "2026-09-29T14:13:43.876Z" }, + { url = "https://files.pythonhosted.org/packages/dc/fb/c80c8842d40347cacf89a60a4986b849dae1a6dfd25830441efdd6faa65b/msgspec-0.22.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:71cbbdb39631064e2f2f9e9ac2b1b69931d72276eb5f9da4ed025726296bdbb6", size = 231816, upload-time = "2026-09-29T14:13:45.329Z" }, + { url = "https://files.pythonhosted.org/packages/73/ac/90bbcfd890b4bda90c93f7e1b7fc24e84b270420486d9d43ae31443d15ab/msgspec-0.22.0-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f0a5c25516e2034b2db7767081759ff8996e214def9c43b3055f61e1be1caad", size = 244241, upload-time = "2026-09-29T14:13:46.851Z" }, + { url = "https://files.pythonhosted.org/packages/72/9a/eabdb5f1b5e6013b0e2f9f2a95790587f6864aa9ca37f9d7dece65b53878/msgspec-0.22.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:a1dab6a99c759d1391ab2993388c1892746a697254f4b5dc6c059ca6e3bfbc8b", size = 230198, upload-time = "2026-09-29T14:13:48.296Z" }, + { url = "https://files.pythonhosted.org/packages/e9/89/9f080532d4ac52f416dd7318e55c2053cc071853d17d58e24897a5b553bf/msgspec-0.22.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:a52eba5c9528fd181fcec39d22b67aaa1dccc6cfe8e24d3f5d41130e6d04289d", size = 242949, upload-time = "2026-09-29T14:13:49.829Z" }, + { url = "https://files.pythonhosted.org/packages/11/df/6baf9b2f3523ebe2b820820c7929fd72ec5f483a93147130338ecc353fac/msgspec-0.22.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:1e547966017265c0d23342bcf2e027305dde40ea042d16694a9b96b4f696a052", size = 233914, upload-time = "2026-09-29T14:13:51.5Z" }, + { url = "https://files.pythonhosted.org/packages/bb/37/9cf650779c8c1e53291ef184c838703930a4cabb1fb37e222c85a7d49fa9/msgspec-0.22.0-cp315-cp315-win_amd64.whl", hash = "sha256:0067057df265795f742658b15dbe53f3b6f21d19dcfa53676db11088cfa41e0a", size = 197910, upload-time = "2026-09-29T14:13:53.071Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ce/2f78c93d4f69e0167a19c2d40d4fbf7bbd6f074e1047536735832a4368ee/msgspec-0.22.0-cp315-cp315-win_arm64.whl", hash = "sha256:05dbc8268e50c9232ec72b9af1c7b13049aade4d1197764e38c427048706e046", size = 197590, upload-time = "2026-09-29T14:13:54.47Z" }, + { url = "https://files.pythonhosted.org/packages/3f/bf/282e9a443058b85b8f706c9a651e2d8cdd11cc09d16e8fa347b6c57b75bb/msgspec-0.22.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:b3113ebcceeb7693a915183c73d92c10bf5c62851dd187cab43bd025fb587419", size = 206298, upload-time = "2026-09-29T14:13:55.913Z" }, + { url = "https://files.pythonhosted.org/packages/ef/2d/2e694fa46f55319007f72013b17341ea3868be1c77e7a597176b202dda92/msgspec-0.22.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:0dfadea8bdcfafc614bd031de55a8ede22b43445cfff6d8b77cc0c07d3edc8a8", size = 198145, upload-time = "2026-09-29T14:13:57.412Z" }, + { url = "https://files.pythonhosted.org/packages/5b/2e/2fa279cb57cb47175ae604d572787f903d4ad3f0afa867201bbd99e6647e/msgspec-0.22.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7a738826936c72348c613061d260446f13c82b6fd7d5d7705b6911ab8dca2f3", size = 232362, upload-time = "2026-09-29T14:13:58.817Z" }, + { url = "https://files.pythonhosted.org/packages/a0/58/a7e759b11b28441c27f803b29d9b5f4b5ad85150c89354b5ede1baca9258/msgspec-0.22.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f2ddea9d78d09460f06c26a7a508adcd049761c3208776162b8eb79b8a032cff", size = 235885, upload-time = "2026-09-29T14:14:00.381Z" }, + { url = "https://files.pythonhosted.org/packages/86/56/8d7ee098e94cbd9f35fa643dc497e06a4a6307b9f562cfbe48103fc3b209/msgspec-0.22.0-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:884c28c80b0a511595b29a9b04a3a230c3797369e4a033e6d5c6d9b5427f8e09", size = 248155, upload-time = "2026-09-29T14:14:01.945Z" }, + { url = "https://files.pythonhosted.org/packages/b9/6d/1cabb4b8a5dbf696e2b24df9e482b2e0333bb3b1b13ebb5433813e6616ec/msgspec-0.22.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:f7a923bcde480065c8e25967464cfb2a687ee67000bb43157e2d57e40eca7305", size = 236416, upload-time = "2026-09-29T14:14:03.363Z" }, + { url = "https://files.pythonhosted.org/packages/ba/43/8bf0f558eb369f1f2d494b3d5ab9d0ae0907d07ecc0cdbe11b6768b02867/msgspec-0.22.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:65eea14bc65ccfeb8f3af62cb204841871e2961f002d7fa87dbe0f79dacf1c1c", size = 247292, upload-time = "2026-09-29T14:14:04.829Z" }, + { url = "https://files.pythonhosted.org/packages/81/33/2fbaadf98b5510cac4bb56d2b03937e0b1fb4bfcd1ae6aba20361f299583/msgspec-0.22.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:0666a1520cab86796612e794e71107e0fbf5e8ff3ddcdfcfff8f1d94b860d2f1", size = 238220, upload-time = "2026-09-29T14:14:06.408Z" }, + { url = "https://files.pythonhosted.org/packages/f1/cc/b6be6041098ab859a8472983ccc2c08339fc2ef53f28d4f5fe7f4f34276b/msgspec-0.22.0-cp315-cp315t-win_amd64.whl", hash = "sha256:885c6e0c89d6103648525fe62aa78d600054dedf7b3713d23b15d7ddb6d66a13", size = 202939, upload-time = "2026-09-29T14:14:08.079Z" }, + { url = "https://files.pythonhosted.org/packages/5a/c1/664578dd98be70cd4ab1a9dcf3a181b1376b83c65ec41ee162130b58c8c0/msgspec-0.22.0-cp315-cp315t-win_arm64.whl", hash = "sha256:268594d0bae5510572599a6ab0364dd9de43c867d24a30856cd9f5edb63d8dc6", size = 202117, upload-time = "2026-09-29T14:14:09.891Z" }, +] + [[package]] name = "multidict" version = "6.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } wheels = [ @@ -2645,7 +2776,8 @@ dependencies = [ { name = "mypy-extensions" }, { name = "pathspec" }, { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/04/af/e3d4b3e9ec91a0ff9aabfdb38692952acf49bbb899c2e4c29acb3a6da3ae/mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665", size = 3817349, upload-time = "2026-04-21T17:12:28.473Z" } wheels = [ @@ -2735,14 +2867,36 @@ wheels = [ name = "nexus-rpc" version = "1.4.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/35/d5/cd1ffb202b76ebc1b33c1332a3416e55a39929006982adc2b1eb069aaa9b/nexus_rpc-1.4.0.tar.gz", hash = "sha256:3b8b373d4865671789cc43623e3dc0bcbf192562e40e13727e17f1c149050fba", size = 82367, upload-time = "2026-02-25T22:01:34.053Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/11/52/6327a5f4fda01207205038a106a99848a41c83e933cd23ea2cab3d2ebc6c/nexus_rpc-1.4.0-py3-none-any.whl", hash = "sha256:14c953d3519113f8ccec533a9efdb6b10c28afef75d11cdd6d422640c40b3a49", size = 29645, upload-time = "2026-02-25T22:01:33.122Z" }, ] +[[package]] +name = "nexus-rpc" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/e5/25be948c92492cfc8ac24a524ad8828fe0cb9f71f46a974d7a37f0794a26/nexus_rpc-1.5.0.tar.gz", hash = "sha256:be2d6934414677bc9dd8e571fb5106c22b8769edc19562c3f49d9988edd24e1c", size = 97404, upload-time = "2026-10-28T17:12:21.82Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/e2/00541e7997f3dfb294f7be3dafea3307ea09b9d3fd332054fe4d30627efc/nexus_rpc-1.5.0-py3-none-any.whl", hash = "sha256:5d6e856972be812573948df02da2257344c407a8eaa99977b5d38be54448305a", size = 30406, upload-time = "2026-10-28T17:12:20.245Z" }, +] + [[package]] name = "nodeenv" version = "1.10.0" @@ -2796,7 +2950,8 @@ dependencies = [ { name = "pydantic" }, { name = "sniffio" }, { name = "tqdm" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/9a/8c75e8c8a5b407a0586faeb2afac91674ff955c191ecc1d6d3b6669f6788/openai-2.54.0.tar.gz", hash = "sha256:e3e6f8bc1ba30ddf381ace1a14340eed381cb984a1a59bd0f34b5be3b5d49cfa", size = 1100285, upload-time = "2026-08-11T18:46:59.035Z" } wheels = [ @@ -2813,7 +2968,8 @@ dependencies = [ { name = "openai" }, { name = "pydantic" }, { name = "requests" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "websockets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/57/18/1204bde976436ad85498b759d5297843ebd2e0a214f729eb2dc4423cdfc7/openai_agents-0.20.0.tar.gz", hash = "sha256:a6b37954877868ac233876a27dab1e2a461c53ecbd5fcf6e19e2d276d3e848cd", size = 6146452, upload-time = "2026-08-11T03:12:49.556Z" } @@ -2885,7 +3041,8 @@ dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f2/90/c81c7426cb9dca075cef1b5fe16f58c68604b7518f7aaa14d837ec80fde3/openinference_instrumentation_openai-0.1.52.tar.gz", hash = "sha256:5a3dceb742209463e33ab2a4aa82f56bedfa20c25c738de28248509931044ea1", size = 23087, upload-time = "2026-06-11T17:13:35.517Z" } @@ -2907,7 +3064,8 @@ name = "opentelemetry-api" version = "1.42.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b4/1c/125e1c936c0873796771b7f04f6c93b9f1bf5d424cea90fda94a99f61da8/opentelemetry_api-1.42.1.tar.gz", hash = "sha256:56c63bea9f77b62856be8c47600474acad853b2924b99b1687c4cb6297166716", size = 72296, upload-time = "2026-05-21T16:32:49.335Z" } wheels = [ @@ -2937,7 +3095,8 @@ dependencies = [ { name = "opentelemetry-exporter-otlp-proto-common" }, { name = "opentelemetry-proto" }, { name = "opentelemetry-sdk" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/87/87/ca7fc790dfdbcf4f9e9aab14a39ef1b7508ead13707e283de0b3131478d2/opentelemetry_exporter_otlp_proto_grpc-1.42.1.tar.gz", hash = "sha256:975c4461f167dd8ed8857d68d3b6b25f3d272eab896f6a9470d0f5b90e2faf15", size = 27140, upload-time = "2026-05-21T16:32:56.162Z" } wheels = [ @@ -2955,7 +3114,8 @@ dependencies = [ { name = "opentelemetry-proto" }, { name = "opentelemetry-sdk" }, { name = "requests" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/77/32/826bfa1d80ecea24f47808de03cd4a0d13c17ecc07712f45123f0f61e4ac/opentelemetry_exporter_otlp_proto_http-1.42.1.tar.gz", hash = "sha256:bf142a21035d7571ac3a09cb2e5639f49886f243972883cfe777ed3bf02b734d", size = 25406, upload-time = "2026-05-21T16:32:56.807Z" } wheels = [ @@ -3039,7 +3199,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/40/f7/b390bd9bfd703bf98a68fea1f27786c6872331fd617164a54b8a59bdc008/opentelemetry_sdk-1.42.1.tar.gz", hash = "sha256:8c834e8f8c9ba4171d4ec843d0cb8a67e4c7394d3f9e9297e582cbd9456ddbf7", size = 239262, upload-time = "2026-05-21T16:33:04.641Z" } wheels = [ @@ -3052,7 +3213,8 @@ version = "0.63b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/93/99/4d7dd6df64795951413ce6e815f8cf1eb191daf7196ae86574589643d5f3/opentelemetry_semantic_conventions-0.63b1.tar.gz", hash = "sha256:3daf963611334b365e98a57438183eb012d3bfb40b2d931a9af613476b8701a9", size = 148340, upload-time = "2026-05-21T16:33:05.455Z" } wheels = [ @@ -3706,7 +3868,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "typing-inspection" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } @@ -3719,7 +3882,8 @@ name = "pydantic-core" version = "2.46.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } wheels = [ @@ -3858,7 +4022,7 @@ name = "pyjwt" version = "2.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } wheels = [ @@ -3885,7 +4049,8 @@ version = "1.1.409" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/51/4e/3aa27f74211522dba7e9cbc3e74de779c6d4b654c54e50a4840623be8014/pyright-1.1.409.tar.gz", hash = "sha256:986ee05beca9e077c165758ad123667c679e050059a2546aa02473930394bc93", size = 4430434, upload-time = "2026-04-23T11:02:03.799Z" } wheels = [ @@ -4079,7 +4244,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } wheels = [ @@ -4540,7 +4706,8 @@ version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" } wheels = [ @@ -4563,7 +4730,8 @@ dependencies = [ { name = "opentelemetry-sdk" }, { name = "pydantic" }, { name = "pyyaml" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "watchdog" }, ] sdist = { url = "https://files.pythonhosted.org/packages/19/15/b87850332d13d77ababa1d50f7a2bb8797d422d834cc93e623b6be9459c1/strands_agents-1.58.1.tar.gz", hash = "sha256:02174515242fd31d847e6f4a5b019adbeec15e706070329068cc31a2e04cc921", size = 1738858, upload-time = "2026-10-06T21:02:36.326Z" } @@ -4590,7 +4758,8 @@ dependencies = [ { name = "strands-agents" }, { name = "sympy" }, { name = "tenacity" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "tzdata", marker = "sys_platform == 'win32'" }, { name = "watchdog" }, ] @@ -4615,12 +4784,15 @@ wheels = [ name = "temporalio" version = "1.35.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] dependencies = [ - { name = "nexus-rpc" }, + { name = "nexus-rpc", version = "1.4.0", source = { registry = "https://pypi.org/simple" } }, { name = "protobuf" }, - { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "python-dateutil" }, { name = "types-protobuf" }, - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/69/38/03641a02eb18e2b16f58c1c136deafdffeb30828dcd83ebd8ac41eb39fd7/temporalio-1.35.0.tar.gz", hash = "sha256:c7a14f29909f8d847f78de49305af89c0ec5aea39272cd4050124386ac3ca295", size = 2879693, upload-time = "2026-10-28T20:13:38.726Z" } wheels = [ @@ -4642,13 +4814,61 @@ pydantic = [ { name = "pydantic" }, ] +[[package]] +name = "temporalio" +version = "1.36.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "nexus-rpc", version = "1.5.0", source = { registry = "https://pypi.org/simple" } }, + { name = "protobuf" }, + { name = "types-protobuf" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/82/e1cdd6b33af79e38d2e3db350821d62b6daf188bc905013852dd480929bd/temporalio-1.36.0.tar.gz", hash = "sha256:e65a9d30a0e3647cb92f2b19254447b5297e26f0f5edd323b27ee406367543e4", size = 2923753, upload-time = "2026-11-11T01:08:39.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/82/3696a76e35452f1960764027b41bff6c52ad5f70cd9d968dd49dc0efdd5c/temporalio-1.36.0-cp311-abi3-macosx_10_12_x86_64.whl", hash = "sha256:042f00e7ad1935c017ca4e964af1c88a1d45ac21f73e455b3f81b2c55a0bcab5", size = 14091242, upload-time = "2026-11-11T01:08:18.938Z" }, + { url = "https://files.pythonhosted.org/packages/a0/79/908bc9218f248f45581f0d4dce338ba5ae2b9fd1b5d20f2dd00b67e54366/temporalio-1.36.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:f3218255207aa37313adc0ae46332089411cb6e240d82a415ba39f8da4de64bd", size = 13648068, upload-time = "2026-11-11T01:08:22.372Z" }, + { url = "https://files.pythonhosted.org/packages/66/b3/4d6dd7d57901e7038263775c84312f523a31bbeb3a5d45958f782f18e36a/temporalio-1.36.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:318f6b3f2315cf1c06c7896ab6c312791b6f9a426bab0a80c42a57af156acc20", size = 14050456, upload-time = "2026-11-11T01:08:25.454Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b6/0572b6211bcceda6b657c13e726ad2ce2959a5228d002d1bd327610de5af/temporalio-1.36.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed457244ed5185a3e4a2ecccd40bff1d11583380b9b80ecf314589463540f3f8", size = 14524738, upload-time = "2026-11-11T01:08:28.079Z" }, + { url = "https://files.pythonhosted.org/packages/1e/9a/ffb5f15773b87cb461f5eeb02f5b6ff493e77e08067e9e0f7da4f57e22da/temporalio-1.36.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d45faa8cb1cc047990e4d61c755e34a843d557e858667da9b175c550373607ab", size = 14132850, upload-time = "2026-11-11T01:08:30.474Z" }, + { url = "https://files.pythonhosted.org/packages/46/e2/d3fde3487dc3430e3c3291e90e52788f47183fc80c2c1dc08e0aa4eade86/temporalio-1.36.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8cf5dfe59a54d2ff1e8a9e3397457661bf560dcd3b9b9ddc07f7c6757f8a3dac", size = 14627513, upload-time = "2026-11-11T01:08:33.428Z" }, + { url = "https://files.pythonhosted.org/packages/76/d8/95ec26dcb35ef2dd35eae52e9e5223f148b21c05344d46ca29d746ebefe9/temporalio-1.36.0-cp311-abi3-win_amd64.whl", hash = "sha256:74940ad05bebf84e1c53a9f10a69addd10b819985286cd5e05c8b48340ded815", size = 15472448, upload-time = "2026-11-11T01:08:36.551Z" }, +] + +[package.optional-dependencies] +opentelemetry = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-sdk" }, +] +pydantic = [ + { name = "pydantic" }, +] + +[[package]] +name = "temporalio-agent-framework" +version = "0.0.0" +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python#b5ed079cb18e313199ef6437de491e536144623f" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "pydantic" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" } }, +] + [[package]] name = "temporalio-claude-agent-sdk" version = "0.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "claude-agent-sdk" }, - { name = "temporalio" }, + { name = "temporalio", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a2/42/fa66877170d08857365f49e10a6ec2e1b8a035a7ff338097b503b35053d3/temporalio_claude_agent_sdk-0.0.2.tar.gz", hash = "sha256:412aa8ca9ff06db3979ea830dab1199e99537a57a81be7223d8ff22478609009", size = 19731, upload-time = "2026-10-30T21:54:58.605Z" } wheels = [ @@ -4666,7 +4886,7 @@ dependencies = [ { name = "langgraph", version = "1.2.11", source = { registry = "https://pypi.org/simple" } }, { name = "langsmith", version = "0.12.6", source = { registry = "https://pypi.org/simple" } }, { name = "pydantic" }, - { name = "temporalio" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/a3/0d/dca4dc7ade5695e97488a6e4d0d59ed489a0d1b8a9e8d4c4a99b9883ebe2/temporalio_deepagents-0.0.1.tar.gz", hash = "sha256:ce7793b9ec98e96ad52768ca99a18f9e291b57024df7c06c3965b53e1739d79f", size = 39336, upload-time = "2026-10-07T17:17:20.72Z" } wheels = [ @@ -4683,7 +4903,8 @@ dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-sdk" }, { name = "pydantic" }, - { name = "temporalio" }, + { name = "temporalio", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e6/76/560481d0fdf61f3644a7eeac329915e72d13cd842d4e734b46172a74ee6c/temporalio_google_adk-0.0.1.tar.gz", hash = "sha256:faad4a0a210e0788859b3548f5f14794de65a498b6bcb56b7457ea6e40286e9f", size = 28843, upload-time = "2026-10-07T18:48:47.47Z" } wheels = [ @@ -4702,7 +4923,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-genai" }, { name = "pydantic" }, - { name = "temporalio" }, + { name = "temporalio", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3d/2a/eeaf7cab52aa6500e7b325378fd20347dba4a905293959ffb91b0468b35f/temporalio_google_genai-0.1.0.tar.gz", hash = "sha256:ce90309de31f8080930a7de1fec16ac9542419325b420e487c88aa38f9ab1f53", size = 31914, upload-time = "2026-10-07T20:56:53.814Z" } wheels = [ @@ -4723,8 +4945,10 @@ dependencies = [ { name = "langchain-core", version = "1.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "langgraph", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "langgraph", version = "1.2.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "temporalio" }, - { name = "typing-extensions" }, + { name = "temporalio", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a0/03/21d4cbeda7843c276d01aea5fe6d17bd82877ac4111f29b39975e568cb6e/temporalio_langgraph-0.1.0.tar.gz", hash = "sha256:35d67361016052b7a87e3878096ca9cd77a7390979dbb60e5b5bb32b53098e0d", size = 17438, upload-time = "2026-10-07T21:36:42.771Z" } wheels = [ @@ -4735,19 +4959,41 @@ wheels = [ name = "temporalio-langsmith" version = "0.1.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] dependencies = [ - { name = "langchain-core", version = "1.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "langchain-core", version = "1.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "langsmith", version = "0.8.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "langsmith", version = "0.12.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "nexus-rpc" }, - { name = "temporalio" }, + { name = "langchain-core", version = "1.4.0", source = { registry = "https://pypi.org/simple" } }, + { name = "langsmith", version = "0.8.9", source = { registry = "https://pypi.org/simple" } }, + { name = "nexus-rpc", version = "1.4.0", source = { registry = "https://pypi.org/simple" } }, + { name = "temporalio", version = "1.35.0", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/db/3b/29e67f44787fcfb7ee8b45c3f40116c5a8189499b9bfe7b43514f1d2d54d/temporalio_langsmith-0.1.0.tar.gz", hash = "sha256:3de432700871d95a364c4da507074430f388b678047dfee47543d93025eb2ff1", size = 213609, upload-time = "2026-10-07T22:34:17.718Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/04/41/f88a3c4874e51f688bee85540706b25ff082beff4fb40b21f1b3ba5c513e/temporalio_langsmith-0.1.0-py3-none-any.whl", hash = "sha256:5dd7078d78294b4cd2812a8e41ccaf938aec412aa586145d8aa9b5e9f0fa42d3", size = 213573, upload-time = "2026-10-07T22:34:16.191Z" }, ] +[[package]] +name = "temporalio-langsmith" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "langchain-core", version = "1.5.4", source = { registry = "https://pypi.org/simple" } }, + { name = "langsmith", version = "0.12.6", source = { registry = "https://pypi.org/simple" } }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/17/16/f2f679300c83f141acced23ac09b55160dbdd2f3fbd1cf561197671da3b1/temporalio_langsmith-0.1.1.tar.gz", hash = "sha256:7cafe4e210152e0ef40a6b832436084fcf55c9956b98b354f6135f1857322488", size = 214499, upload-time = "2026-10-28T18:55:44.815Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/e6/95dc7eba97dfa3c821fb2931cf367632d1b303e7370d65ec2e541d434937/temporalio_langsmith-0.1.1-py3-none-any.whl", hash = "sha256:74a71f4ceb59eb285007dd09cdb83f88d670ee8384fe3ad6e873f2afcb7bc938", size = 214523, upload-time = "2026-10-28T18:55:43.142Z" }, +] + [[package]] name = "temporalio-openai-agents" version = "1.0.0" @@ -4757,8 +5003,10 @@ dependencies = [ { name = "openai" }, { name = "openai-agents" }, { name = "pydantic" }, - { name = "temporalio", extra = ["opentelemetry", "pydantic"] }, - { name = "typing-extensions" }, + { name = "temporalio", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, extra = ["opentelemetry", "pydantic"], marker = "python_full_version < '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, extra = ["opentelemetry", "pydantic"], marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0f/5c/394834735b6987ab398bc37f22483d0e3239c509ba39e31eb56f3267584f/temporalio_openai_agents-1.0.0.tar.gz", hash = "sha256:ae49add8d91f40762b3cd7f4bbc13b93fc667b738b063212c59378d03733e623", size = 72601, upload-time = "2026-09-16T20:54:40.938Z" } wheels = [ @@ -4771,16 +5019,24 @@ version = "0.1a1" source = { editable = "." } dependencies = [ { name = "protobuf" }, - { name = "temporalio" }, + { name = "temporalio", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] [package.dev-dependencies] +agent-framework = [ + { name = "agent-framework-openai", marker = "python_full_version >= '3.11'" }, + { name = "opentelemetry-exporter-otlp-proto-grpc", marker = "python_full_version >= '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, extra = ["opentelemetry"], marker = "python_full_version >= '3.11'" }, + { name = "temporalio-agent-framework", marker = "python_full_version >= '3.11'" }, +] bedrock = [ { name = "boto3" }, ] claude-agent-sdk = [ { name = "opentelemetry-exporter-otlp-proto-grpc" }, - { name = "temporalio", extra = ["opentelemetry", "pydantic"] }, + { name = "temporalio", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, extra = ["opentelemetry", "pydantic"], marker = "python_full_version < '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, extra = ["opentelemetry", "pydantic"], marker = "python_full_version >= '3.11'" }, { name = "temporalio-claude-agent-sdk" }, ] cloud-export-to-parquet = [ @@ -4792,7 +5048,7 @@ cloud-export-to-parquet = [ deepagents = [ { name = "langchain-anthropic", version = "1.5.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "temporalio-deepagents", marker = "python_full_version >= '3.11'" }, - { name = "temporalio-langsmith", marker = "python_full_version >= '3.11'" }, + { name = "temporalio-langsmith", version = "0.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] dev = [ { name = "fakeredis" }, @@ -4831,7 +5087,8 @@ google-adk = [ { name = "google-adk" }, { name = "opentelemetry-exporter-prometheus" }, { name = "prometheus-client" }, - { name = "temporalio", extra = ["opentelemetry"] }, + { name = "temporalio", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, extra = ["opentelemetry"], marker = "python_full_version < '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, extra = ["opentelemetry"], marker = "python_full_version >= '3.11'" }, { name = "temporalio-google-adk", extra = ["mcp"] }, ] google-genai = [ @@ -4842,7 +5099,8 @@ langfuse-tracing = [ { name = "openinference-instrumentation-openai" }, { name = "opentelemetry-exporter-otlp-proto-http" }, { name = "opentelemetry-instrumentation-openai-v2" }, - { name = "temporalio", extra = ["opentelemetry"] }, + { name = "temporalio", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, extra = ["opentelemetry"], marker = "python_full_version < '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, extra = ["opentelemetry"], marker = "python_full_version >= '3.11'" }, ] langgraph = [ { name = "langchain", version = "1.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -4852,24 +5110,29 @@ langgraph = [ { name = "langgraph", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "langgraph", version = "1.2.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "temporalio-langgraph" }, - { name = "temporalio-langsmith" }, + { name = "temporalio-langsmith", version = "0.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "temporalio-langsmith", version = "0.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] langsmith-tracing = [ { name = "langsmith", version = "0.8.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "langsmith", version = "0.12.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "openai" }, - { name = "temporalio", extra = ["pydantic"] }, - { name = "temporalio-langsmith" }, + { name = "temporalio", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, extra = ["pydantic"], marker = "python_full_version < '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, extra = ["pydantic"], marker = "python_full_version >= '3.11'" }, + { name = "temporalio-langsmith", version = "0.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "temporalio-langsmith", version = "0.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] litellm = [ { name = "litellm" }, ] nexus = [ - { name = "nexus-rpc" }, + { name = "nexus-rpc", version = "1.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "nexus-rpc", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] open-telemetry = [ { name = "opentelemetry-exporter-otlp-proto-grpc" }, - { name = "temporalio", extra = ["opentelemetry"] }, + { name = "temporalio", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, extra = ["opentelemetry"], marker = "python_full_version < '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, extra = ["opentelemetry"], marker = "python_full_version >= '3.11'" }, ] openai-agents = [ { name = "openai-agents", extra = ["litellm"] }, @@ -4904,6 +5167,12 @@ requires-dist = [ ] [package.metadata.requires-dev] +agent-framework = [ + { name = "agent-framework-openai", marker = "python_full_version >= '3.11'", specifier = ">=1.17,<2" }, + { name = "opentelemetry-exporter-otlp-proto-grpc", marker = "python_full_version >= '3.11'", specifier = ">=1.30.0,<2" }, + { name = "temporalio", extras = ["opentelemetry"], marker = "python_full_version >= '3.11'", specifier = ">=1.36.0,<2" }, + { name = "temporalio-agent-framework", marker = "python_full_version >= '3.11'", git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python" }, +] bedrock = [{ name = "boto3", specifier = ">=1.34.92,<2" }] claude-agent-sdk = [ { name = "opentelemetry-exporter-otlp-proto-grpc", specifier = ">=1.30.0,<2" }, @@ -5012,7 +5281,8 @@ dependencies = [ { name = "botocore" }, { name = "mcp" }, { name = "strands-agents" }, - { name = "temporalio" }, + { name = "temporalio", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/35/ab/2b2a30df8ef357546e14a151ab114869da541405d99da513a74b9c4fc89f/temporalio_strands_agents-0.1.0.tar.gz", hash = "sha256:1da8ac046004bb83f12cd3c87df278f4c71ee11c2abecfb791928e22c22c3f75", size = 36060, upload-time = "2026-10-07T22:52:50.223Z" } wheels = [ @@ -5246,7 +5516,8 @@ name = "types-aiobotocore-s3" version = "3.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/94/ce/5b7baa06cde79ab5f083f56f060ccd2b60f6249127d70bbb8a37aafbdcd4/types_aiobotocore_s3-3.7.0.tar.gz", hash = "sha256:6ec738853dbba9133707991b98ea4dab19f7c62e02b3cca016e6cd8e3d684576", size = 77662, upload-time = "2026-05-10T03:17:35.088Z" } wheels = [ @@ -5275,17 +5546,37 @@ wheels = [ name = "typing-extensions" version = "4.15.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + [[package]] name = "typing-inspection" version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ @@ -5430,7 +5721,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f6/b1/8e7077a8641086aea449e1b5752a570f1b5906c64e0a33cd6d93b63a066b/uvicorn-0.47.0.tar.gz", hash = "sha256:7c9a0ea1a9414106bbab7324609c162d8fa0cdcdcb703060987269d77c7bb533", size = 90582, upload-time = "2026-05-14T18:16:54.455Z" } wheels = [ From d62d1ef53ae833bf07c9e8be8be5a13d2b43d39c Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 22:51:29 -0600 Subject: [PATCH 02/26] Keep Client.connect sites type-checking on temporalio 1.36 --- deepagents_plugin/external_storage/client.py | 2 +- langfuse_tracing/ticket_triage/starter.py | 2 +- langfuse_tracing/ticket_triage/worker.py | 2 +- langsmith_tracing/basic/starter.py | 2 +- langsmith_tracing/chatbot/starter.py | 2 +- openai_agents/basic/run_remote_image_workflow.py | 2 +- openai_agents/handoffs/run_message_filter_workflow.py | 2 +- openai_agents/mcp/run_file_system_worker.py | 2 +- openai_agents/mcp/run_file_system_workflow.py | 2 +- openai_agents/mcp/run_memory_research_scratchpad_worker.py | 2 +- openai_agents/mcp/run_memory_research_scratchpad_workflow.py | 2 +- openai_agents/mcp/run_prompt_server_worker.py | 2 +- openai_agents/mcp/run_prompt_server_workflow.py | 2 +- openai_agents/mcp/run_sse_worker.py | 2 +- openai_agents/mcp/run_sse_workflow.py | 2 +- openai_agents/mcp/run_streamable_http_worker.py | 2 +- openai_agents/mcp/run_streamable_http_workflow.py | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/deepagents_plugin/external_storage/client.py b/deepagents_plugin/external_storage/client.py index 39340c2a..dabad827 100644 --- a/deepagents_plugin/external_storage/client.py +++ b/deepagents_plugin/external_storage/client.py @@ -62,4 +62,4 @@ async def connect_client( ] ), ) - yield await Client.connect(**config, plugins=[plugin]) + yield await Client.connect(**config, plugins=[plugin]) # type: ignore[misc] diff --git a/langfuse_tracing/ticket_triage/starter.py b/langfuse_tracing/ticket_triage/starter.py index e77e8e8f..97d38fca 100644 --- a/langfuse_tracing/ticket_triage/starter.py +++ b/langfuse_tracing/ticket_triage/starter.py @@ -43,7 +43,7 @@ async def main() -> None: config = ClientConfig.load_client_connect_config() config.setdefault("target_host", "localhost:7233") - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[OpenTelemetryPlugin(add_temporal_spans=True)], ) diff --git a/langfuse_tracing/ticket_triage/worker.py b/langfuse_tracing/ticket_triage/worker.py index db33e1e8..aded0492 100644 --- a/langfuse_tracing/ticket_triage/worker.py +++ b/langfuse_tracing/ticket_triage/worker.py @@ -44,7 +44,7 @@ async def main() -> None: # add_temporal_spans=True emits spans for Temporal operations (StartWorkflow, # RunWorkflow, RunActivity, HandleUpdate, ...) in addition to propagating # trace context across the client/workflow/activity boundaries. - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[OpenTelemetryPlugin(add_temporal_spans=True)], ) diff --git a/langsmith_tracing/basic/starter.py b/langsmith_tracing/basic/starter.py index f818d3fa..e6b22754 100644 --- a/langsmith_tracing/basic/starter.py +++ b/langsmith_tracing/basic/starter.py @@ -33,7 +33,7 @@ async def main() -> str: add_temporal_runs=add_temporal_runs, ) - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[plugin], ) diff --git a/langsmith_tracing/chatbot/starter.py b/langsmith_tracing/chatbot/starter.py index 1d5b1e3d..5e8cb07c 100644 --- a/langsmith_tracing/chatbot/starter.py +++ b/langsmith_tracing/chatbot/starter.py @@ -27,7 +27,7 @@ async def main(): add_temporal_runs=add_temporal_runs, ) - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, data_converter=pydantic_data_converter, plugins=[plugin], diff --git a/openai_agents/basic/run_remote_image_workflow.py b/openai_agents/basic/run_remote_image_workflow.py index 61bee517..d0f3ceaa 100644 --- a/openai_agents/basic/run_remote_image_workflow.py +++ b/openai_agents/basic/run_remote_image_workflow.py @@ -11,7 +11,7 @@ async def main(): config = ClientConfig.load_client_connect_config() config.setdefault("target_host", "localhost:7233") - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[OpenAIAgentsPlugin()], ) diff --git a/openai_agents/handoffs/run_message_filter_workflow.py b/openai_agents/handoffs/run_message_filter_workflow.py index 828be168..64b5e4e3 100644 --- a/openai_agents/handoffs/run_message_filter_workflow.py +++ b/openai_agents/handoffs/run_message_filter_workflow.py @@ -15,7 +15,7 @@ async def main(): config.setdefault("target_host", "localhost:7233") # Create client connected to server at the given address - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[ OpenAIAgentsPlugin(), diff --git a/openai_agents/mcp/run_file_system_worker.py b/openai_agents/mcp/run_file_system_worker.py index d1b1009d..8e72e0b0 100644 --- a/openai_agents/mcp/run_file_system_worker.py +++ b/openai_agents/mcp/run_file_system_worker.py @@ -38,7 +38,7 @@ async def main(): # Create client connected to server at the given address config = ClientConfig.load_client_connect_config() config.setdefault("target_host", "localhost:7233") - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[ OpenAIAgentsPlugin( diff --git a/openai_agents/mcp/run_file_system_workflow.py b/openai_agents/mcp/run_file_system_workflow.py index 2dcce3fb..d16ba309 100644 --- a/openai_agents/mcp/run_file_system_workflow.py +++ b/openai_agents/mcp/run_file_system_workflow.py @@ -11,7 +11,7 @@ async def main(): # Create client connected to server at the given address config = ClientConfig.load_client_connect_config() config.setdefault("target_host", "localhost:7233") - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[ OpenAIAgentsPlugin(), diff --git a/openai_agents/mcp/run_memory_research_scratchpad_worker.py b/openai_agents/mcp/run_memory_research_scratchpad_worker.py index f7c23fe7..e8fe7211 100644 --- a/openai_agents/mcp/run_memory_research_scratchpad_worker.py +++ b/openai_agents/mcp/run_memory_research_scratchpad_worker.py @@ -37,7 +37,7 @@ async def main(): # Create client connected to server at the given address config = ClientConfig.load_client_connect_config() config.setdefault("target_host", "localhost:7233") - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[ OpenAIAgentsPlugin( diff --git a/openai_agents/mcp/run_memory_research_scratchpad_workflow.py b/openai_agents/mcp/run_memory_research_scratchpad_workflow.py index 82b407aa..a033b452 100644 --- a/openai_agents/mcp/run_memory_research_scratchpad_workflow.py +++ b/openai_agents/mcp/run_memory_research_scratchpad_workflow.py @@ -14,7 +14,7 @@ async def main(): config = ClientConfig.load_client_connect_config() config.setdefault("target_host", "localhost:7233") - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[ OpenAIAgentsPlugin(), diff --git a/openai_agents/mcp/run_prompt_server_worker.py b/openai_agents/mcp/run_prompt_server_worker.py index ef8f03e5..af4ab1f8 100644 --- a/openai_agents/mcp/run_prompt_server_worker.py +++ b/openai_agents/mcp/run_prompt_server_worker.py @@ -36,7 +36,7 @@ async def main(): # Create client connected to server at the given address config = ClientConfig.load_client_connect_config() config.setdefault("target_host", "localhost:7233") - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[ OpenAIAgentsPlugin( diff --git a/openai_agents/mcp/run_prompt_server_workflow.py b/openai_agents/mcp/run_prompt_server_workflow.py index 2e455634..41af6d56 100644 --- a/openai_agents/mcp/run_prompt_server_workflow.py +++ b/openai_agents/mcp/run_prompt_server_workflow.py @@ -13,7 +13,7 @@ async def main(): # Create client connected to server at the given address config = ClientConfig.load_client_connect_config() config.setdefault("target_host", "localhost:7233") - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[OpenAIAgentsPlugin()], ) diff --git a/openai_agents/mcp/run_sse_worker.py b/openai_agents/mcp/run_sse_worker.py index bf735687..f93eeb49 100644 --- a/openai_agents/mcp/run_sse_worker.py +++ b/openai_agents/mcp/run_sse_worker.py @@ -36,7 +36,7 @@ async def main(): # Create client connected to server at the given address config = ClientConfig.load_client_connect_config() config.setdefault("target_host", "localhost:7233") - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[ OpenAIAgentsPlugin( diff --git a/openai_agents/mcp/run_sse_workflow.py b/openai_agents/mcp/run_sse_workflow.py index df8b8b8f..6827e72c 100644 --- a/openai_agents/mcp/run_sse_workflow.py +++ b/openai_agents/mcp/run_sse_workflow.py @@ -13,7 +13,7 @@ async def main(): # Create client connected to server at the given address config = ClientConfig.load_client_connect_config() config.setdefault("target_host", "localhost:7233") - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[OpenAIAgentsPlugin()], ) diff --git a/openai_agents/mcp/run_streamable_http_worker.py b/openai_agents/mcp/run_streamable_http_worker.py index f6c453a2..eed1738a 100644 --- a/openai_agents/mcp/run_streamable_http_worker.py +++ b/openai_agents/mcp/run_streamable_http_worker.py @@ -36,7 +36,7 @@ async def main(): # Create client connected to server at the given address config = ClientConfig.load_client_connect_config() config.setdefault("target_host", "localhost:7233") - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[ OpenAIAgentsPlugin( diff --git a/openai_agents/mcp/run_streamable_http_workflow.py b/openai_agents/mcp/run_streamable_http_workflow.py index 5ed312a0..f5ea06d7 100644 --- a/openai_agents/mcp/run_streamable_http_workflow.py +++ b/openai_agents/mcp/run_streamable_http_workflow.py @@ -13,7 +13,7 @@ async def main(): # Create client connected to server at the given address config = ClientConfig.load_client_connect_config() config.setdefault("target_host", "localhost:7233") - client = await Client.connect( + client = await Client.connect( # type: ignore[misc] **config, plugins=[OpenAIAgentsPlugin()], ) From 6da7d77cef9b768d79e913a574386db74c1b97de Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 22:51:29 -0600 Subject: [PATCH 03/26] Add Microsoft Agent Framework samples --- agent_framework_plugin/__init__.py | 0 .../continue_as_new/__init__.py | 0 .../continue_as_new/run_worker.py | 43 + .../continue_as_new/run_workflow.py | 39 + .../continue_as_new/workflow.py | 85 + .../hello_world/__init__.py | 0 .../hello_world/run_worker.py | 45 + .../hello_world/run_workflow.py | 27 + .../hello_world/workflow.py | 25 + .../human_in_the_loop/__init__.py | 0 .../human_in_the_loop/run_worker.py | 43 + .../human_in_the_loop/run_workflow.py | 48 + .../human_in_the_loop/workflow.py | 97 + agent_framework_plugin/mcp/__init__.py | 0 agent_framework_plugin/mcp/echo_mcp_server.py | 17 + agent_framework_plugin/mcp/pyproject.toml | 50 + agent_framework_plugin/mcp/run_worker.py | 62 + agent_framework_plugin/mcp/run_workflow.py | 25 + agent_framework_plugin/mcp/uv.lock | 1791 +++++++++++++++++ agent_framework_plugin/mcp/workflow.py | 36 + agent_framework_plugin/middleware/__init__.py | 0 .../middleware/run_worker.py | 43 + .../middleware/run_workflow.py | 25 + agent_framework_plugin/middleware/workflow.py | 88 + .../multi_agent/__init__.py | 0 .../multi_agent/run_worker.py | 44 + .../multi_agent/run_workflow.py | 25 + .../multi_agent/workflow.py | 51 + .../open_telemetry/__init__.py | 0 .../open_telemetry/run_worker.py | 48 + .../open_telemetry/run_workflow.py | 40 + .../open_telemetry/telemetry.py | 26 + .../open_telemetry/workflow.py | 27 + agent_framework_plugin/streaming/__init__.py | 0 .../streaming/run_worker.py | 43 + .../streaming/run_workflow.py | 67 + agent_framework_plugin/streaming/workflow.py | 41 + .../structured_output/__init__.py | 0 .../structured_output/run_worker.py | 43 + .../structured_output/run_workflow.py | 33 + .../structured_output/workflow.py | 39 + agent_framework_plugin/tools/__init__.py | 0 agent_framework_plugin/tools/run_worker.py | 45 + agent_framework_plugin/tools/run_workflow.py | 29 + agent_framework_plugin/tools/workflow.py | 59 + 45 files changed, 3249 insertions(+) create mode 100644 agent_framework_plugin/__init__.py create mode 100644 agent_framework_plugin/continue_as_new/__init__.py create mode 100644 agent_framework_plugin/continue_as_new/run_worker.py create mode 100644 agent_framework_plugin/continue_as_new/run_workflow.py create mode 100644 agent_framework_plugin/continue_as_new/workflow.py create mode 100644 agent_framework_plugin/hello_world/__init__.py create mode 100644 agent_framework_plugin/hello_world/run_worker.py create mode 100644 agent_framework_plugin/hello_world/run_workflow.py create mode 100644 agent_framework_plugin/hello_world/workflow.py create mode 100644 agent_framework_plugin/human_in_the_loop/__init__.py create mode 100644 agent_framework_plugin/human_in_the_loop/run_worker.py create mode 100644 agent_framework_plugin/human_in_the_loop/run_workflow.py create mode 100644 agent_framework_plugin/human_in_the_loop/workflow.py create mode 100644 agent_framework_plugin/mcp/__init__.py create mode 100644 agent_framework_plugin/mcp/echo_mcp_server.py create mode 100644 agent_framework_plugin/mcp/pyproject.toml create mode 100644 agent_framework_plugin/mcp/run_worker.py create mode 100644 agent_framework_plugin/mcp/run_workflow.py create mode 100644 agent_framework_plugin/mcp/uv.lock create mode 100644 agent_framework_plugin/mcp/workflow.py create mode 100644 agent_framework_plugin/middleware/__init__.py create mode 100644 agent_framework_plugin/middleware/run_worker.py create mode 100644 agent_framework_plugin/middleware/run_workflow.py create mode 100644 agent_framework_plugin/middleware/workflow.py create mode 100644 agent_framework_plugin/multi_agent/__init__.py create mode 100644 agent_framework_plugin/multi_agent/run_worker.py create mode 100644 agent_framework_plugin/multi_agent/run_workflow.py create mode 100644 agent_framework_plugin/multi_agent/workflow.py create mode 100644 agent_framework_plugin/open_telemetry/__init__.py create mode 100644 agent_framework_plugin/open_telemetry/run_worker.py create mode 100644 agent_framework_plugin/open_telemetry/run_workflow.py create mode 100644 agent_framework_plugin/open_telemetry/telemetry.py create mode 100644 agent_framework_plugin/open_telemetry/workflow.py create mode 100644 agent_framework_plugin/streaming/__init__.py create mode 100644 agent_framework_plugin/streaming/run_worker.py create mode 100644 agent_framework_plugin/streaming/run_workflow.py create mode 100644 agent_framework_plugin/streaming/workflow.py create mode 100644 agent_framework_plugin/structured_output/__init__.py create mode 100644 agent_framework_plugin/structured_output/run_worker.py create mode 100644 agent_framework_plugin/structured_output/run_workflow.py create mode 100644 agent_framework_plugin/structured_output/workflow.py create mode 100644 agent_framework_plugin/tools/__init__.py create mode 100644 agent_framework_plugin/tools/run_worker.py create mode 100644 agent_framework_plugin/tools/run_workflow.py create mode 100644 agent_framework_plugin/tools/workflow.py diff --git a/agent_framework_plugin/__init__.py b/agent_framework_plugin/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_framework_plugin/continue_as_new/__init__.py b/agent_framework_plugin/continue_as_new/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_framework_plugin/continue_as_new/run_worker.py b/agent_framework_plugin/continue_as_new/run_worker.py new file mode 100644 index 00000000..f9e98708 --- /dev/null +++ b/agent_framework_plugin/continue_as_new/run_worker.py @@ -0,0 +1,43 @@ +"""Worker for the chat continue-as-new sample.""" + +# @@@SNIPSTART python-agent-framework-continue-as-new-worker +import asyncio +import os + +from agent_framework.openai import OpenAIChatClient +from openai import AsyncOpenAI +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.client import Client +from temporalio.worker import Worker + +from agent_framework_plugin.continue_as_new.workflow import ChatWorkflow + + +def openai_chat_client() -> OpenAIChatClient: + return OpenAIChatClient( + model="gpt-4o-mini", async_client=AsyncOpenAI(max_retries=0) + ) + + +async def main() -> None: + if not os.environ.get("OPENAI_API_KEY"): + raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") + + plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + client = await Client.connect( + os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), + plugins=[plugin], + ) + + worker = Worker( + client, + task_queue="agent-framework-chat", + workflows=[ChatWorkflow], + ) + print("Worker started. Ctrl+C to exit.") + await worker.run() + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/continue_as_new/run_workflow.py b/agent_framework_plugin/continue_as_new/run_workflow.py new file mode 100644 index 00000000..7952c024 --- /dev/null +++ b/agent_framework_plugin/continue_as_new/run_workflow.py @@ -0,0 +1,39 @@ +"""Start the chat workflow, send two turns, then end it.""" + +# @@@SNIPSTART python-agent-framework-continue-as-new-run-workflow +import asyncio +import os + +from temporalio.client import Client + +from agent_framework_plugin.continue_as_new.workflow import ChatInput, ChatWorkflow + + +async def main() -> None: + client = await Client.connect(os.environ.get("TEMPORAL_ADDRESS", "localhost:7233")) + + handle = await client.start_workflow( + ChatWorkflow.run, + ChatInput(), + id="agent-framework-chat", + task_queue="agent-framework-chat", + ) + + for prompt in [ + "Hi! What is durable execution?", + "Give me a one-sentence summary.", + ]: + reply = await handle.execute_update(ChatWorkflow.turn, prompt) + print(f"user: {prompt}") + print(f"assistant: {reply}\n") + + transcript = await handle.query(ChatWorkflow.transcript) + print(f"{len(transcript)} messages in the session") + + await handle.signal(ChatWorkflow.end_chat) + await handle.result() + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/continue_as_new/workflow.py b/agent_framework_plugin/continue_as_new/workflow.py new file mode 100644 index 00000000..2a636027 --- /dev/null +++ b/agent_framework_plugin/continue_as_new/workflow.py @@ -0,0 +1,85 @@ +"""A chat that continues as new before its history grows too large. + +Each user turn is an Update, so the caller gets the reply from the same call. +When Temporal suggests Continue-As-New, the Workflow lets in-flight Updates +finish and hands the serialized session to a fresh run. ``session.to_dict()`` +carries the transcript and Agent Framework's own session state across the +boundary; the ``Agent`` object does not cross and is built again in the new run. +""" + +# @@@SNIPSTART python-agent-framework-continue-as-new-workflow +import asyncio +from dataclasses import dataclass +from datetime import timedelta +from typing import Any + +from agent_framework import Agent, AgentSession +from temporalio import workflow +from temporalio.agent_framework import TemporalChatClient + + +@dataclass +class ChatInput: + session: dict[str, Any] | None = None + + +@workflow.defn +class ChatWorkflow: + def __init__(self) -> None: + self._done = False + self._lock = asyncio.Lock() + self._agent: Agent | None = None + self._session: AgentSession | None = None + + @workflow.update + async def turn(self, prompt: str) -> str: + await workflow.wait_condition(lambda: self._agent is not None) + assert self._agent is not None and self._session is not None + # One turn at a time, so the session's history stays in order. + async with self._lock: + response = await self._agent.run(prompt, session=self._session) + return response.text + + @workflow.signal + def end_chat(self) -> None: + self._done = True + + @workflow.query + def transcript(self) -> list[dict[str, str]]: + if self._session is None: + return [] + # Agent Framework's in-memory history provider keeps the messages here. + messages = self._session.state.get("in_memory", {}).get("messages", []) + return [ + { + "role": str(getattr(message.role, "value", message.role)), + "text": message.text, + } + for message in messages + ] + + @workflow.run + async def run(self, input: ChatInput) -> None: + self._agent = Agent( + client=TemporalChatClient( + "gpt-4o-mini", start_to_close_timeout=timedelta(seconds=60) + ), + instructions="You are a helpful assistant. Keep replies short.", + ) + self._session = ( + AgentSession.from_dict(input.session) + if input.session + else self._agent.create_session() + ) + + await workflow.wait_condition( + lambda: self._done or workflow.info().is_continue_as_new_suggested() + ) + # Let in-flight turns finish before the handoff. + await workflow.wait_condition(workflow.all_handlers_finished) + + if not self._done: + workflow.continue_as_new(ChatInput(session=self._session.to_dict())) + + +# @@@SNIPEND diff --git a/agent_framework_plugin/hello_world/__init__.py b/agent_framework_plugin/hello_world/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_framework_plugin/hello_world/run_worker.py b/agent_framework_plugin/hello_world/run_worker.py new file mode 100644 index 00000000..7f42c5f1 --- /dev/null +++ b/agent_framework_plugin/hello_world/run_worker.py @@ -0,0 +1,45 @@ +"""Worker for the hello world sample.""" + +# @@@SNIPSTART python-agent-framework-hello-world-worker +import asyncio +import os + +from agent_framework.openai import OpenAIChatClient +from openai import AsyncOpenAI +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.client import Client +from temporalio.worker import Worker + +from agent_framework_plugin.hello_world.workflow import HelloWorldWorkflow + + +def openai_chat_client() -> OpenAIChatClient: + # Runs on the Worker on first use; OPENAI_API_KEY never reaches the Workflow. + # Temporal retries the model Activity, so the OpenAI SDK's own retries are off. + return OpenAIChatClient( + model="gpt-4o-mini", async_client=AsyncOpenAI(max_retries=0) + ) + + +async def main() -> None: + if not os.environ.get("OPENAI_API_KEY"): + raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") + + plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + client = await Client.connect( + os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), + plugins=[plugin], + ) + + worker = Worker( + client, + task_queue="agent-framework-hello-world", + workflows=[HelloWorldWorkflow], + ) + print("Worker started. Ctrl+C to exit.") + await worker.run() + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/hello_world/run_workflow.py b/agent_framework_plugin/hello_world/run_workflow.py new file mode 100644 index 00000000..1b83ea0b --- /dev/null +++ b/agent_framework_plugin/hello_world/run_workflow.py @@ -0,0 +1,27 @@ +"""Start the hello world workflow.""" + +# @@@SNIPSTART python-agent-framework-hello-world-run-workflow +import asyncio +import os + +from temporalio.client import Client + +from agent_framework_plugin.hello_world.workflow import HelloWorldWorkflow + + +async def main() -> None: + client = await Client.connect(os.environ.get("TEMPORAL_ADDRESS", "localhost:7233")) + + result = await client.execute_workflow( + HelloWorldWorkflow.run, + "Write a haiku about durable execution.", + id="agent-framework-hello-world", + task_queue="agent-framework-hello-world", + ) + + print(f"Result: {result}") + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/hello_world/workflow.py b/agent_framework_plugin/hello_world/workflow.py new file mode 100644 index 00000000..beedd967 --- /dev/null +++ b/agent_framework_plugin/hello_world/workflow.py @@ -0,0 +1,25 @@ +"""One Agent Framework agent, one prompt, one model Activity.""" + +# @@@SNIPSTART python-agent-framework-hello-world-workflow +from datetime import timedelta + +from agent_framework import Agent +from temporalio import workflow +from temporalio.agent_framework import TemporalChatClient + + +@workflow.defn +class HelloWorldWorkflow: + @workflow.run + async def run(self, prompt: str) -> str: + agent = Agent( + client=TemporalChatClient( + "gpt-4o-mini", start_to_close_timeout=timedelta(seconds=60) + ), + instructions="You are a concise assistant.", + ) + response = await agent.run(prompt) + return response.text + + +# @@@SNIPEND diff --git a/agent_framework_plugin/human_in_the_loop/__init__.py b/agent_framework_plugin/human_in_the_loop/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_framework_plugin/human_in_the_loop/run_worker.py b/agent_framework_plugin/human_in_the_loop/run_worker.py new file mode 100644 index 00000000..1b0f6897 --- /dev/null +++ b/agent_framework_plugin/human_in_the_loop/run_worker.py @@ -0,0 +1,43 @@ +"""Worker for the human in the loop sample.""" + +# @@@SNIPSTART python-agent-framework-human-in-the-loop-worker +import asyncio +import os + +from agent_framework.openai import OpenAIChatClient +from openai import AsyncOpenAI +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.client import Client +from temporalio.worker import Worker + +from agent_framework_plugin.human_in_the_loop.workflow import HumanInTheLoopWorkflow + + +def openai_chat_client() -> OpenAIChatClient: + return OpenAIChatClient( + model="gpt-4o-mini", async_client=AsyncOpenAI(max_retries=0) + ) + + +async def main() -> None: + if not os.environ.get("OPENAI_API_KEY"): + raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") + + plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + client = await Client.connect( + os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), + plugins=[plugin], + ) + + worker = Worker( + client, + task_queue="agent-framework-human-in-the-loop", + workflows=[HumanInTheLoopWorkflow], + ) + print("Worker started. Ctrl+C to exit.") + await worker.run() + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/human_in_the_loop/run_workflow.py b/agent_framework_plugin/human_in_the_loop/run_workflow.py new file mode 100644 index 00000000..339a3510 --- /dev/null +++ b/agent_framework_plugin/human_in_the_loop/run_workflow.py @@ -0,0 +1,48 @@ +"""Start the human in the loop workflow, wait for the request, approve it.""" + +# @@@SNIPSTART python-agent-framework-human-in-the-loop-run-workflow +import asyncio +import os + +from temporalio.client import Client + +from agent_framework_plugin.human_in_the_loop.workflow import ( + ApprovalRequest, + HumanInTheLoopWorkflow, +) + + +async def main() -> None: + client = await Client.connect(os.environ.get("TEMPORAL_ADDRESS", "localhost:7233")) + + handle = await client.start_workflow( + HumanInTheLoopWorkflow.run, + "Please delete /tmp/report.txt", + id="agent-framework-human-in-the-loop", + task_queue="agent-framework-human-in-the-loop", + ) + + # Poll the Query until the agent asks for approval. + pending: list[ApprovalRequest] = [] + for _ in range(120): + pending = await handle.query(HumanInTheLoopWorkflow.pending_approvals) + if pending: + break + await asyncio.sleep(0.5) + else: + raise RuntimeError("the agent never asked for approval") + + for request in pending: + print(f"Approval requested: {request.tool_name}({request.arguments})") + + # Send "reject" to see the agent handle a rejection. + outcome = await handle.execute_update(HumanInTheLoopWorkflow.decide, "approve") + print(f"Decision: {outcome}") + + result = await handle.result() + print(f"Result: {result}") + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/human_in_the_loop/workflow.py b/agent_framework_plugin/human_in_the_loop/workflow.py new file mode 100644 index 00000000..14513df0 --- /dev/null +++ b/agent_framework_plugin/human_in_the_loop/workflow.py @@ -0,0 +1,97 @@ +"""Approval before a tool runs, with the wait as a Workflow Update. + +``delete_file`` carries ``approval_mode="always_require"``, Agent Framework's +own approval setting. The run returns with the request in +``response.user_input_requests`` instead of calling the tool. The Workflow +publishes the request through a Query, waits for a ``decide`` Update, and sends +the decision back on the same session. Only an approved call runs the tool. +""" + +# @@@SNIPSTART python-agent-framework-human-in-the-loop-workflow +from dataclasses import dataclass, field +from datetime import timedelta +from typing import Any + +from agent_framework import Agent, Content, tool +from temporalio import workflow +from temporalio.agent_framework import TemporalChatClient + + +@tool(approval_mode="always_require") +def delete_file(path: str) -> str: + """Delete a file. Runs only after a person approves.""" + return f"deleted {path}" + + +@dataclass +class ApprovalRequest: + request_id: str + tool_name: str + arguments: dict[str, Any] = field(default_factory=dict) + + +def approval_request(request: Content) -> ApprovalRequest: + call = request.function_call + return ApprovalRequest( + request_id=request.id or "", + tool_name=(call.name if call is not None else None) or "", + arguments=(call.parse_arguments() if call is not None else None) or {}, + ) + + +@workflow.defn +class HumanInTheLoopWorkflow: + def __init__(self) -> None: + self._pending: list[ApprovalRequest] = [] + self._decision: str | None = None + + @workflow.query + def pending_approvals(self) -> list[ApprovalRequest]: + return list(self._pending) + + @workflow.update + async def decide(self, decision: str) -> str: + # Applies to every pending request. The validator below runs before the + # Workflow sees the model's reply, so it checks the input, not the state. + self._decision = decision + return decision + + @decide.validator + def validate_decide(self, decision: str) -> None: + # Anything else never enters the Workflow's history. + if decision not in ("approve", "reject"): + raise ValueError( + f"decision must be 'approve' or 'reject', not {decision!r}" + ) + + @workflow.run + async def run(self, prompt: str) -> str: + agent = Agent( + client=TemporalChatClient( + "gpt-4o-mini", start_to_close_timeout=timedelta(seconds=60) + ), + instructions="You manage files. Use delete_file when asked to delete one.", + tools=[delete_file], + ) + # Approvals are keyed by the function call's id and resolved against + # the session, so both runs use the same session. + session = agent.create_session() + response = await agent.run(prompt, session=session) + while response.user_input_requests: + requests = response.user_input_requests + self._pending = [approval_request(request) for request in requests] + await workflow.wait_condition(lambda: self._decision is not None) + approved = self._decision == "approve" + self._decision = None + self._pending = [] + response = await agent.run( + [ + request.to_function_approval_response(approved) + for request in requests + ], + session=session, + ) + return response.text + + +# @@@SNIPEND diff --git a/agent_framework_plugin/mcp/__init__.py b/agent_framework_plugin/mcp/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_framework_plugin/mcp/echo_mcp_server.py b/agent_framework_plugin/mcp/echo_mcp_server.py new file mode 100644 index 00000000..afdcbbe6 --- /dev/null +++ b/agent_framework_plugin/mcp/echo_mcp_server.py @@ -0,0 +1,17 @@ +"""A one-tool MCP server over stdio. The Worker starts it as a subprocess.""" + +# @@@SNIPSTART python-agent-framework-mcp-server +from mcp.server.mcpserver import MCPServer + +server = MCPServer("echo") + + +@server.tool() +def echo(message: str) -> str: + """Return the message unchanged.""" + return message + + +if __name__ == "__main__": + server.run() +# @@@SNIPEND diff --git a/agent_framework_plugin/mcp/pyproject.toml b/agent_framework_plugin/mcp/pyproject.toml new file mode 100644 index 00000000..ace72b4b --- /dev/null +++ b/agent_framework_plugin/mcp/pyproject.toml @@ -0,0 +1,50 @@ +[project] +name = "temporalio-samples-agent-framework-mcp" +version = "0.1.0" +description = "Temporal.io Python SDK sample: Microsoft Agent Framework with an MCP server" +requires-python = ">=3.10" +# Agent Framework needs Python 3.11. On 3.10 these resolve to nothing and the +# tests are not collected, so the shared standalone CI matrix stays green. +dependencies = [ + "temporalio-agent-framework[mcp] ; python_version >= '3.11'", + "agent-framework-openai>=1.17,<2 ; python_version >= '3.11'", +] + +[dependency-groups] +dev = [ + "ruff>=0.5.0,<0.6", + "mypy>=1.4.1,<2", + "poethepoet>=0.36.0", + "pytest>=9.0.3,<10", + "pytest-asyncio>=1.3,<2", +] + +[tool.uv.sources] +# Temporary while the plugin is unreleased: remove once temporalio-agent-framework is on PyPI. +temporalio-agent-framework = { git = "https://github.com/temporalio/ai-integrations", subdirectory = "python/agent_framework", branch = "dabh/agent-framework-python" } + +[tool.poe.tasks] +format = [ + { cmd = "uv run ruff check --select I --fix" }, + { cmd = "uv run ruff format" }, +] +lint = [ + { cmd = "uv run ruff check --select I" }, + { cmd = "uv run ruff format --check" }, + { ref = "lint-types" }, +] +lint-types = "uv run --all-groups mypy --check-untyped-defs --namespace-packages --explicit-package-bases ." +test = "uv run pytest" + +[tool.ruff] +target-version = "py310" + +[tool.mypy] +ignore_missing_imports = true + +[tool.pytest.ini_options] +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "session" +asyncio_default_test_loop_scope = "session" +pythonpath = ["."] +testpaths = ["tests"] diff --git a/agent_framework_plugin/mcp/run_worker.py b/agent_framework_plugin/mcp/run_worker.py new file mode 100644 index 00000000..78fa9bd0 --- /dev/null +++ b/agent_framework_plugin/mcp/run_worker.py @@ -0,0 +1,62 @@ +"""Worker for the MCP sample. It starts ``echo_mcp_server.py`` on demand.""" + +# @@@SNIPSTART python-agent-framework-mcp-worker +import asyncio +import os +import sys +from pathlib import Path + +from agent_framework.openai import OpenAIChatClient +from mcp import Client as MCPClient +from mcp import StdioServerParameters, stdio_client +from openai import AsyncOpenAI +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.client import Client +from temporalio.worker import Worker + +from workflow import MCPWorkflow + +ECHO_SERVER = Path(__file__).parent / "echo_mcp_server.py" + + +def openai_chat_client() -> OpenAIChatClient: + return OpenAIChatClient( + model="gpt-4o-mini", async_client=AsyncOpenAI(max_retries=0) + ) + + +def echo_mcp_client() -> MCPClient: + # Runs on the Worker. The plugin connects on the first MCP Activity and + # keeps the connection for 5 minutes of idleness. + return MCPClient( + stdio_client( + StdioServerParameters(command=sys.executable, args=[str(ECHO_SERVER)]) + ) + ) + + +async def main() -> None: + if not os.environ.get("OPENAI_API_KEY"): + raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") + + plugin = AgentFrameworkPlugin( + chat_clients={"gpt-4o-mini": openai_chat_client}, + mcp_clients={"echo": echo_mcp_client}, + ) + client = await Client.connect( + os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), + plugins=[plugin], + ) + + worker = Worker( + client, + task_queue="agent-framework-mcp", + workflows=[MCPWorkflow], + ) + print("Worker started. Ctrl+C to exit.") + await worker.run() + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/mcp/run_workflow.py b/agent_framework_plugin/mcp/run_workflow.py new file mode 100644 index 00000000..5bde08f7 --- /dev/null +++ b/agent_framework_plugin/mcp/run_workflow.py @@ -0,0 +1,25 @@ +"""Start the MCP workflow.""" + +import asyncio +import os + +from temporalio.client import Client + +from workflow import MCPWorkflow + + +async def main() -> None: + client = await Client.connect(os.environ.get("TEMPORAL_ADDRESS", "localhost:7233")) + + result = await client.execute_workflow( + MCPWorkflow.run, + "Use the echo tool to echo the message 'hello from MCP'.", + id="agent-framework-mcp", + task_queue="agent-framework-mcp", + ) + + print(f"Result: {result}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/agent_framework_plugin/mcp/uv.lock b/agent_framework_plugin/mcp/uv.lock new file mode 100644 index 00000000..c3d1edeb --- /dev/null +++ b/agent_framework_plugin/mcp/uv.lock @@ -0,0 +1,1791 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "(python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten') or (python_full_version == '3.11.*' and sys_platform == 'emscripten')", + "python_full_version < '3.11'", +] + +[[package]] +name = "agent-framework-core" +version = "1.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "msgspec" }, + { name = "opentelemetry-api" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/b0/632e1bd56c8d6202b146d81767ef3dc5dff0e25a05442aa0c571ac973612/agent_framework_core-1.26.0.tar.gz", hash = "sha256:7ecd792184c8e706220ea3994fba71aa422a3e8be396a510ec2c4dd014289bca", size = 827413, upload-time = "2026-11-12T18:25:08.694Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/2a/5cc1d3b5274489355e57e273df3c5e330613a0b3e5498bd2bf6265500ed5/agent_framework_core-1.26.0-py3-none-any.whl", hash = "sha256:175b75f07e15d1bb7b6cea9cb35b965ac2528cd96e3eae8f791c85c48c7232b3", size = 889044, upload-time = "2026-11-12T18:25:48.022Z" }, +] + +[[package]] +name = "agent-framework-openai" +version = "1.18.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "openai" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/d9/d09025590ea8138d7dbe364133c4de4981878d4e5254a144005ac4d18924/agent_framework_openai-1.18.2.tar.gz", hash = "sha256:e88cd384af6cdc12a855425fd92d6131519550cc6157afc24796250af8ebb33e", size = 79711, upload-time = "2026-11-12T18:25:28.587Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/e6/cb4bff3b07a50cec20585767c2e1034a1561b103dd290c5ac64f9ba8e87e/agent_framework_openai-1.18.2-py3-none-any.whl", hash = "sha256:f0522f10ced39f9956cdb55956582f1abcbfdda528cf7d9a02c46f2f73e09460", size = 84617, upload-time = "2026-11-12T18:26:08.062Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" }, +] + +[[package]] +name = "anyio" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.15'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/98/7e457a3b014976887a050561955c0b76e1e7e2d6e27b78ecef2ef80518f2/anyio-4.16.0.tar.gz", hash = "sha256:59bb03251907983d1603d27c3087ddb1e93299ea2163adcdba37899a4fb12ffb", size = 310971, upload-time = "2026-11-01T11:20:07.282Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/16/7757fb84c819accc7d7e9af5291b261e4abb8627ee040f99e7d35ef2457a/anyio-4.16.0-py3-none-any.whl", hash = "sha256:c66ae634e0b8228396b801741beb3294326d4b6102f7bba4041c0dfb58c29026", size = 134521, upload-time = "2026-11-01T11:20:05.59Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "backports-asyncio-runner" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, +] + +[[package]] +name = "cffi" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/d2/2cde336b375f55c76ca670f0be3978cc048e31e24f3b4d7ce8473150a388/cffi-2.1.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be", size = 183779, upload-time = "2026-08-03T21:19:15.602Z" }, + { url = "https://files.pythonhosted.org/packages/94/1a/4b2f7c92293ba05cbd4a9a1b28faaf0326272d9488e6354657571c48a7aa/cffi-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b", size = 184178, upload-time = "2026-08-03T21:19:16.67Z" }, + { url = "https://files.pythonhosted.org/packages/17/0b/ba385d8ccedf926c3cd06e8e2f327027da5afe5f0eb30f1f7bc43ac55125/cffi-2.1.1-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004", size = 211037, upload-time = "2026-08-03T21:19:17.705Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b9/0f2e58b2cefa33255bff36935d42b13180fe559bba82596540eb404bde7d/cffi-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9", size = 218652, upload-time = "2026-08-03T21:19:18.735Z" }, + { url = "https://files.pythonhosted.org/packages/37/15/180e0dab27b9312c7479003d14c9e547634b7dcb934e2cc4650e1b131a7a/cffi-2.1.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98", size = 205422, upload-time = "2026-08-03T21:19:19.96Z" }, + { url = "https://files.pythonhosted.org/packages/18/d4/03026f0c850cbbaa9030750490225b4a7f4d524ea4df72c3cc740a90f4ef/cffi-2.1.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9", size = 205444, upload-time = "2026-08-03T21:19:21.246Z" }, + { url = "https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6", size = 218742, upload-time = "2026-08-03T21:19:22.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ae/679bf47e73fd77b352171727f07de559a003f14de5d02b904a6ec1fa73ca/cffi-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf", size = 221054, upload-time = "2026-08-03T21:19:23.694Z" }, + { url = "https://files.pythonhosted.org/packages/09/b8/eefc0e06913b70aa153bf74c946094a18f58fd4aff11b7f372bfdfdca050/cffi-2.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659", size = 213489, upload-time = "2026-08-03T21:19:24.922Z" }, + { url = "https://files.pythonhosted.org/packages/6f/13/4e56852824a03cdf68523a35686f1c28eacd4bd30a7b0a78e682e6e6e1d3/cffi-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9", size = 220241, upload-time = "2026-08-03T21:19:26.214Z" }, + { url = "https://files.pythonhosted.org/packages/99/7f/040f9e163e4acac3ee3d85b02d00b2576e7ca980d8785f0a3a5f1a9bf7f5/cffi-2.1.1-cp310-cp310-win32.whl", hash = "sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41", size = 174578, upload-time = "2026-08-03T21:19:27.338Z" }, + { url = "https://files.pythonhosted.org/packages/ba/0b/644a2ec1a4eaba49c2939410bb1eb1d25b09d6d0582f5d2f95c537043725/cffi-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1", size = 185082, upload-time = "2026-08-03T21:19:28.409Z" }, + { url = "https://files.pythonhosted.org/packages/70/d2/16d99a0c4948febc0ebd133a13b2f688ff7f8cb04da971e1128872ce0c03/cffi-2.1.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12", size = 183838, upload-time = "2026-08-03T21:19:29.637Z" }, + { url = "https://files.pythonhosted.org/packages/cd/95/31b535a9f0220ae9f357de4a08d57ce89cb417653c2fd9f075f50822a388/cffi-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1", size = 184168, upload-time = "2026-08-03T21:19:30.764Z" }, + { url = "https://files.pythonhosted.org/packages/ad/5a/4707a0dc1f203f5dde5a907b0d4e3c25d71120241048bd5bc6f1bb9d4e71/cffi-2.1.1-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0", size = 211805, upload-time = "2026-08-03T21:19:31.867Z" }, + { url = "https://files.pythonhosted.org/packages/ad/66/c19feabb28485b6e0bbaaafa90837a1ef5d302e90f2178bd33f17a49879b/cffi-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813", size = 218716, upload-time = "2026-08-03T21:19:32.896Z" }, + { url = "https://files.pythonhosted.org/packages/a7/92/500760486c8baab49a7a8a58ba7fc3355ec3974b454b8a09e528efde9e1d/cffi-2.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990", size = 205569, upload-time = "2026-08-03T21:19:34.142Z" }, + { url = "https://files.pythonhosted.org/packages/a5/a7/a67c733254d6e7373f7822f8082d8d6beade791e0cf12a7611f376fa61c7/cffi-2.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af", size = 204907, upload-time = "2026-08-03T21:19:35.174Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a4/4399daaf8f7dfee9d7c3327fdb0426ee041cc63edc358b93911ceb2bfc7a/cffi-2.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632", size = 217807, upload-time = "2026-08-03T21:19:36.286Z" }, + { url = "https://files.pythonhosted.org/packages/28/f7/dabe6da2466ecbd82dc62e7342dc6b1065dad990c06f00f0ede9ebf2a0ed/cffi-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd", size = 221252, upload-time = "2026-08-03T21:19:37.416Z" }, + { url = "https://files.pythonhosted.org/packages/ce/87/616202d8e51342c07d2534c510111c4cc37201775ce8f60802c9335d1edd/cffi-2.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a", size = 214214, upload-time = "2026-08-03T21:19:38.507Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c6/ab025d75d2c26c19b087c0124e75ee31cb65032f4fe345d356d8c507ab97/cffi-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa", size = 219408, upload-time = "2026-08-03T21:19:39.809Z" }, + { url = "https://files.pythonhosted.org/packages/db/e2/7e8109f65445bdc673a7b54f02c677de462db75674220fd1335efc8eb598/cffi-2.1.1-cp311-cp311-win32.whl", hash = "sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3", size = 174470, upload-time = "2026-08-03T21:19:41.246Z" }, + { url = "https://files.pythonhosted.org/packages/73/c0/77ba02423c2f7d7091143c45cd49e0e6575c4c1967394bb542bd923a9b74/cffi-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0", size = 185096, upload-time = "2026-08-03T21:19:42.615Z" }, + { url = "https://files.pythonhosted.org/packages/7c/47/9f1f85f9672ceda4984dc6c4f8824e8558992a2972c3d3c81fb8eb28d4ba/cffi-2.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455", size = 179941, upload-time = "2026-08-03T21:19:43.747Z" }, + { url = "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", size = 184821, upload-time = "2026-08-03T21:19:44.887Z" }, + { url = "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", size = 184719, upload-time = "2026-08-03T21:19:46.129Z" }, + { url = "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", size = 214799, upload-time = "2026-08-03T21:19:47.218Z" }, + { url = "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", size = 222389, upload-time = "2026-08-03T21:19:48.331Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", size = 210249, upload-time = "2026-08-03T21:19:49.543Z" }, + { url = "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", size = 208775, upload-time = "2026-08-03T21:19:50.918Z" }, + { url = "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", size = 221822, upload-time = "2026-08-03T21:19:52.054Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", size = 225232, upload-time = "2026-08-03T21:19:53.109Z" }, + { url = "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", size = 223597, upload-time = "2026-08-03T21:19:54.515Z" }, + { url = "https://files.pythonhosted.org/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e", size = 175292, upload-time = "2026-08-03T21:19:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a", size = 185919, upload-time = "2026-08-03T21:19:56.89Z" }, + { url = "https://files.pythonhosted.org/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80", size = 180093, upload-time = "2026-08-03T21:19:58.155Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" }, + { url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" }, + { url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" }, + { url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" }, + { url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" }, + { url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" }, + { url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" }, + { url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" }, + { url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" }, + { url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" }, + { url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" }, + { url = "https://files.pythonhosted.org/packages/d3/7b/d6bbf82b8b96e7391438898c42f5bd96dd02030fd5b64937d248220003e2/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c", size = 194064, upload-time = "2026-08-03T21:20:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/94/e6/bcc91b283be94735e268487a054004f0aa19947b6348fa367db53230abc8/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb", size = 196720, upload-time = "2026-08-03T21:20:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/d9/99/c4b0c17cacdc9c3b8f280026286a9826d6a208c0f047591a3c3ce99b91fd/cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54", size = 184964, upload-time = "2026-08-03T21:20:19.708Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72", size = 184962, upload-time = "2026-08-03T21:20:20.833Z" }, + { url = "https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1", size = 222328, upload-time = "2026-08-03T21:20:22.118Z" }, + { url = "https://files.pythonhosted.org/packages/80/10/c5c0cbf0a657aecf59ef511409734230bf556f05a0d6c9eed7aa5c0a0166/cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062", size = 209985, upload-time = "2026-08-03T21:20:23.401Z" }, + { url = "https://files.pythonhosted.org/packages/d5/6c/bfa0b87b03b9238148beca990292843c9396ba069b54496596594173de7b/cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03", size = 208530, upload-time = "2026-08-03T21:20:24.628Z" }, + { url = "https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96", size = 221525, upload-time = "2026-08-03T21:20:25.758Z" }, + { url = "https://files.pythonhosted.org/packages/82/1d/a4aaf9babd75acb4d5f223bff71533bee748dd770a382619a798960ee9ba/cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527", size = 225053, upload-time = "2026-08-03T21:20:26.985Z" }, + { url = "https://files.pythonhosted.org/packages/81/10/5dc0e7bdd18e22107054288283380fc97a06ae3f1656a106908d666a3c88/cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13", size = 223213, upload-time = "2026-08-03T21:20:28.277Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e9/d0061c364cde06ee43168a0d076ac1da512cbc380d44767b844ba34fe2b6/cffi-2.1.1-cp314-cp314-win32.whl", hash = "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c", size = 177682, upload-time = "2026-08-03T21:20:44.288Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1c3e01e3ba14c39f6d10bfbac52753b7e22259e38088e5cfe1d704918690/cffi-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48", size = 187949, upload-time = "2026-08-03T21:20:45.623Z" }, + { url = "https://files.pythonhosted.org/packages/87/5b/da4e39efe18eeb89cf580ea9cfc66b6a7c3eadb808fc0cc1d3a295cb5a5d/cffi-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836", size = 182947, upload-time = "2026-08-03T21:20:46.955Z" }, + { url = "https://files.pythonhosted.org/packages/23/59/40338bf421c5accea1d45158170c87006ef1cd371b05c077e76476949728/cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3", size = 188504, upload-time = "2026-08-03T21:20:29.495Z" }, + { url = "https://files.pythonhosted.org/packages/7d/47/5ecf1023850036e674c77ec4de86182d309ae344e39e7cba984b7df5d647/cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2", size = 188259, upload-time = "2026-08-03T21:20:31.291Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9c/92934c3bea9f785b23eba304538c0b4d37a2a96d2431eb3a1bc87a11aa19/cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94", size = 223864, upload-time = "2026-08-03T21:20:32.571Z" }, + { url = "https://files.pythonhosted.org/packages/4d/45/ba4c93527bc38616a8bd36488acb69a2212d60486794f0c1f318949bbb76/cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc", size = 211538, upload-time = "2026-08-03T21:20:33.808Z" }, + { url = "https://files.pythonhosted.org/packages/80/e9/b6ef565e452acb932fb0cb5443f44a78efbd1233e566f02b5a83855e9115/cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29", size = 210688, upload-time = "2026-08-03T21:20:34.974Z" }, + { url = "https://files.pythonhosted.org/packages/9a/95/eff5f0cee78d2eabc7eebffec40d3fc1876b5f3c95582e018bb4b99601f2/cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676", size = 223803, upload-time = "2026-08-03T21:20:36.564Z" }, + { url = "https://files.pythonhosted.org/packages/fa/01/579d39fb8bef00a335a23d83757b44feb24cd6345a2c451b64cb67b9c362/cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e", size = 226763, upload-time = "2026-08-03T21:20:37.816Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b0/0b44f47c60b01b57b6e2bbd92343f13a85a1d93bc46ccf6e47e244acd99c/cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f", size = 225688, upload-time = "2026-08-03T21:20:38.959Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d2/3b7176cb570a1d3e27faf67b72f591af508036e0d8b2be2ef9af9e8c84bb/cffi-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4", size = 182868, upload-time = "2026-08-03T21:20:40.388Z" }, + { url = "https://files.pythonhosted.org/packages/56/78/31f00c1bcd97c9bbf55f1bfdf5bc809a5de8887473e90bb9960dca825e80/cffi-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e", size = 194104, upload-time = "2026-08-03T21:20:41.725Z" }, + { url = "https://files.pythonhosted.org/packages/7b/1b/58496f2ed0a35de575250c02a43ab3cc2c04d494a88fed31c1cabc0fd176/cffi-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5", size = 186402, upload-time = "2026-08-03T21:20:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8f/9ebe220eab48a093d1a5a5e339ab0dc7316eef3bb04d63c42f0251b61f50/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d", size = 194043, upload-time = "2026-08-03T21:20:48.179Z" }, + { url = "https://files.pythonhosted.org/packages/ff/69/844bad3ece306c4782c2ecb93597035b6690d48704b803914c199da1e8b3/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b", size = 196737, upload-time = "2026-08-03T21:20:49.457Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8a/af668013284634733f02d683458a0728739c7d6ddb5e14cb0c20832266fe/cffi-2.1.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4", size = 184933, upload-time = "2026-08-03T21:20:50.639Z" }, + { url = "https://files.pythonhosted.org/packages/0c/75/2f5207ff6d1a613133b23a5203cc0c2a628313b5eb3974d7956ae3c57950/cffi-2.1.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8", size = 185002, upload-time = "2026-08-03T21:20:52.173Z" }, + { url = "https://files.pythonhosted.org/packages/e2/31/9e1313b0a6e30e91b3b3d3fff51ae99c857c07738e3afcce1f7334e1b7ab/cffi-2.1.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6", size = 222271, upload-time = "2026-08-03T21:20:53.462Z" }, + { url = "https://files.pythonhosted.org/packages/50/e3/f6234a833e6e08c7007003074723c406559eecf9b48dfc97471e5a8eb7a0/cffi-2.1.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80", size = 209919, upload-time = "2026-08-03T21:20:54.783Z" }, + { url = "https://files.pythonhosted.org/packages/0d/fc/5f74e293fced6edb51af3a46c4ccf6c23c9943774ecb375ddbd522c76add/cffi-2.1.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779", size = 208529, upload-time = "2026-08-03T21:20:56.066Z" }, + { url = "https://files.pythonhosted.org/packages/44/16/29e6d01b388bef055ecd6ca8244b3f4d336bd09e92d5d892187b9601084e/cffi-2.1.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399", size = 221630, upload-time = "2026-08-03T21:20:57.336Z" }, + { url = "https://files.pythonhosted.org/packages/a4/18/fa7f1f6857d5eb88a4ca99ffcbfb7c387a287ccc154c64a73e86314745d7/cffi-2.1.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688", size = 225134, upload-time = "2026-08-03T21:20:58.675Z" }, + { url = "https://files.pythonhosted.org/packages/e0/9f/e8e3dfa04a1b4c241f8c91faacad872b4d4efd051d49764ad4e2fd4b9fea/cffi-2.1.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7", size = 223197, upload-time = "2026-08-03T21:20:59.968Z" }, + { url = "https://files.pythonhosted.org/packages/f8/7e/8debeb04f1ab9fe2a6963964cd6f1aaf7192627b83926586a6a4e089c9fa/cffi-2.1.1-cp315-cp315-win32.whl", hash = "sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac", size = 177683, upload-time = "2026-08-03T21:21:14.901Z" }, + { url = "https://files.pythonhosted.org/packages/e0/31/5158704cc474ab65c1647932e88be78dc0873f47130e253be38bcaf13d01/cffi-2.1.1-cp315-cp315-win_amd64.whl", hash = "sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960", size = 187897, upload-time = "2026-08-03T21:21:16.108Z" }, + { url = "https://files.pythonhosted.org/packages/cc/4b/b3a2da8570c704ffc0f9762cdc3ec0f02c8573798e0b5cf7f11c82bbb70f/cffi-2.1.1-cp315-cp315-win_arm64.whl", hash = "sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1", size = 182935, upload-time = "2026-08-03T21:21:17.271Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ef/5443574510a1207e6f6bc38ba6e1f1de36cb48fef07b2728bb896a21f430/cffi-2.1.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc", size = 188464, upload-time = "2026-08-03T21:21:01.163Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ae/a56fa8c4686ad50e148fcbc8d3ae0d03915ff5c30d795058988c24118cef/cffi-2.1.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab", size = 188262, upload-time = "2026-08-03T21:21:02.382Z" }, + { url = "https://files.pythonhosted.org/packages/53/b2/6187f46f2912276a3ae284076109cc5c8680482f11f766ccf26db4a86427/cffi-2.1.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e", size = 223779, upload-time = "2026-08-03T21:21:03.553Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f6/c3ad28bd19f77047a03084424fbd4cbe997303267c14423737324be0385d/cffi-2.1.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358", size = 211520, upload-time = "2026-08-03T21:21:04.863Z" }, + { url = "https://files.pythonhosted.org/packages/a0/cd/ccac9013a5bd9fd764de118674ab9c805b5ca10c19270d90ee273f8b2240/cffi-2.1.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231", size = 210673, upload-time = "2026-08-03T21:21:06.223Z" }, + { url = "https://files.pythonhosted.org/packages/52/86/2976131c639aead931c5bee5aba67e4b09fbeb8018b6f282f70803f923a7/cffi-2.1.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6", size = 223835, upload-time = "2026-08-03T21:21:07.539Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0c/33a7aeab2f9c76918c52e084beb39c570db3588133412929e8ec06fab90b/cffi-2.1.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94", size = 226705, upload-time = "2026-08-03T21:21:08.774Z" }, + { url = "https://files.pythonhosted.org/packages/e3/26/2cde30fdde421130bfc18f70395731a6e6b2053c6a1978a5258ff04e72fa/cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5", size = 225539, upload-time = "2026-08-03T21:21:09.911Z" }, + { url = "https://files.pythonhosted.org/packages/6d/cd/a361394c94b2129d604bb846f624a8e88255a3ee33129c434a00d715e64f/cffi-2.1.1-cp315-cp315t-win32.whl", hash = "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66", size = 182707, upload-time = "2026-08-03T21:21:11.226Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b5/ba2b299993c26577d529b6ae29841f9e15b9fcf004d65f423f4fcf94ade9/cffi-2.1.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3", size = 193772, upload-time = "2026-08-03T21:21:12.39Z" }, + { url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" }, +] + +[[package]] +name = "click" +version = "8.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/37/edbf1b0707709013c0cf2e8317e03d97ed29bf4cac709fe328904ae92a38/click-8.5.1.tar.gz", hash = "sha256:9bbfada76703fd1d784b83eea012e3e8c6a001a876543ef104fa56aaaeb072b4", size = 399151, upload-time = "2026-11-06T17:37:01.021Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/7d/164b7ffc8624fe9efb4221bf9af485d5c1e41b9ae118045d2b6dc5457d8b/click-8.5.1-py3-none-any.whl", hash = "sha256:8bb5376d2006950386f8c6427613334a2acadc5f559cb80e638d00b4df957e37", size = 126163, upload-time = "2026-11-06T17:36:59.287Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "cryptography" +version = "50.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/af/182eb91b0df3fe75c4d9f26fe70684569566745f6ba7e5c9c73a862c5252/cryptography-50.0.2.tar.gz", hash = "sha256:7b46165bb56eb4704e2eaaf86f3c940d19154535d9b0ca7d6d590b04060e00d5", size = 880623, upload-time = "2026-09-30T15:30:04.884Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/56/d194340cc4a57535e82e1bee9e89667ac4b7c13b5d3f59686deae3094dd5/cryptography-50.0.2-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:fa8f5efb344d6908a1ce62f4a24e2e5780f825d6f53f5f50ec5ffacac72936cb", size = 3914904, upload-time = "2026-09-30T14:43:44.339Z" }, + { url = "https://files.pythonhosted.org/packages/d9/69/c9bd862c3bf43d6399c433caf002df16e2dffd4be49bdf515cda38038711/cryptography-50.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:79def8d059362e7831389ed3be0ecdf58a89386e1271e35dd9f5af84e81bffd0", size = 4731146, upload-time = "2026-09-30T14:43:47.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/69/64cef1f702bf6657e0cc186ed1a2891d50d29fb41586b254e1c07adea261/cryptography-50.0.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:630ebfea3bf689d075f82316324ff7433dc447fe6bc1bfc76524b74b4a9567d2", size = 4719841, upload-time = "2026-09-30T14:43:49.01Z" }, + { url = "https://files.pythonhosted.org/packages/38/6b/61a3f8d8c5e1e49a6cddccafc4015cc1c0021360ab0acb4080e7a423644a/cryptography-50.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f9f6143a8c75945eb960d9eb98905a441394abfa24afaae239d514ffb2586480", size = 4738340, upload-time = "2026-09-30T14:43:50.932Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2e/7212ca32fd43dc91f2f41db20160b268098874b4c9a0e7be94d6835f5b2e/cryptography-50.0.2-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a582ab2ae1d34f67112cadc86702774c9ea4374df6bca6afe672817203c99134", size = 5367029, upload-time = "2026-09-30T14:43:52.911Z" }, + { url = "https://files.pythonhosted.org/packages/1a/f1/b474e930c4d910328780e3940da76f5aa5cbc48ce1fc14e44d239d9ea9db/cryptography-50.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4061c0079120205fb760c58acab6443e217307dcf05e3702cf970e0689972856", size = 4753050, upload-time = "2026-09-30T14:43:55.272Z" }, + { url = "https://files.pythonhosted.org/packages/7c/52/9af10e80ac16b0fcc2123f9cbd5e7afbd0fd5075bb7a607c592258a39cda/cryptography-50.0.2-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:ac9ed99d81760c62fe89d5f0815cdfa1ba9a35141cf30f1c2d044f04b4803d2e", size = 4376724, upload-time = "2026-09-30T14:43:57.24Z" }, + { url = "https://files.pythonhosted.org/packages/71/37/6202e488cc1eb625ea110c292c6bda92823176e023f427d8d5660ce8d632/cryptography-50.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:87e9ce85beb6b328ba370cc6e6aea483c92617b4c95b1d33a49297eb662bfb04", size = 4737859, upload-time = "2026-09-30T14:43:59.541Z" }, + { url = "https://files.pythonhosted.org/packages/8f/30/e86d7d518489b0ae2497091a35287abcb1a2ce4037837a34afbe9b1d6964/cryptography-50.0.2-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f265528741e048bce55c3463ed721fb0aa45a5888d8add8cfeccb3035451bbdc", size = 5324103, upload-time = "2026-09-30T14:44:01.901Z" }, + { url = "https://files.pythonhosted.org/packages/d3/69/2c833a049475e0a3444e94c7d0aca0aa51d166374a449b09e92ac98138de/cryptography-50.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9dab55f57c74c3cad24c323bacbbd04be4705ba6eb0d92e920b1fc4837ed5079", size = 4752576, upload-time = "2026-09-30T14:44:04.545Z" }, + { url = "https://files.pythonhosted.org/packages/6c/5d/906970b83bbfc1f5bbfb677a143c181f2801f23b6a7204a3b47c42c97e65/cryptography-50.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25784ce8b9621c90c643efb9e1e2162ab3b0224cae446ad5e70e7fcb1ce18b51", size = 4870819, upload-time = "2026-09-30T14:44:06.884Z" }, + { url = "https://files.pythonhosted.org/packages/68/e3/f2298d3bb55e0c4a91841ec4d01b3f020ba8c5fbf15ccdcc6dcf03f97025/cryptography-50.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:85d0d9a31b9098e98534226d5686b47264b95e62ce459dc2e62fdfc809f9fe93", size = 5030152, upload-time = "2026-09-30T14:44:09.443Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4f/adfc442765721292fff86d314ce385d3249d22db42295c0dd057727b60f3/cryptography-50.0.2-cp311-abi3-win_amd64.whl", hash = "sha256:7afa5a6602a9f29af1f3a2965f831bae7c9d5d597b7cbb716d41ab3b7d89879c", size = 3824692, upload-time = "2026-09-30T14:44:11.671Z" }, + { url = "https://files.pythonhosted.org/packages/ce/cb/52eb3770c0d0be2702a98c6e96065ddc0a2877cf0845aa9c23397c142cd4/cryptography-50.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f785f6161f202ab04d8ca194158968798e480ca058943907972da5f12e2881e8", size = 3892731, upload-time = "2026-09-30T14:44:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/19/8e/aa1fc533d4546b127b45de8aa024eb5933d23eff9debfe25931e56861095/cryptography-50.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0ecbc5652bdb6fc9eaf89a7d196e20941adfe812f43bc4ca05d9150496821047", size = 4710431, upload-time = "2026-09-30T14:44:15.427Z" }, + { url = "https://files.pythonhosted.org/packages/6a/64/72bc3f75176e7e406b748a3e3830432b8c51297b38368713df04dc04898a/cryptography-50.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ab50ee449bf968271e820086f10a33d101dd060370abc10bcd22279be2656539", size = 4694824, upload-time = "2026-09-30T14:44:17.69Z" }, + { url = "https://files.pythonhosted.org/packages/4e/c6/62c77550edfa5ca3f14bf44a1e6739b9fa09d6e998a11d97ed8213bccc98/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a9f7355e6fab51f6c369b86fb7571cffa05edee2c2121e0380a37fb9ac1cd5c1", size = 4716967, upload-time = "2026-09-30T14:44:19.661Z" }, + { url = "https://files.pythonhosted.org/packages/f4/37/cce70f150c432914460157a6ecc161752e053aa5ec0ef3b3f7dc6e31039a/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:94e5e9f108ee10471288214d3d233fbfbb492840a8457eb85178d643ddeb32c7", size = 5328676, upload-time = "2026-09-30T14:44:21.744Z" }, + { url = "https://files.pythonhosted.org/packages/aa/9a/6f2f0304d634ceafdeaf23e84537336664ac419b5d07611675c2ad3f6b7a/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:241449bf940a5d27309bd317e6f9a2af6932113818bb2b8f5c59ddc7ef16da18", size = 4727698, upload-time = "2026-09-30T14:44:24.178Z" }, + { url = "https://files.pythonhosted.org/packages/1d/de/66bcf9244d118663b2e1aaded8990f4640e3d7b7411870a5765f252074d2/cryptography-50.0.2-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d8947001be83df1394050758ce0e745dd74fb134eef0a4b5124208dfc3a68c37", size = 4354821, upload-time = "2026-09-30T14:44:26.263Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e6/db28a28c7b6c676addce89136de3d8db49ea825a8c863472e36e42ead4ad/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4a20ce1e5cb4284a86692fdcba7cb8754185c6b2e5c56fcef3751cf451d3cdc2", size = 4716748, upload-time = "2026-09-30T14:44:28.447Z" }, + { url = "https://files.pythonhosted.org/packages/30/96/01546c7f69ea0e2ab790a2e4f0934a4052fb9b388147fbf83c2fd72f1e57/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:84f964e537f916e2cc85199e5a88742e964939b575ac8598b3f9d6cc416cdaf1", size = 5285085, upload-time = "2026-09-30T14:44:30.704Z" }, + { url = "https://files.pythonhosted.org/packages/6c/01/03263395f74d50b071e9e66daace3f8bef80493e5d410726f2ba8554736b/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:828d49b0ff5a0e3975865571c5d91dbbdd0d38d8289b249a163e9425413a5e05", size = 4727268, upload-time = "2026-09-30T14:44:32.92Z" }, + { url = "https://files.pythonhosted.org/packages/eb/94/2bfe8f29ec0cc9c0d99359c4161adf32858e4934b72c6d100d2ac0bbe962/cryptography-50.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:deb9fde5c60e437ee4821bc9bc39ff31b42135c27e1dc61ef0a629389c1de62e", size = 4849503, upload-time = "2026-09-30T14:44:34.969Z" }, + { url = "https://files.pythonhosted.org/packages/54/44/e80651ecbf0e42b62e2bb5f5768916e07eea72e1297338956a61df361f88/cryptography-50.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8c71ba2cd31fc93748c38e1b613200ff1c2665cbfd5341fe3a61cfde35a1430e", size = 5004057, upload-time = "2026-09-30T14:44:37.064Z" }, + { url = "https://files.pythonhosted.org/packages/f8/cc/1d33befb3cd7ea7e77d2d73f43f2066471da1b21f24a6156efcaabf6d2e8/cryptography-50.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:78198641e5be9521beea5aa782bb551a58068d10e6eb04c9c680c1b69f2e7d45", size = 3795868, upload-time = "2026-09-30T14:44:39.71Z" }, + { url = "https://files.pythonhosted.org/packages/2d/49/93f6a6e7a87c9aa68d44d3e1cdb5fe8f60c90d5d2f46acae9a56892816b8/cryptography-50.0.2-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:edc3342adf8f697fc5f59c887a304356f147b397809440ed64e2fa6af2f50f37", size = 4133708, upload-time = "2026-09-30T14:44:41.807Z" }, + { url = "https://files.pythonhosted.org/packages/8c/75/32ac2a56243d778805c16ca6a32b8f74fb757df7e28d7ecb560afafb59cf/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d370b8d1dfcdf7130178137f6fbee6140774a1acc6cacefc4b42643ec11d0a3a", size = 4956267, upload-time = "2026-09-30T14:44:43.693Z" }, + { url = "https://files.pythonhosted.org/packages/aa/a4/2c8d734e43d97f0842ee9f1b7b4bfb3d0cf5e19edebf43c2afe6675c2320/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2f9bd7f90c64fe89253f0a2c05e3c4856072660429ce8831b4235bf29403a67", size = 4966465, upload-time = "2026-09-30T14:44:45.769Z" }, + { url = "https://files.pythonhosted.org/packages/c2/58/ee288c829a6f41f6235ae9dd33d82fd19b45442b65b4c8a3da36963d9f7a/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_aarch64.whl", hash = "sha256:e275096ea1e60cc595cda2836fd4a6c725d1125108b868be17f53684d164e2cc", size = 4959356, upload-time = "2026-09-30T14:44:48.211Z" }, + { url = "https://files.pythonhosted.org/packages/92/20/9ded6d51ddd9897f6b6e81fb9ebea7951d7cc5d6c890b0ed8abf77a51a80/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_ppc64le.whl", hash = "sha256:b13478603dcd0a2479ff8e87e2c19a7d525734686fe3c49542472293a204212d", size = 5548822, upload-time = "2026-09-30T14:44:50.86Z" }, + { url = "https://files.pythonhosted.org/packages/02/a8/8df951850d6b31d2a00218f19e2b3f999523437ed7a819df7fa427942fca/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_x86_64.whl", hash = "sha256:58a0c478eeca76fe5e07993c5a0703def34a6dc6a0cda4f5564639b33112ffe7", size = 5001199, upload-time = "2026-09-30T14:44:53.379Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f9/36b3022218ce75b7cdf068fb95f809f9bd0d820e4955ef43b90c255cc7ac/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_31_armv7l.whl", hash = "sha256:d38cdff612d06fa6a32840d5e1b1f7a27cee4a349aa9085d94a67789d6bfd408", size = 4629333, upload-time = "2026-09-30T14:44:55.635Z" }, + { url = "https://files.pythonhosted.org/packages/8c/72/20f99a219f6af47cdd1cbd978c243b92d71496e168a746138af44ded4f29/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_aarch64.whl", hash = "sha256:fdd28f912fccfec1846a94e2e1e8f9b0012f557f0c46fe4f3eb0d7a87afcf90b", size = 4958822, upload-time = "2026-09-30T14:44:59.639Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/196f112617fb08eb4d608a2a6c422373d46f9cc2857f38fc0667033c0899/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_ppc64le.whl", hash = "sha256:cbc8738fd8526d80f35cb3a40d41f41a2e7030bb3b18b09a6778ef63d291c2fd", size = 5506351, upload-time = "2026-09-30T14:45:02.267Z" }, + { url = "https://files.pythonhosted.org/packages/24/95/83378121ef3eaaaf71d4b781577ff794acb39b9e1b87a3f156898c8497ed/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_x86_64.whl", hash = "sha256:e105ab60406787da31fccc883fc0f733af1efd78f0136a4599692c4083a73d0c", size = 5000859, upload-time = "2026-09-30T14:45:05.009Z" }, + { url = "https://files.pythonhosted.org/packages/22/f7/70fd7ae4d1dbfa7ba29b02e1b9068771519a86027756510b700ce81086a8/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:6f8700550aa1474a91e5dc07049c46f98b423b5b1ddd0483e0b51362eeeaf5be", size = 5092151, upload-time = "2026-09-30T15:29:15.932Z" }, + { url = "https://files.pythonhosted.org/packages/d4/be/688367b74de86984bd58d8efacfc7c9e68b89a6a22ced0fb4f38db50254a/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:c71be1cbfa5cd9a41ee452acf1eccd82b2c05950358b106ec8ceb83411d1a020", size = 5286120, upload-time = "2026-09-30T15:29:18.309Z" }, + { url = "https://files.pythonhosted.org/packages/39/d1/55f8a3f2ef5d1529e16835ef10cf0fe3d559ce237b46dddc440c0bba3649/cryptography-50.0.2-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:c423ab384a46c4dff7217b2ea5ba2e11cffdeab6441acd04cf65a369caf0366c", size = 4111557, upload-time = "2026-09-30T15:29:20.155Z" }, + { url = "https://files.pythonhosted.org/packages/23/ad/ac987755d00e1e64273760228d2635ae38dae2be83e3c6e0d3289d91dec3/cryptography-50.0.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:0ec5f09541743261e66e291b4a0cbf0fb2997aeaab6d9e9c740b9dba1b58d1c2", size = 3943588, upload-time = "2026-09-30T15:29:22.265Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8d/6d585339bedf85d45044c85d8412dac53f2bb6f918e8b7777efba1787844/cryptography-50.0.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c5e67125c7dca78d199ec4e116aa93dbb83494808ecbb8211a2cb09b1bf41dbd", size = 4756166, upload-time = "2026-09-30T15:29:24.58Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f1/1c1f6874e8550cfddd4b688ceb38cefb6ed15ceed224d56f133f3d88c214/cryptography-50.0.2-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee247f5c245c9a2fe7c8e2214e295918838e44e00a45a6718451e4004219e767", size = 4749145, upload-time = "2026-09-30T15:29:26.807Z" }, + { url = "https://files.pythonhosted.org/packages/c1/63/61b15dc1a8de03fe0adbe3fd7608b3ad5c73bf50993bbcb1faaa930afe33/cryptography-50.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dfe9763530994147d9af1def057a5b9658b00e8f8fe8743d144d1e0911c2e454", size = 4763638, upload-time = "2026-09-30T15:29:28.588Z" }, + { url = "https://files.pythonhosted.org/packages/fc/35/b345bdfa40c9126df1a9d33236aa98418367931b8725f84fc3ae2b98dc59/cryptography-50.0.2-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:58ddb5a8e3179d12f19e4ea34d2d32e9d63a4baa142c875c1eb59f41b7243acd", size = 5382217, upload-time = "2026-09-30T15:29:30.589Z" }, + { url = "https://files.pythonhosted.org/packages/4f/87/ef344a9e616871f2519c22d6afcda79ddd5d35e9592d95eb6e677608d055/cryptography-50.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f21e8a22c8605750c7af886bab299a363721264061b4ac0a30efb73cfd58efc5", size = 4781387, upload-time = "2026-09-30T15:29:32.605Z" }, + { url = "https://files.pythonhosted.org/packages/90/5b/f2fdb13cd0b96f6f932c8627bb292a45f11c64d21620a8e120aee9a3b848/cryptography-50.0.2-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:9c8402a82ea0dc4ceeab793db05f0fafa8ca139ca34fcde5df0f596103c74107", size = 4403790, upload-time = "2026-09-30T15:29:34.374Z" }, + { url = "https://files.pythonhosted.org/packages/bc/ce/7e4f662b1e3c393513569e402cfc85ac7da0bd3d5435e122a3140219eb2d/cryptography-50.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:0ddc924c04591c2811ca024d62ecad4f7f6f08af8939c211438f48a16bd23602", size = 4764319, upload-time = "2026-09-30T15:29:36.149Z" }, + { url = "https://files.pythonhosted.org/packages/3c/3f/86ff33ce34cc0de6847fb96e035a1a760d81652e38643f617c02ad32ef7a/cryptography-50.0.2-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a6557e5f38e065ca9fbdaf7cfc7435ecb1d113aa81a022d1b51921ee7432e227", size = 5338560, upload-time = "2026-09-30T15:29:39.053Z" }, + { url = "https://files.pythonhosted.org/packages/40/cf/6b5c8e2fd9202d98988ab7cb5cc5c991704c4ad55f492ff408e4969f83f1/cryptography-50.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1981f1db4630889b9ef7803fadef12b056f428cb6b85c27ba57b774793b6093c", size = 4780973, upload-time = "2026-09-30T15:29:41.251Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/8d6ebc7dded797bd0f0160d52188021211f011a2b164ef0ae1dac4587465/cryptography-50.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a8701d6b584d76e909e3d305b7d126b41439876a5aaf76cddc67fc230eafa2e", size = 4897738, upload-time = "2026-09-30T15:29:43.106Z" }, + { url = "https://files.pythonhosted.org/packages/d4/aa/f3f6e0de7e6253b8baa8b2d8fb9d50924fa75cee3d4624bd4bc1208ee923/cryptography-50.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce47f66801c20ec6c6632453bb5960fe38939e9306970b48b3a5a26de7745d94", size = 5058280, upload-time = "2026-09-30T15:29:44.827Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b6/a1faf3a27ae9405fb34b1713cc73b2d8a26b04d5c561578fa2e6ef3e5bb9/cryptography-50.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:4e81d95e5bafc2d6e34e4bed780e53e4d5b9a2f928573428aa4d35fbec1eb0de", size = 3854095, upload-time = "2026-09-30T15:29:46.782Z" }, + { url = "https://files.pythonhosted.org/packages/1d/7a/f08d34ce09d60f89ebd391e2ebc6ba2b995e6dd7552f41820f8085f94e53/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:92e665960f25fcdc73725b9cec7a3824f279ba97a98653afe9ffac2e43668f67", size = 4716035, upload-time = "2026-09-30T15:29:48.681Z" }, + { url = "https://files.pythonhosted.org/packages/45/67/e18fb65592451a2acb76e9f2fbe14e0f47a8318b4c5430f1633851d03daa/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eef4c2f3423810b3070ab391f85436d2f8bbfcb286ac15cbc73190b3563b1f1a", size = 4726917, upload-time = "2026-09-30T15:29:50.608Z" }, + { url = "https://files.pythonhosted.org/packages/83/28/38fdce17e60f6b825e69fc3b7f75e70a6612759980704697e1de4cbfaf6e/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:7c6d0330c472d96f6a6afe24d80dfdf15176c33096f0a4397ae4c60f3dd3be48", size = 4715341, upload-time = "2026-09-30T15:29:52.522Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b1/d9121a717e0f893c64bd6ca7702614778d7df2a5c309128a002421788516/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:1ba34f04897fcdaa73f74145c25f3ec146fbd56593853e88adc2e811303c5f42", size = 4726322, upload-time = "2026-09-30T15:29:54.263Z" }, + { url = "https://files.pythonhosted.org/packages/36/8b/e6d153808bf353e152abd2fd4d8f09670d956ac78379ac46e60d7efbf04c/cryptography-50.0.2-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:3dc4fd8058cea1644971207d530e1a03a184a805ffc8ebdddf0599d78a331b81", size = 3873097, upload-time = "2026-09-30T15:29:56.097Z" }, + { url = "https://files.pythonhosted.org/packages/ca/1d/1271f287ff7170ddafc2aad36260c4eec20ccd2fea70f38455e9d56d427b/cryptography-50.0.2-pp311-pypy311_pp80-win_amd64.whl", hash = "sha256:7b75de3c8b3be1cdb1052747c929440c3eea46c1bc2cb8a6e3a48388e9b7b452", size = 3805376, upload-time = "2026-09-30T15:29:58.729Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore2" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "truststore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/61/afa9d6b7452f9eca73f73d45324eb0bb52f098fd5e6049bf6b8263be9b4e/httpcore2-2.16.0.tar.gz", hash = "sha256:c72cd0413563b17e1cd8354677dff887466f8ec2551679759e93126534428643", size = 69886, upload-time = "2026-10-21T07:09:45.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/c2/ce417c397afa5181f0e3c6ff01a4d01639414e1adef08730d97571297151/httpcore2-2.16.0-py3-none-any.whl", hash = "sha256:5c4358319f2602d984ad93182c9fee5160f22e9da7babdda870a5e7c4e2dc34f", size = 85070, upload-time = "2026-10-21T07:09:42.802Z" }, +] + +[[package]] +name = "httpx2" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", marker = "sys_platform != 'emscripten'" }, + { name = "httpcore2", marker = "sys_platform != 'emscripten'" }, + { name = "httpx2-jsfetch", marker = "python_full_version >= '3.12' and sys_platform == 'emscripten'" }, + { name = "idna" }, + { name = "truststore", marker = "sys_platform != 'emscripten'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/73/5ca2cedf5e4e50423d1c258877865a6b62793633b561c58499f8aeb33b7e/httpx2-2.16.0.tar.gz", hash = "sha256:0d54c98128e8f2ff7fbbde0fca8c359e5f032edeaaaac443cdb7f31f685bcd7f", size = 103116, upload-time = "2026-10-21T07:09:46.348Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/3d/2e4bc4fd1ef9770871669adb348d612a6e4786b303a23f9935b81c48dd60/httpx2-2.16.0-py3-none-any.whl", hash = "sha256:fd2c5d7909e8d86fe92d53020c310ca0e4e431b838c207c5754b9bbf0308d49a", size = 97880, upload-time = "2026-10-21T07:09:44.01Z" }, +] + +[[package]] +name = "httpx2-jsfetch" +version = "1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/0e5636363151a2a1795e0a77617168b9ca438e1748ec05fc9b5687f93d64/httpx2_jsfetch-1.0.tar.gz", hash = "sha256:70a0e3eabfef7cce5ad9c629f7d01ca05e418f586646f4ddf14782e4c1454c60", size = 6872, upload-time = "2026-08-07T00:13:07.492Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/43/832f631d32e4f1211caa2ba368317739fe71f0b8530e4c9d15dc454bac2a/httpx2_jsfetch-1.0-py3-none-any.whl", hash = "sha256:cb916b707601e69a07721aabc8f3f6659be3a6893bc1ff5c6f9e02241df2da32", size = 6382, upload-time = "2026-08-07T00:13:06.567Z" }, +] + +[[package]] +name = "idna" +version = "3.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/08/8eea9d4b8302028f3abb2c0813953f7aec26d33b7a8960ed760e65ff29fa/idna-3.20.tar.gz", hash = "sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44", size = 216463, upload-time = "2026-09-17T14:11:04.752Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl", hash = "sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c", size = 69583, upload-time = "2026-09-17T14:11:03.168Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/e1/2069291243c926a2ff1cd706c7f3eeb9b62144bf60f77c9fb9ff2fb26bd3/iniconfig-2.3.1.tar.gz", hash = "sha256:67f4b9c50da0dedf52af349e7749a80a9057a5031199791b906c3bb3ae878960", size = 21209, upload-time = "2026-10-06T22:48:38.076Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl", hash = "sha256:9121e2c1fdb355232495be3194c8dfe87ccc2d5dee45947b78e68f499790d7a7", size = 7552, upload-time = "2026-10-06T22:48:36.959Z" }, +] + +[[package]] +name = "jiter" +version = "0.19.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/67/de/8dfd06a2153d8ab6a83a8ca8f8ae3ff860ce2fe324ee6a5d05294b621c2a/jiter-0.19.0.tar.gz", hash = "sha256:ba3064f8be276d7cfb83c2fb85c858be533da2c2f80f9b048c3781b45a57a195", size = 665248, upload-time = "2026-11-06T15:03:33.462Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/c5/3f98ccee535b6481fc401c594d23e07ca400d940ea8f9100a06262e07d66/jiter-0.19.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9e1773b9a58b76c6c5398557f9beeddc64fdbb31ba66ef940280a6d603e0b989", size = 289386, upload-time = "2026-11-06T15:01:03.741Z" }, + { url = "https://files.pythonhosted.org/packages/91/60/2e8cd6da62ef71153a7b430c3b98259802af75546ea35e5f34653c672d68/jiter-0.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af3ee9471733f88d1f13061fc58e4731a6b0141bf47e08c39a95a9bb391c3955", size = 318196, upload-time = "2026-11-06T15:01:05.532Z" }, + { url = "https://files.pythonhosted.org/packages/1d/27/4605012ffeafdc3d7733694d2d5083796381770973fe41b7371dc6544e16/jiter-0.19.0-cp311-cp311-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:fac2b90ad1709af1a4a5419e2031ff7d950810a584343c747a8087f842a4278e", size = 333702, upload-time = "2026-11-06T15:01:06.957Z" }, + { url = "https://files.pythonhosted.org/packages/80/2b/ca43d02e3ca5a3d8659d2d00f4c1a58408a05bf9db07a817f551ce7c365c/jiter-0.19.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:da606acf39d7d2f2a56bfbb48a0b36f25da1d22d801cd0c9aecfce2f791e3239", size = 337353, upload-time = "2026-11-06T15:01:08.311Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9c/b9725a39cec632737a82521a6320aea46b8dde418ecc1fc2c86ea0a415d8/jiter-0.19.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:d60d0621f0d6f330a1fd92067340a8eb4262651a07b2dfd92a87e6527d05c933", size = 323554, upload-time = "2026-11-06T15:01:09.893Z" }, + { url = "https://files.pythonhosted.org/packages/1b/33/f72b9252b63c3a56920cbf44360722ef4a40f3e74830ede6ec9c02a26254/jiter-0.19.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fdc0a6ddcc9228a0955b350509041c9321af91e928c918402d0212d5fd315355", size = 335673, upload-time = "2026-11-06T15:01:11.135Z" }, + { url = "https://files.pythonhosted.org/packages/ef/f9/a422ccb7f89adc6238a860bea22816fae1d903a48289dccff44a1399b49b/jiter-0.19.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:e0238393882c0737846239bf4a553140e27d6b6781e87f570e7c3d671c916bab", size = 349297, upload-time = "2026-11-06T15:01:12.461Z" }, + { url = "https://files.pythonhosted.org/packages/43/06/37a1051b8fe6189c9cc87d4483fc3b30841b9678cba95caddcadc77d6ead/jiter-0.19.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc4a2b70ecd4f102583344c1f8cc5dc11618665b1889286bb3b4279c19b337dd", size = 337119, upload-time = "2026-11-06T15:01:13.961Z" }, + { url = "https://files.pythonhosted.org/packages/20/a9/6de8dab28150e381b7733a27adaae42bc22af52b79a198c09ad9f046c85c/jiter-0.19.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:23a9cd6d7779b0022b9446d7f8b19ba81bf9631d494ca371d810d3925e10fc45", size = 316181, upload-time = "2026-11-06T15:01:15.339Z" }, + { url = "https://files.pythonhosted.org/packages/ee/df/78e3721c6c452a7209764d2d9760746bdbe69de7c8b93797a149c5cd80f8/jiter-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2777713f5c0b49fe909ee0a68320bce095cdcdb9b7611d347d49394f24afabc0", size = 481784, upload-time = "2026-11-06T15:01:17.188Z" }, + { url = "https://files.pythonhosted.org/packages/52/af/6101a311e708c24587c44ab0b142f8d077c5b4c93285c48b63c7a99a5ed2/jiter-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fe16c8e0caf7513c6b4e1c6466761b153aa16c4e18a84ddcf6dcf731b2bace1a", size = 516223, upload-time = "2026-11-06T15:01:19.004Z" }, + { url = "https://files.pythonhosted.org/packages/3c/72/d7cc3fe03ded6f0c0c49329e542969ec180f3ecdb8c947f162914bd0c097/jiter-0.19.0-cp311-cp311-win32.whl", hash = "sha256:7f1ce80a4f1eee5570f824300bca01b5b1d4174a84ecca6d7019c1159176519b", size = 187725, upload-time = "2026-11-06T15:01:20.655Z" }, + { url = "https://files.pythonhosted.org/packages/6b/97/0550927e719aca463c4ef4858ade937c434efbb4a68a66a6943cb23167fd/jiter-0.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:7b9107bb4ec6889f943c948d69cee0f0ef64f5c5a7f6a8a6a6c5f23e8ff5f557", size = 226786, upload-time = "2026-11-06T15:01:22.018Z" }, + { url = "https://files.pythonhosted.org/packages/f0/1a/b437e2536ff37ee461f0bfd8fd7c99f541753b3737ae3c4be22a03ed9707/jiter-0.19.0-cp311-cp311-win_arm64.whl", hash = "sha256:9cd6b141ccfac1fa05c49b579717505304caa0cfb41ab569baffcc109077e492", size = 185632, upload-time = "2026-11-06T15:01:23.47Z" }, + { url = "https://files.pythonhosted.org/packages/8c/cd/88cf9efec4d4b6cb816233a6fbfcd842e9675012b2827c6abee4f87e53f1/jiter-0.19.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0282eae10bbccfabcb6001ced6a20abc5e05e26c6316e7729c8fc1510bd3b8e1", size = 290088, upload-time = "2026-11-06T15:01:25.103Z" }, + { url = "https://files.pythonhosted.org/packages/86/56/371651ed5fdbab064b9ca39f5ceddd1bad2175bc1d4c81915eb63ac1fa3d/jiter-0.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:730b0387fbea21b71b86b6975e20da7a364b04c4428eba0d15e6f0a7b20aa59b", size = 314238, upload-time = "2026-11-06T15:01:26.672Z" }, + { url = "https://files.pythonhosted.org/packages/71/56/357172c32d0763f5cf8eb1afb8f5ded5ba314856eccbdb600fc82efe8b18/jiter-0.19.0-cp312-cp312-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:cfbf731056b76d2d600f11ee895ca5d4d39d22d881562d65ada3ecbaf6053d25", size = 332123, upload-time = "2026-11-06T15:01:28.273Z" }, + { url = "https://files.pythonhosted.org/packages/b1/a8/9c0e83f11fd067d1973e8f801e78d0c2ea1186aef7cd6bf9e7c847c16836/jiter-0.19.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fcce75459e06cf811395581f6d256e2535d165ec4d223b809115a59b4ea6f398", size = 335561, upload-time = "2026-11-06T15:01:29.834Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e9/d49c126dad2bc47d8e02392eb7ffae797134f021f4dcfc15bccc3b704c7a/jiter-0.19.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:10e863d52095bc1106d22a4b9285bbe722a56d7f3e65f9a5d09ec9814a31be43", size = 322601, upload-time = "2026-11-06T15:01:31.313Z" }, + { url = "https://files.pythonhosted.org/packages/ad/d3/9085b225b080218f7ae7f3f33b746686cabefa99cbc34895fd35221df4d6/jiter-0.19.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bafc856de75f6d4400e2d839189a6aefa1c985a151893743262811495917c20c", size = 333667, upload-time = "2026-11-06T15:01:33.032Z" }, + { url = "https://files.pythonhosted.org/packages/fb/2d/d71dbb7bb2f5954e2f33151de25721abf03771fb5368acda88f781b419d0/jiter-0.19.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:562d329189727922d9ee2e58f37382de1d0ec0d7a69b8d9670a23ef7ea216a49", size = 348693, upload-time = "2026-11-06T15:01:34.541Z" }, + { url = "https://files.pythonhosted.org/packages/8c/41/22f74631763c50a6ec76fec1e6a9557ffba181fe5048135520284a5746f3/jiter-0.19.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0956b5f58b804221dfb929c120e6d4032138fed20c1cc0ef683a2d1326255e46", size = 338071, upload-time = "2026-11-06T15:01:35.871Z" }, + { url = "https://files.pythonhosted.org/packages/9b/4a/66e5d347e63b452eb160b549d1bb9bc88b2e7ad4d55c900afd278b8ec85b/jiter-0.19.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:aa8e941d7569417134d14ed982bd5cef2c9baa55748bd70601f29e59b65497a9", size = 314409, upload-time = "2026-11-06T15:01:38.148Z" }, + { url = "https://files.pythonhosted.org/packages/cf/19/919970ab010e41cc8daae6e3b225cf457b09f9ce1a8458a0b949272d312d/jiter-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:39032d61473b11a6201300a710d6f912935bb3c783f3b564f94caa4d8ea34f40", size = 480350, upload-time = "2026-11-06T15:01:39.515Z" }, + { url = "https://files.pythonhosted.org/packages/6c/53/fef6d7566fdc019201be101bb63056a01ac3746c4f072901318593c65a2b/jiter-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1427fd34cf36db7c13d9643465a9fe00bfcc9ba969d8a633e43a4e2525afb565", size = 515375, upload-time = "2026-11-06T15:01:40.9Z" }, + { url = "https://files.pythonhosted.org/packages/50/94/0eb1a877a9e34fe757aa6fbcb9e19a996540283fafa4826c4fff816087bc/jiter-0.19.0-cp312-cp312-win32.whl", hash = "sha256:178bdffd86ccaf25fda33dee522b046720b1f617f5b72864a720a4e88d574fc6", size = 185353, upload-time = "2026-11-06T15:01:42.628Z" }, + { url = "https://files.pythonhosted.org/packages/10/7f/9d1dd194cc52698a9c7116b0b464826cc1a2feda2afd12fa562182ff27d6/jiter-0.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:f64154096d9aa5462ef3c4f48d624492602d0073d54d30c20c08793387f67e42", size = 225500, upload-time = "2026-11-06T15:01:43.975Z" }, + { url = "https://files.pythonhosted.org/packages/98/d9/5065590d951e03216b7b0beba984d397d1e724201dc57184503f79d696eb/jiter-0.19.0-cp312-cp312-win_arm64.whl", hash = "sha256:d9cf0a878f99f97335023f51bf07e1f81c19ce13636b06347c8b548e3a26bc70", size = 181832, upload-time = "2026-11-06T15:01:45.279Z" }, + { url = "https://files.pythonhosted.org/packages/c4/b0/9af5c95d02e75ec5ee2c45ab37a7ead761cfbf1807b6b568944baa371699/jiter-0.19.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ee63ba51f837d5a4bc2d79f6255ef0e754922519b172099fe4b9e8b1a6c85513", size = 289637, upload-time = "2026-11-06T15:01:46.615Z" }, + { url = "https://files.pythonhosted.org/packages/72/31/826662abf9197a96ac347f4f7456bca96e751de71ad8983de1ae06f210ef/jiter-0.19.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ea284d5a9391fa7b6bec676569a91463325298532887d3b324b27d541f6ffc1", size = 315796, upload-time = "2026-11-06T15:01:48.327Z" }, + { url = "https://files.pythonhosted.org/packages/76/c2/a4e62a7af0cd7f17423593437b721f4035b025b1d4ac0f2b996ed6d059c4/jiter-0.19.0-cp313-cp313-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:345f9f5dc1496a17b83cbbe7c351f94ddcd77b853518b0382b82702652353c9e", size = 331137, upload-time = "2026-11-06T15:01:49.675Z" }, + { url = "https://files.pythonhosted.org/packages/aa/60/edd90b139888ec1f40ff1d37b5d446447164806dc607706b933172277ab3/jiter-0.19.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d056e8cf0ad1c4676ae559311d4a90b2613ca415452f5072bee62ee7424a094a", size = 335268, upload-time = "2026-11-06T15:01:51.172Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d1/5a5a8684097942c24854f4308b36cf630ed573a94b06305dc146c74e0bb7/jiter-0.19.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:62fda39be2cd67deca433e8ff9b125b285a592c5b5a439a5e80ba18337a73d2a", size = 321592, upload-time = "2026-11-06T15:01:52.58Z" }, + { url = "https://files.pythonhosted.org/packages/fc/94/f4466e2ada992e82f4967021953121039998b8e4ad83ebfb2b6e2c2a401c/jiter-0.19.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a09edc861155f4df705a382e0d241e0ba4bf504a1438a6cd3a94e8d3fe2cf37d", size = 332880, upload-time = "2026-11-06T15:01:53.849Z" }, + { url = "https://files.pythonhosted.org/packages/1f/b3/11a1d37dcdca6edc003ac6f2512c1dd37b0f0c2dbf7c988e5213e1b29d19/jiter-0.19.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fc13db05062df81357bdb926fc6b1014ce8ebef511a0fe7520b818f43b74fe5b", size = 348166, upload-time = "2026-11-06T15:01:55.216Z" }, + { url = "https://files.pythonhosted.org/packages/30/f1/75de49da39833a8be13b47468b99876c1f4a96b301c6a3fa3ad6a7d87e7d/jiter-0.19.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3b27d6bec319dc17cdced13e495f90ce066676ca296fc56f2dd2128f6a7599ed", size = 335970, upload-time = "2026-11-06T15:01:56.493Z" }, + { url = "https://files.pythonhosted.org/packages/6a/95/6ceb994aae2511e94da56a3def4795e0b797357af252c824bdfdaae70323/jiter-0.19.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:541669df0273fafdc506e89d7750afd528b6094586f87c01291c62f3273575ed", size = 313765, upload-time = "2026-11-06T15:01:57.768Z" }, + { url = "https://files.pythonhosted.org/packages/02/a9/ee7f3f2ceacb6e4593cfe9d9b7eff3b2b404efe2e05e152070a50abe42a9/jiter-0.19.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:bcf434316c96f58b4623500c01498506cc1d3fb689e948158a5955dc245202a4", size = 479932, upload-time = "2026-11-06T15:01:59.149Z" }, + { url = "https://files.pythonhosted.org/packages/f5/50/127a653d5292c6d9e6ec993c3948e3b9f3d0fbc6af91094dbab175ee8d3e/jiter-0.19.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8b9d75a51df8f3b6fa7e25a0c0d7c4735e9d2196d6441e5a7c5b4ef1b76cd8bd", size = 514729, upload-time = "2026-11-06T15:02:00.695Z" }, + { url = "https://files.pythonhosted.org/packages/54/23/17c033c7336a5ec7d4c6b10edb851c96ffc9c0652d9f5b14ea5bae6028de/jiter-0.19.0-cp313-cp313-win32.whl", hash = "sha256:a39f45078cc2b75ac91b48833093c033e374f8224ad38576ebba06398a1de96b", size = 184740, upload-time = "2026-11-06T15:02:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/2d/ce/c11a38ccc9d4fdb0c092cf583919ecff62724d39ad3556b5324173dad1ee/jiter-0.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:050dd505e407c028aa76b9b4a379a06f79f484c3b256d99a7fea118d863c9a79", size = 223145, upload-time = "2026-11-06T15:02:03.485Z" }, + { url = "https://files.pythonhosted.org/packages/4e/54/d63c0ed96e4d2b6f4a64ada70a8eaedbd8849416045821d70c27d26c2dcb/jiter-0.19.0-cp313-cp313-win_arm64.whl", hash = "sha256:96b8d6795c8e8121d0a99b2c8f60ca4db02ff8063205bf2c9712e2714a52ea9f", size = 181228, upload-time = "2026-11-06T15:02:04.664Z" }, + { url = "https://files.pythonhosted.org/packages/21/7c/4e294eece8901d8b56cb7c7d8504ad4da16a402867d89af1505034a0df93/jiter-0.19.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:cfcf2473b941e8695b496ce9dc155f772ad97d98be5071ebf1e4e4bbfe00f632", size = 291741, upload-time = "2026-11-06T15:02:05.931Z" }, + { url = "https://files.pythonhosted.org/packages/70/d2/c81dfd6c0c60891341ac7ae7e8d0a4c7eea5d9693a3ad205e053f6c50d4a/jiter-0.19.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e8dc4fb0fe3a5e36a373933340f4353da816c6de684a57908633504df3c2c90b", size = 316068, upload-time = "2026-11-06T15:02:07.193Z" }, + { url = "https://files.pythonhosted.org/packages/8f/51/5960eb532971f27ac0962f8a05c438224d1f9cd548772df14c4064a0c7fa/jiter-0.19.0-cp314-cp314-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:d2358442c7c45e1761b546b458a3c5bcd7d571838e2340a397464475f688494d", size = 334264, upload-time = "2026-11-06T15:02:08.514Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7f/3b131a083dd46d495120ee92318f2dd07870eae853df3ab8c0a8f977f1a3/jiter-0.19.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:95b75f42a112e39ddd0b8683b4ccf6319a182460afdc4bb6828423eeeef97a6f", size = 338039, upload-time = "2026-11-06T15:02:09.819Z" }, + { url = "https://files.pythonhosted.org/packages/d9/2c/62c8ae6b16f3e369ed2d95690ba747b8319d0a96693d1d57ec2d306bf256/jiter-0.19.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:d5cdf03154ba3867707f48454e3f9235df7a0f772cf4c9f57e4004f7c67d9821", size = 324016, upload-time = "2026-11-06T15:02:11.195Z" }, + { url = "https://files.pythonhosted.org/packages/f7/e3/516e16d89f93b657bc62fbc010cf96cff967c58b23874da8ea494d420b08/jiter-0.19.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:dac2525467cca169c3f8049e04e69334eecbd38caa1b0b10a11d9cf07c7d5871", size = 335866, upload-time = "2026-11-06T15:02:12.701Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3d/0899ef2a0cc7a31935a450a0cadd9708754cd1e3498268d3bce44760f9a3/jiter-0.19.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:71361b3484086f538674ad37af5b4d5e86c04c39ca79b026cbd41cb63476dc72", size = 350517, upload-time = "2026-11-06T15:02:14.103Z" }, + { url = "https://files.pythonhosted.org/packages/d0/6a/8ec186d2307054a54a7656cb0fe1a186970f7b8740c08701874e3dffcf82/jiter-0.19.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:def1f12b5cef4f53c30ec89d1644c6c5500b343f1ec5e9ac8010dad8c0a41d7b", size = 338365, upload-time = "2026-11-06T15:02:15.515Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4d/1f0c4a9d0e529025e5292416f52b0a0efff7f9b9efac56f345aea91ed0e7/jiter-0.19.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:62d0ab327933a8cc5cba2ff2a1ff83400eb10bee1694eb1cc0c4c83296855d56", size = 316480, upload-time = "2026-11-06T15:02:16.918Z" }, + { url = "https://files.pythonhosted.org/packages/95/c8/72f6f2a6ce44d5aca96440498d5144abd2a509c58e0f53e4a039bdebbf7a/jiter-0.19.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:9b9e55e0182f9ff9da2e34ee92492f30ab99bd276cad496fa56feb7a64148dd8", size = 482150, upload-time = "2026-11-06T15:02:18.285Z" }, + { url = "https://files.pythonhosted.org/packages/2a/53/f78cf27995858d32f212ff825addb32eabd70c896a530e624f36d20ac1f0/jiter-0.19.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:1433a914536ac5da69f91cc7743ac2f64d3705db2de52f0715ecd00fed2c95fa", size = 516946, upload-time = "2026-11-06T15:02:19.661Z" }, + { url = "https://files.pythonhosted.org/packages/ce/aa/14e436a6a0fa643a66867f32e68e9683e5ccfaa8ff6df13781611dc62783/jiter-0.19.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:707b8194222a343c4a5e1574df95f5ade03e3f1cbdba77d8c89c6af0f40d92cd", size = 111991, upload-time = "2026-11-06T15:02:21.062Z" }, + { url = "https://files.pythonhosted.org/packages/c7/e3/d80e254dd1a85d585943046b9ba652cf01b3338beabb2035a7f5df82a906/jiter-0.19.0-cp314-cp314-win32.whl", hash = "sha256:512b07721e67a3036db5966173885bebbffd1b0b1583b56f08bd52723865b382", size = 186542, upload-time = "2026-11-06T15:02:22.323Z" }, + { url = "https://files.pythonhosted.org/packages/62/02/0a387aedb729da03acb860ba38330a2abb94893809b47d3ce5088f3edc43/jiter-0.19.0-cp314-cp314-win_amd64.whl", hash = "sha256:76cb5b95b07f853911a39835de285da6954f44634890c26dd6ecdfc465fc977c", size = 225421, upload-time = "2026-11-06T15:02:23.586Z" }, + { url = "https://files.pythonhosted.org/packages/8e/cf/ce7abfc44da37c7807d244a400309c69d211a4704b2b37b2a3d022ea0897/jiter-0.19.0-cp314-cp314-win_arm64.whl", hash = "sha256:12d90242083e7ce352d12dc8b04daee4b6c29314385bc84ecd2b809d0607fbc3", size = 182873, upload-time = "2026-11-06T15:02:24.874Z" }, + { url = "https://files.pythonhosted.org/packages/16/b0/5f5bfb4fa82b85f6027eb9e46e4001a0d2ba3f92370c3cff565d87706419/jiter-0.19.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d0b16e19a7ea17ab0fc6089ded12040d46afbd9681c7978012044dc7ff0073aa", size = 317990, upload-time = "2026-11-06T15:02:26.154Z" }, + { url = "https://files.pythonhosted.org/packages/79/fc/99ec7c673c2b5eff83e52bc1fd23265c4fac8a5236b19d21ddde05cbffc9/jiter-0.19.0-cp314-cp314t-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:6f476b368da302a9cf6dece5af4979dac8b71a8986d69e47ce5a71f2cdea08ec", size = 334774, upload-time = "2026-11-06T15:02:27.53Z" }, + { url = "https://files.pythonhosted.org/packages/cd/6e/95ccd4c6770eeb9946733bea3eb19882cf170c02a5cabe23c3dc675ddb29/jiter-0.19.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c094e472d6e54bfed6c78ef8595c27cf46b76d287f5cebb5264569f9b02b3737", size = 336708, upload-time = "2026-11-06T15:02:28.897Z" }, + { url = "https://files.pythonhosted.org/packages/38/d8/8abec81697c8d6d6c0c55712ef6a57233e30913e849a4248fdfa859d1fdd/jiter-0.19.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:825a0b9c30ff5b12698cec9d034b568a1f26b2bc888acd1bd009d0efa1de081d", size = 322945, upload-time = "2026-11-06T15:02:30.174Z" }, + { url = "https://files.pythonhosted.org/packages/42/0a/159a0276519b1b30335e3f10b9518c696dd7a982208eb78d22ec7b595866/jiter-0.19.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e8f7d3072bd68b6ccc264bfe6600b0f730997bb414c7a274c317804f8f90c871", size = 335349, upload-time = "2026-11-06T15:02:32.366Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f4/1dbc8a61d109985a65262972a9ce5bb59249585542d3ffd2d0cf7d6f1152/jiter-0.19.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:ab7a041b4c4a0afb160470839bbcafa3f5fea5572e0d1d46bd5239beeb30b19a", size = 350611, upload-time = "2026-11-06T15:02:34.094Z" }, + { url = "https://files.pythonhosted.org/packages/d6/59/0a6944592a79a78104cec927cb5ce68400c152299a9129fea63e1dc296ae/jiter-0.19.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f7cf95dcab93e61fb58f395eedd4924557c8b08cc044933a4b7918c3d20751d7", size = 338402, upload-time = "2026-11-06T15:02:35.352Z" }, + { url = "https://files.pythonhosted.org/packages/3c/eb/1ff411f106d18026527a12af8e4f2b0a4c589d64bec71d4f6eae3344fdd5/jiter-0.19.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a01d50640fb033259858f9203292aaf4dc9eb903d5646c529ba5be655a982a9b", size = 316165, upload-time = "2026-11-06T15:02:37.14Z" }, + { url = "https://files.pythonhosted.org/packages/e4/73/17b8c34e48dc33c30a5db75550769cf930cc62de37d46b8c540f613081a2/jiter-0.19.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:4ddc2617d3725c37501737f9725694fac10f2fb3f274adbfabc521bea7200d9a", size = 481091, upload-time = "2026-11-06T15:02:39.009Z" }, + { url = "https://files.pythonhosted.org/packages/1d/1a/2ecaab02af01e885a216a0dc0930e060325b81f5580c80cd69cfdf1d9827/jiter-0.19.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:c528273371289b506762bfda85094b55e298082b7cc2259f0d01ee30d61b771c", size = 515502, upload-time = "2026-11-06T15:02:40.505Z" }, + { url = "https://files.pythonhosted.org/packages/ed/87/9c3917a60c381132af4d06eab58210a171b74a18060a27590f338dbf21b3/jiter-0.19.0-cp314-cp314t-win32.whl", hash = "sha256:521438d81c8c42293e09297441de331731f576ba647e7dd6c05698d873c17c34", size = 185092, upload-time = "2026-11-06T15:02:41.81Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f5/0aea271de1d0194634c235d5d864006658282fad462beee2e5268ea57dd5/jiter-0.19.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f2531f55026fca2c448148c7d9371117f801e0df7a084af62133b3d775564ebd", size = 227614, upload-time = "2026-11-06T15:02:43.234Z" }, + { url = "https://files.pythonhosted.org/packages/91/da/69a37a4c11a9d5a2f70e9704081474489a05a379e5baece7454fad5a7481/jiter-0.19.0-cp314-cp314t-win_arm64.whl", hash = "sha256:8167c2cd6fd39b2b9428061cbb03889ba17a45df2285307750eb720b9befee03", size = 183029, upload-time = "2026-11-06T15:02:44.597Z" }, + { url = "https://files.pythonhosted.org/packages/74/b7/ec514207195a7dbe7437239f9b55f36de1cd577716eddcc3cc69b5e8064c/jiter-0.19.0-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:1ed2bfe0518f584fb0dc22b36412d7c6d19529b5682de79e67b44666034938bd", size = 291909, upload-time = "2026-11-06T15:02:46.018Z" }, + { url = "https://files.pythonhosted.org/packages/68/66/09e6ba7930810191b798549c4cccbab65e5825bd27aa44b63caf8e303e67/jiter-0.19.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:baab631aa2a8bd139db09e56bd2f6358aebb5d7dddcac0713d07ac7fb0861ebb", size = 318969, upload-time = "2026-11-06T15:02:47.354Z" }, + { url = "https://files.pythonhosted.org/packages/ee/5c/7b4171cd22912b9c2bbc561e0d065439b4d65724d2c565aaeb0798c12db7/jiter-0.19.0-cp315-cp315-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:13c916eed31210fcf3b5d070709adaa6c2a39fdb0ad003962663460dd5cebcaa", size = 334730, upload-time = "2026-11-06T15:02:49.221Z" }, + { url = "https://files.pythonhosted.org/packages/e0/af/4119de31e174a529f20b9b51cf73f90cdbc89d3c6e9dc138fa3c0dbf8efe/jiter-0.19.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:63e17a237323b240cbb9be6c4157fcdb2b4edaf8aa267cefc52157ff6b27514e", size = 338040, upload-time = "2026-11-06T15:02:50.471Z" }, + { url = "https://files.pythonhosted.org/packages/3c/6e/d5b972d66811a6be98bc85087416a4cc7f38276903647fed708390862460/jiter-0.19.0-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:86946bc97afcc82c79b12ca8acc87223acc2fee9b25ed3455e6abb0f3c40f722", size = 324217, upload-time = "2026-11-06T15:02:51.723Z" }, + { url = "https://files.pythonhosted.org/packages/15/04/2ad357983c345a45474d029dee9f4b6f9f88a603e79692a4f5348b05568a/jiter-0.19.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f614930e5bfcaed1b18fc46c197b2df5f4172180ca07d7a2690b542cb21103a2", size = 335734, upload-time = "2026-11-06T15:02:53.221Z" }, + { url = "https://files.pythonhosted.org/packages/d0/b1/87f22901550c631b39de77f9376fc218d6e6c2055fe673904061f93cf812/jiter-0.19.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5315406c0c95b18e07359113fcd96d219fafac94227be2b4e77d0e30ef6225ce", size = 350498, upload-time = "2026-11-06T15:02:54.576Z" }, + { url = "https://files.pythonhosted.org/packages/67/51/68340023ea8bbaf3abe4d37568d53c6a7f07bf498e2e9b0573d67614a493/jiter-0.19.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:daa5bd8317d7b9a56d95af3b0b054f0d06c94a8cb68e332943d4532ef8611eef", size = 338750, upload-time = "2026-11-06T15:02:55.916Z" }, + { url = "https://files.pythonhosted.org/packages/34/54/2e2e218e9b931cb7392e43902a05517d9ddbef687b3a081aa1669a65f0ae/jiter-0.19.0-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:977b59124adddc535a794ee75eb27f223c4dd677822a3fd74e0d17be6a52c39b", size = 316619, upload-time = "2026-11-06T15:02:57.249Z" }, + { url = "https://files.pythonhosted.org/packages/d1/3c/fdf1690bde9ca32e53424f92a0728576399bee18490b221603e5590fbb5c/jiter-0.19.0-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:be19151fceb1a7fcdbe7b187cadf914dc54e6f0fcfe88df7a0616ee0baa71a2d", size = 482123, upload-time = "2026-11-06T15:02:58.559Z" }, + { url = "https://files.pythonhosted.org/packages/e9/f0/3e9c6d18b9618a4d8cfdd5ffe320004cd78459eb70c20bfd4de0b0827580/jiter-0.19.0-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:fa03bfa89a25915283cb748153db3da0fbd8fb298ed55abfba32bf3bf23d2b68", size = 516970, upload-time = "2026-11-06T15:03:00.011Z" }, + { url = "https://files.pythonhosted.org/packages/f2/d6/93533d76fc10e7e06e97abf2bb6aa5bd83b9ec513e911a42cda4c7c6839a/jiter-0.19.0-cp315-cp315-win32.whl", hash = "sha256:11f14da1ec8f4bc673a4fa475e84c5c7261b0239e18e0db03af55294919b5be8", size = 186648, upload-time = "2026-11-06T15:03:01.346Z" }, + { url = "https://files.pythonhosted.org/packages/bf/6d/9b480cd3bb00423c8251e322e147c9659fe8120fc345dbc0e0e8b128872a/jiter-0.19.0-cp315-cp315-win_amd64.whl", hash = "sha256:7981d17671689181d8b6806fac508ba6a794381f9486e62c598ead75ac857704", size = 227504, upload-time = "2026-11-06T15:03:02.762Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ee/6e552e437dca26dc447fd1d44141b2d37d1b00530bfb3c0b92fa61ca7c53/jiter-0.19.0-cp315-cp315-win_arm64.whl", hash = "sha256:50522b7e6f3d86f038b6d3be748fcd352c1611235d54442e70458b26de324df2", size = 183098, upload-time = "2026-11-06T15:03:03.971Z" }, + { url = "https://files.pythonhosted.org/packages/69/a4/2b95137709553f7820e3011389d6b5cac6a66fceec2484283d0b8e0be445/jiter-0.19.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:c3c3099dff0565e08ad503f2f9cf1ab8ec4a5b474042264c72517afc652e3285", size = 316134, upload-time = "2026-11-06T15:03:05.247Z" }, + { url = "https://files.pythonhosted.org/packages/ea/a9/05205b79e08d710bcc62de5472e7b208365cc047361d917a829421486da2/jiter-0.19.0-cp315-cp315t-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:2b26727103c0121ed2ec2e78ea23d669db71dad09128d274f1ba0aa4c07f96c5", size = 335277, upload-time = "2026-11-06T15:03:07.09Z" }, + { url = "https://files.pythonhosted.org/packages/06/fe/2be55dea9b0d14d44245200ed306302c8bc0e5a73977749ae5925ffb7c47/jiter-0.19.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80d85202190ba13c3958bb01134bde8d81300a6530e2d5cfe73b63ae9a55f408", size = 336622, upload-time = "2026-11-06T15:03:08.366Z" }, + { url = "https://files.pythonhosted.org/packages/32/eb/9383e49faaf67b19f8ddb1b08963668a1c1cbd236ea25f22de19e3aa4908/jiter-0.19.0-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:cb7458f07137ed895d01344d321205f050fb15fa8597a29e5d9bdaac140d4678", size = 323119, upload-time = "2026-11-06T15:03:09.847Z" }, + { url = "https://files.pythonhosted.org/packages/cd/9f/75816f1f9cc27e5212991a27c99d14f9995d92e6cda2562ab527a3400f7f/jiter-0.19.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7a01b76a206200d8a55111ac3d8fdc131dfdbefeaee0efca72597854aa71d6ea", size = 335134, upload-time = "2026-11-06T15:03:11.786Z" }, + { url = "https://files.pythonhosted.org/packages/d8/3b/9fcb38a871223a8759d46514594b2d146d4fd50890110e3b0e47d9b7775a/jiter-0.19.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a628bd3603bb4a9f73d6bf659fbf8565e30902b4d7ba915e125d00aa2c3cacf7", size = 350588, upload-time = "2026-11-06T15:03:13.177Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e0/297733d77491186cad6b239ed87231bfd670d4224f6348217656e547ca47/jiter-0.19.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ca0bb11753d4e24d07f579ec4b62413bb43f88d8736216b57996f4011906b20c", size = 338023, upload-time = "2026-11-06T15:03:14.546Z" }, + { url = "https://files.pythonhosted.org/packages/c7/33/bfe34cf6f72ee13090169390e829b8339d769264fe3e92f2b8f211915782/jiter-0.19.0-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:f19ebbeeae0eade71b798efc2afacdfb07402e0fcc7c0943529759f31476985e", size = 315871, upload-time = "2026-11-06T15:03:15.857Z" }, + { url = "https://files.pythonhosted.org/packages/3d/12/2c095a7929daa0421e0ce631b12ce7374b41ac19a9a9141e79411d483868/jiter-0.19.0-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:49610246ac2dd94392659d94a53c8d71f2b2c92dc22f2b15a4d1b0d774f74c52", size = 480771, upload-time = "2026-11-06T15:03:17.113Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d7/0df9bcc5a35180d05f3b1af2813434af1eeaa9ee4dd2f54cd79cddb7ef0f/jiter-0.19.0-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:1c77841a8e98c7c765fcc804f9c1c4f66905340cab7a81c3ebdca5c4d68bfe4a", size = 515629, upload-time = "2026-11-06T15:03:18.408Z" }, + { url = "https://files.pythonhosted.org/packages/cf/98/8c2a5ab47b4a8b4a9d9b6c12354a3a1497c20b779c44d38732a02e14e1c7/jiter-0.19.0-cp315-cp315t-win32.whl", hash = "sha256:3694d60669e7d8277f6366170a6944f8b9779d85417b3ee6dda3897300528141", size = 185198, upload-time = "2026-11-06T15:03:19.882Z" }, + { url = "https://files.pythonhosted.org/packages/ea/6f/736254f4a132622ee3481d90cf7a521b3b3d0f8c6e3794a87bb10b88388b/jiter-0.19.0-cp315-cp315t-win_amd64.whl", hash = "sha256:093add0b9c12cfa04920cb9fcdc9845ff580fb374ca6a645f6d74f471a5d25f7", size = 224870, upload-time = "2026-11-06T15:03:21.225Z" }, + { url = "https://files.pythonhosted.org/packages/49/cc/9bf0f4ea335a7b9468f7e72c2cc5d19a77f7d9ddf77f7fd9af69f82f489e/jiter-0.19.0-cp315-cp315t-win_arm64.whl", hash = "sha256:9f5ac1f8d1b0eda36d98ab5266c98deddf4d8dc5ff7e031c51f9e9f632abee2f", size = 183185, upload-time = "2026-11-06T15:03:22.475Z" }, + { url = "https://files.pythonhosted.org/packages/4c/67/66164d4be8991d7f3b7163be80c7c5e94040cc7d9ba62b4e4e4113da5f79/jiter-0.19.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e7682cfd9561bcdc47dbd10d2546b5b37640b4bef407f76fb434c30e4cb0ae81", size = 287293, upload-time = "2026-11-06T15:03:23.746Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8f/94f8b4d9be8cbf8e0be61c21b368ba367d8cb57d8f5d0ef71bcc8f61ef3d/jiter-0.19.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:bee08505baeb38c891d884e977b20924ec13bbedfc3ff1d32cfd66a867f8e9b1", size = 277813, upload-time = "2026-11-06T15:03:25.476Z" }, + { url = "https://files.pythonhosted.org/packages/b3/5e/1ba251fae0c9d689cc40b4dbedc9a6674db0d5ef5dae2af0a7a4ccaa8886/jiter-0.19.0-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:dd34bb5493184ca9816fc4ffb19ea7377940d5171512cfc107356a3a145a4ba2", size = 300193, upload-time = "2026-11-06T15:03:26.882Z" }, + { url = "https://files.pythonhosted.org/packages/7b/00/03f9750ce7fc6882c00e030f5843afaec7ab06813ebe2f0d363acaadb973/jiter-0.19.0-graalpy312-graalpy250_312_native-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:885673d25907536b3bce8d61accd67a534eb3ad5fa2dc7ebfa67c4842b3b0060", size = 301132, upload-time = "2026-11-06T15:03:28.217Z" }, + { url = "https://files.pythonhosted.org/packages/c1/43/f7adc61beb4d2f9990ad802856324cfa5968f6b54c9dff91e16acc866b0b/jiter-0.19.0-graalpy313-graalpy253_313_native-macosx_11_0_arm64.whl", hash = "sha256:50f9e5bde6bef0eef6a12315088e635b19d50cbceec745615eb8614f6bf4b52c", size = 277105, upload-time = "2026-11-06T15:03:29.514Z" }, + { url = "https://files.pythonhosted.org/packages/5d/56/9d34c6a580f010259c136a1354d0e51cbe1293aef4def5f6c42347c73727/jiter-0.19.0-graalpy313-graalpy253_313_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42efe1a711c0745b6098b8bff931b0a2e7fff36098cbc2dc16b76497f1cbe834", size = 300134, upload-time = "2026-11-06T15:03:30.774Z" }, + { url = "https://files.pythonhosted.org/packages/e7/b7/dd4360ad780ea1455ff2607b43cd9fcd0fd785ae366c8a632785010e92ff/jiter-0.19.0-graalpy313-graalpy253_313_native-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2c82005ee84b348cee72a8df6672b5d6c9fe9db4b81c98b84a28155d447d2348", size = 300943, upload-time = "2026-11-06T15:03:32.042Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "librt" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/f5/9dc696772d241814bacac7880bac32f2930b5a6ebc1f85317b83161a011c/librt-0.16.0.tar.gz", hash = "sha256:ac38d6d8d66bf3d744148dbbc0b8e193e195a51e364ed55e224631f5721891fc", size = 219839, upload-time = "2026-09-29T00:55:32.891Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/77/900294b3c9a81415323d81b3f3f271a7f5d7ed9d01e824fc38ffa49470b6/librt-0.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:96f576f2711f8519152ec76d0e599243555c1f07679fa73606ca8c8c868c0be6", size = 148668, upload-time = "2026-09-29T00:44:20.282Z" }, + { url = "https://files.pythonhosted.org/packages/55/3b/c301e8cc47758e4518b93e926a0111080155cc79fa03651d202ac2025065/librt-0.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bec3818c7da7c96ceae0ef5915a3d16c52dd08f3ea913bf1fe8568c447c7978", size = 153562, upload-time = "2026-09-29T00:44:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/62/b1/4d5b98114a58bf17e3832cd33400e40f2582f68c2fb7ce25b4468cb1afd3/librt-0.16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64c79520414a3fdfc6aabd7593e6169afa14d5f8d9908d4b498db068868b08dd", size = 494337, upload-time = "2026-09-29T00:44:23.096Z" }, + { url = "https://files.pythonhosted.org/packages/88/36/14223c9b0dc38982eb4f399732c8cb27b19c7e027ae0faef5ca51d08d19f/librt-0.16.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f81b5b19ce748ef68d4746656b7929762eb2fe99269b266e4be07e2ee4de7144", size = 485440, upload-time = "2026-09-29T00:44:24.555Z" }, + { url = "https://files.pythonhosted.org/packages/ec/9e/d7155e6800d0c33e297e529b217649f7361d706491ebf9d2fc0fc627309e/librt-0.16.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c71d1b76210a36729fedfc5115069b50a3d8619054745f8758fe5d6f19e86671", size = 498363, upload-time = "2026-09-29T00:44:26.03Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ce/d5f1fb9f913ac0979d6fc5db9fb9c0bf28cc3470988528957922f15ed40c/librt-0.16.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:473eebc7866bb0a0c8849a292b5e7157c1aba5d14d0f0f610c52158d6d964262", size = 515453, upload-time = "2026-09-29T00:44:27.566Z" }, + { url = "https://files.pythonhosted.org/packages/b4/d4/ca0fca1a27c26080d1a25b2b31f271e532078a5719878c3daca0c3d4c539/librt-0.16.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4eb1313a19847089ee81e88742abedf285c60538816640b99742d8534b81d26a", size = 509537, upload-time = "2026-09-29T00:44:29.14Z" }, + { url = "https://files.pythonhosted.org/packages/ee/c0/26807bf2221a53a2c66f2e23a5c4d57f1c0eaa4afed7cb81d841cf3d1903/librt-0.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ebefd60b42e2a82b32d136bb5f7c94eadfcd29f772b547df6f3291d1ed855a1c", size = 532541, upload-time = "2026-09-29T00:44:30.745Z" }, + { url = "https://files.pythonhosted.org/packages/0e/50/c329d509907e1bc061abeec3796f94d255d43ba25d28c3854d2138ed5ab2/librt-0.16.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6c5da27e8056439f927ea896735da60e616c477a8293feaa3233d4e7781a6726", size = 537059, upload-time = "2026-09-29T00:44:32.102Z" }, + { url = "https://files.pythonhosted.org/packages/84/d6/4571b9c9e65d98e5503734166dce2fd3e216f526dc3c59a3773faa8a60e3/librt-0.16.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:67e718c7a43f8db325abbbf1404e2d535f12f8f7a1a82259568385cc5274b82a", size = 536614, upload-time = "2026-09-29T00:44:33.653Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d3/a2ed8a292e4ab7889792a9c3f4ad5d1caf6557afce7f6b452e77f8ed20b7/librt-0.16.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:314e703f0c19320dc8094e7a784b9cf29e1b67402515abf580069a6363c0b4f1", size = 517199, upload-time = "2026-09-29T00:44:35.115Z" }, + { url = "https://files.pythonhosted.org/packages/9e/24/276e5218b4b7adb6258a42b7403cb1b7cef25616238d0c2152288a9d2f53/librt-0.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:943c6bbecbdf7fa575a4f2952fcfd848c88ef95507c3fca411e89d4ac3ff8143", size = 558746, upload-time = "2026-09-29T00:44:36.656Z" }, + { url = "https://files.pythonhosted.org/packages/7c/5c/9156df81a1726e1efc9567379def2b8777a6d56f464d2f1b760bf951b8af/librt-0.16.0-cp310-cp310-win32.whl", hash = "sha256:7cc365f006891afb006b52d5ee5ee74c09306ffa20e2f8705a32a4450af2f3ba", size = 104424, upload-time = "2026-09-29T00:44:38.123Z" }, + { url = "https://files.pythonhosted.org/packages/0b/62/4fd172cb3f49ab2cf5ef17de519a93cfb935202c1270ba9d44c6a64178c3/librt-0.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:0314058469f4d2fd279ce7c62ac274ac82c3918ef7db62ef0697c4c359370155", size = 124267, upload-time = "2026-09-29T00:44:39.455Z" }, + { url = "https://files.pythonhosted.org/packages/ba/0e/b04fd2d76b6a78a02286938251b2c119fb36ddece92c76cb4886ef7d44b8/librt-0.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fe4372c52d4849096c6cc1cda2817d293ec51440c890474ed59ef38d46556f18", size = 148058, upload-time = "2026-09-29T00:44:40.903Z" }, + { url = "https://files.pythonhosted.org/packages/a3/fc/e1ed24aadb32d38603c0c07f579d739eb4d90e64e90d525b024e4a999b4d/librt-0.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c72c5295a84bd249526da9bdca38f2e176d15c31c13bb0063c5053f4ca023421", size = 153073, upload-time = "2026-09-29T00:44:42.165Z" }, + { url = "https://files.pythonhosted.org/packages/be/38/133ab655409420e0c34a9a9836a7f4e5ae75fba758d7fe177099130d58ba/librt-0.16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be56ba9c884143495b517f23fe794ae367d58cd89ea0fdd6d437e3c024a87f9f", size = 493052, upload-time = "2026-09-29T00:44:44.043Z" }, + { url = "https://files.pythonhosted.org/packages/94/42/aaa663bc9421f5bf7bbbe9d3316d7219749698ec7688ac3b519770a89c04/librt-0.16.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:ef46c1a29ffb8c72e882e22618ec618778eacd0578fb22c6e7cf9c11d15f357b", size = 485506, upload-time = "2026-09-29T00:44:45.479Z" }, + { url = "https://files.pythonhosted.org/packages/7b/59/83a6eb5087ba8b45b083859e2085256963e06d4c2531944cdd962670bfb5/librt-0.16.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d3c94211ee0c4f8d649ec06b7c115c0ec4eadb873a0e3154ca15cef3f814b071", size = 498708, upload-time = "2026-09-29T00:44:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e4/15a8d62b2bc12935080ec7277e2e0b9b233047781eb784a51d3e25af669a/librt-0.16.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94aed6a8308818b91677957d1bd03188869cd7aeb23c5dba7912a6c0402f7602", size = 515981, upload-time = "2026-09-29T00:44:48.316Z" }, + { url = "https://files.pythonhosted.org/packages/46/4f/287cc282ac5dd815204c0779e79401bb0a568c3a1335fb319bf0838fb566/librt-0.16.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:349c0bcb87ebd07481b6ff781e25cdc699723dbe2212e57dabb27f7a13b7b87d", size = 508670, upload-time = "2026-09-29T00:44:49.726Z" }, + { url = "https://files.pythonhosted.org/packages/bf/5b/ab7292add898ade0d2b5b75d32a923aae315861671aa5c39f5491309f7ae/librt-0.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:001bfd59a7d45b17e3e75f2a8c6405280b35e7b84471792778e718c4f368950e", size = 530393, upload-time = "2026-09-29T00:44:51.356Z" }, + { url = "https://files.pythonhosted.org/packages/7a/9e/8e6bcf027d4bcbc16a4e864b3d158a1d3e5f55ffa53af0238020cad89271/librt-0.16.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:28e038895b998d7a0c7798922ce8a1dc157675df5cf1c9ef0aca809ed804b7a1", size = 534302, upload-time = "2026-09-29T00:44:52.893Z" }, + { url = "https://files.pythonhosted.org/packages/96/a4/8c5ab4f3b3a8e905ff8305d76976a2e666f52fe049edd398aed2402beb36/librt-0.16.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0dbe4096a7ecc00fa835d24510ad8545a4efef738dac96e0e63516783ccde905", size = 536150, upload-time = "2026-09-29T00:44:54.421Z" }, + { url = "https://files.pythonhosted.org/packages/ea/5e/7c4bc267e3cd4b8bd288de9f62d61fff203829c9cc649ea2aa12e9218cf0/librt-0.16.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5cd5b092441053364af968ea12084692cb9d4a22f3ce9524e377880bf028761e", size = 514316, upload-time = "2026-09-29T00:44:55.998Z" }, + { url = "https://files.pythonhosted.org/packages/e0/75/c8bc6b50f43c73819334233f5a3259c81216742cef56d2140c3dd377c3f0/librt-0.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ddeb3c9dedb461bb457c6c7d9aa7fbf35329da313d1a7543d00c8d0f3473c96", size = 557731, upload-time = "2026-09-29T00:44:57.581Z" }, + { url = "https://files.pythonhosted.org/packages/93/fb/e241f91bc8de6c7ac1c6a20ea6dd381bc52312b44d711ffcd97140db1e57/librt-0.16.0-cp311-cp311-win32.whl", hash = "sha256:e05108e0849966f53a8d2d3112a7af881d0efaa479bc735bba91108f9f2350a7", size = 104922, upload-time = "2026-09-29T00:44:59.078Z" }, + { url = "https://files.pythonhosted.org/packages/d1/38/c892383d2bb1a6a80b2ec8ede84970a265a792c56614a06282bf3008517c/librt-0.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:5f49cff01bd608ef7d97104cb035c75455e79c2d70bf4a506cf773338ac1860d", size = 125093, upload-time = "2026-09-29T00:49:01.621Z" }, + { url = "https://files.pythonhosted.org/packages/e4/56/8bbde1ed0b21480163617648f638fdaec9374cf5e9c846ebcee34ec02a42/librt-0.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:1d28ae980ae2218f9c5b95d191e947296f918c9bf0b400d467a9430275bbe678", size = 115910, upload-time = "2026-09-29T00:52:40.131Z" }, + { url = "https://files.pythonhosted.org/packages/ad/76/bbdaeb87b7c47b5c7343e90222b9bfa4e4a8a83f647e02933ad0129225b1/librt-0.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fe52bf4641069e7978a14253b036cb9002def1926317e710f2e249f8a8c47742", size = 151028, upload-time = "2026-09-29T00:52:41.508Z" }, + { url = "https://files.pythonhosted.org/packages/fd/0c/ab8ed3dab0085931aec4a792c7eaac8dc6c5ff4691fda3a5360d9d8a9cd2/librt-0.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5bcc2c4726ced915b00de0c9856a4eeabfb3fddb93e10e0b8f735b7709358b6d", size = 155283, upload-time = "2026-09-29T00:52:42.873Z" }, + { url = "https://files.pythonhosted.org/packages/eb/36/494e79d460c80c1f030661e8287c9eca5e1ad652dc2b2180b6cd42abce0a/librt-0.16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff7baa55f8e7c69851419e50a666015d02a74198716fd45c0125a2112e0a389f", size = 503185, upload-time = "2026-09-29T00:52:44.638Z" }, + { url = "https://files.pythonhosted.org/packages/9b/34/a8464038dd9db6e4381fa2b6eb73dc9a50888d77102c4c139304ac35cddc/librt-0.16.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:b95d5d92ab83d39e760a52091bb1baba664f3a2351e39b1e16801e5747c2f0e9", size = 496738, upload-time = "2026-09-29T00:52:46.441Z" }, + { url = "https://files.pythonhosted.org/packages/6a/53/e0e5e334ef0c6ed27039d323819368b9ef6712be87d55ee2bf9799398afd/librt-0.16.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b6d085d70bce51d43c5c7c36d63490770180d8779e71c49305c87b4213918de7", size = 513818, upload-time = "2026-09-29T00:52:48.068Z" }, + { url = "https://files.pythonhosted.org/packages/ad/f7/7ce72cbf19d0addd05090b339152fd0548a02562c2866a603e6e3b3da2df/librt-0.16.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36e53948e99bbe3ffea257124cfcae1cfb01831555c9a9c903c9f9a72db7fd07", size = 531952, upload-time = "2026-09-29T00:52:49.816Z" }, + { url = "https://files.pythonhosted.org/packages/83/22/0b1bcb6a8e723c8b4fd60dfc8ae8ec6461c54073fbc8685efeb8d900d407/librt-0.16.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:54d11f726aae9df5a6ffbbf0a03a52449bbac84a53ef03669cb41cdfd4ae41bf", size = 524465, upload-time = "2026-09-29T00:52:51.354Z" }, + { url = "https://files.pythonhosted.org/packages/64/2e/e9c23b8b9df1813da1be205deca9606beb7ddd033972246cd426d05374a0/librt-0.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4323193ac0cd025f85af531df8ba91bf24d1973b401697347a6282e8fd3fcf5e", size = 543191, upload-time = "2026-09-29T00:52:53.284Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e5/6a8b21b342c03ed7e230fa3afbfd2edc58e6e87ef1f0d11fa2b9a748c252/librt-0.16.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e42f8e098b9c5396fefa05fb1cc7e33b0e08fc51da106b5de4a45fd22aac6743", size = 546979, upload-time = "2026-09-29T00:52:54.932Z" }, + { url = "https://files.pythonhosted.org/packages/84/9e/b5129023eced1be01e01c22757f53be551d463b1bb7264f787927404c1d6/librt-0.16.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:39ec1d5a14e37baf1450a6cabf03fe552340808bf1ad9d71824ab90117716459", size = 555317, upload-time = "2026-09-29T00:52:56.869Z" }, + { url = "https://files.pythonhosted.org/packages/71/89/28bba5938c725fe91f06bf93f7fa6c6b150229df53a87b454b0d5c2a796e/librt-0.16.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d1aabe3925cbb4a08d15b7b20ba4011b53019da0c4173a25155139b7b1baed65", size = 535846, upload-time = "2026-09-29T00:52:58.475Z" }, + { url = "https://files.pythonhosted.org/packages/22/92/63773026614f888c5d4e370e395ce42ca604b89f70b3acdfedbf94851b80/librt-0.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:300c3ffdc459f4a779a8411ecb188e3ac0b1ff3a3a7b099642555dedae06c69b", size = 573700, upload-time = "2026-09-29T00:53:00.127Z" }, + { url = "https://files.pythonhosted.org/packages/66/8f/347d4677eefb57cd9f8e01d95a1dcee9a91b4e44e664173c32ff6f3752e5/librt-0.16.0-cp312-cp312-win32.whl", hash = "sha256:c17194318e4c0c0348b36f36c2ec7534436fe0a4c15582403162a4f08c80797a", size = 106158, upload-time = "2026-09-29T00:53:02.031Z" }, + { url = "https://files.pythonhosted.org/packages/f0/2c/5193dc81127cd5ddfad031391b046bf32dda219b38463ab872407ca30646/librt-0.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:25a58a19ea8d83b68209f04912df765e9260635ef77646542ed4b4abe6bc7940", size = 126176, upload-time = "2026-09-29T00:53:03.445Z" }, + { url = "https://files.pythonhosted.org/packages/ff/3d/9668a400c8dd81d162eba38b33fa49fa6205f1a64493570a13fbb815c3ee/librt-0.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7be7cf555bc30ec12622e9447299cc4a9b8ff307548b634794353db0c2065dc", size = 116572, upload-time = "2026-09-29T00:53:04.815Z" }, + { url = "https://files.pythonhosted.org/packages/46/cd/ae5e0e9dba45d1399aa04a5395bcc0bead40d9fa06dc903634a7b4d7473d/librt-0.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c5e6144e68b577f157519f2ba88ca20e3ed61c29b00e5cdfa76cd2d45acf059a", size = 151036, upload-time = "2026-09-29T00:53:06.284Z" }, + { url = "https://files.pythonhosted.org/packages/41/5a/48a16e323c5f9447a94cce7b59babf60fa04e62c3365ecf060c77ed8b320/librt-0.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:33f41443a1f4e1f099331b3d8120e409fbff84b9760bc1cc9ea496f37ddaa5cc", size = 155211, upload-time = "2026-09-29T00:53:07.72Z" }, + { url = "https://files.pythonhosted.org/packages/3f/29/0f59299eb4251a409b2e690ad4b7d9f8a676db829d7817ec961f32b44f7e/librt-0.16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e510b7770bee609617a3374a96548eb114cae048023e3f049ee449e7ff2db32", size = 502587, upload-time = "2026-09-29T00:53:09.191Z" }, + { url = "https://files.pythonhosted.org/packages/de/ba/d6fb4ef8d1537c396079d72289f16be7cd35a366e5065c51253fea2760b6/librt-0.16.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:efc49c462d4516b8a58b00b490078fa64689fd1fe66970cc190131d7afb8027e", size = 496146, upload-time = "2026-09-29T00:53:11.016Z" }, + { url = "https://files.pythonhosted.org/packages/52/fc/8c50dd4d7cc97c0ee8f252c8a3104980f234391cf1519b554e8b9de08b60/librt-0.16.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:92caf82ebef5e12d21c72242b70d1e92536f1711cf2a727a4c276de4b4469087", size = 513336, upload-time = "2026-09-29T00:53:12.655Z" }, + { url = "https://files.pythonhosted.org/packages/a9/59/16c409c56f708eda2db9a0553662845d45e3871c77d70a240dae3f3bdc56/librt-0.16.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17bac7f7a16b328fff77e440287693eb017abde913595b5827ebccbc21ecd8a6", size = 531689, upload-time = "2026-09-29T00:53:14.39Z" }, + { url = "https://files.pythonhosted.org/packages/88/82/d34772a6c29d1446dcca6e64d74062efd508523ab351aa16625a4689d5cc/librt-0.16.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5b976054553670829985ed767feb78fb6bcede0175327c4844dd5c281c1be659", size = 524537, upload-time = "2026-09-29T00:53:16.064Z" }, + { url = "https://files.pythonhosted.org/packages/77/8f/24c5631313746131ccee53bc91fdc8374f9cf25e0082a1fee9c93bb98acc/librt-0.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0058f9d68721094105917254c72ac0569117bb7b13b9769cf45d26d89f9d21cd", size = 543231, upload-time = "2026-09-29T00:53:17.939Z" }, + { url = "https://files.pythonhosted.org/packages/f2/cb/5f8e0d41dbd8b499c2265e939c31acc9ba59845565bf99539ad1c06aebcf/librt-0.16.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b7beaf3f4487b7d8adef1f158b49067cb4d5a19fa7a3bf31a4e7a820e435c5", size = 546499, upload-time = "2026-09-29T00:53:19.666Z" }, + { url = "https://files.pythonhosted.org/packages/be/61/063052de441d1385f59cea4223f184bf9e5d125de1ae3239b490aa1e486e/librt-0.16.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:468df902df016a06eb0e40b0747dc8d14e47d7a38b18b63b1fb167d85cb94d63", size = 555261, upload-time = "2026-09-29T00:53:21.292Z" }, + { url = "https://files.pythonhosted.org/packages/14/11/a2ada0529372268d6401afa9d457a095b68cd7753532b6f7f33049a19b43/librt-0.16.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:aea7b1f2b125dad5de85f049136651bff256c883c65e6b9209b2da0a1ac3cdef", size = 536029, upload-time = "2026-09-29T00:53:23.07Z" }, + { url = "https://files.pythonhosted.org/packages/23/9d/5bb6d38853382986dca702fc7e06c8256d30f5fa0676d882773b744610dc/librt-0.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a8afb6557920860b7a3a596eb804cf37e09e7cf8a803db2478c202acc72d8c2e", size = 573748, upload-time = "2026-09-29T00:53:24.696Z" }, + { url = "https://files.pythonhosted.org/packages/99/f6/0025cde35ff7f607684dc775a2b2d732cce2561cec050a6ee1fb2e1fc6fe/librt-0.16.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:77c7a2b4fe2c1369e0d5aa1cade26740a7b14be32fbc9a5535d617d20065c39d", size = 79861, upload-time = "2026-09-29T00:53:26.089Z" }, + { url = "https://files.pythonhosted.org/packages/93/93/303b8592909bd583f83f02818ffbea3f7647ca1e22b1cd5465d04b8145fe/librt-0.16.0-cp313-cp313-win32.whl", hash = "sha256:02d89c813d5ff74b17df72d3a34819d132cd168e56b81bf755b809bd9e46b8c4", size = 106236, upload-time = "2026-09-29T00:53:27.43Z" }, + { url = "https://files.pythonhosted.org/packages/cf/24/80bbb463c60ed18e29cb26aba386ddb76609580e4e7160710e550587018c/librt-0.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:14ed6ebe3e4f85f326d7920011ad30ff49ed9334e62cf88caef9ba973d9e3a92", size = 126180, upload-time = "2026-09-29T00:53:28.7Z" }, + { url = "https://files.pythonhosted.org/packages/43/80/b1a6fbdd7da825cdd55c71aa81eb6cfa82c513c360774152eacb50b4a771/librt-0.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:83d4041a3d9b2fd053a8a4e1f22878b3e5833e2712956382d5c048d791454e91", size = 116557, upload-time = "2026-09-29T00:53:30.012Z" }, + { url = "https://files.pythonhosted.org/packages/1e/93/9e0cf7da129a93c3dc7f45bc3cd4a660f2aaa995aa8a6f95c2583ef41239/librt-0.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:931a0bb0fcac88f263e269e46eb30ba8e21402cd3c62ca40cb97034c0693fab1", size = 149844, upload-time = "2026-09-29T00:53:31.391Z" }, + { url = "https://files.pythonhosted.org/packages/8f/26/8a90d2a8f2b2e471bb486b7aec117b8ae622715ed6c39853aec48ea20073/librt-0.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1bc17e54e5305f8d40b7ca203671ff5a9e59c1d0f8ea0f625dcca53a3984de11", size = 154111, upload-time = "2026-09-29T00:53:32.718Z" }, + { url = "https://files.pythonhosted.org/packages/35/ce/67abb46258da4d3e42ff5b141db6f38c59357bef84c7979f183b22f924f1/librt-0.16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:877698bf6bca5721d8be345f2fe09778e40ecadea8b58c73075f2b1a53666bf2", size = 494234, upload-time = "2026-09-29T00:53:34.466Z" }, + { url = "https://files.pythonhosted.org/packages/12/f9/ea7162414a16f8f1bbd3b493ad6d22b926c5471916e078350bdbca8c4e5f/librt-0.16.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5981c011b306781ce561e18e14230a14524a3d8109b97553666c942c18f31a96", size = 491173, upload-time = "2026-09-29T00:53:36.124Z" }, + { url = "https://files.pythonhosted.org/packages/b1/09/9b3e869060dd33f9989b80ba4fea306f6db8cecefcdfe6d7346ef0603f65/librt-0.16.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:afced3dfc17cd805ecf7a3d77996a71cf5f2c75aa66eb0c21a9930f4fc992f86", size = 505547, upload-time = "2026-09-29T00:53:37.686Z" }, + { url = "https://files.pythonhosted.org/packages/50/07/79007d2165f649ea93e08c0962d1d62c70af9cde77965255095bf9d96f9a/librt-0.16.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ca8052401c55d7511dda6760719fda7618067e83535d7d0010096d216c34b667", size = 523057, upload-time = "2026-09-29T00:53:39.301Z" }, + { url = "https://files.pythonhosted.org/packages/61/0c/8fbaff66d0ba376d8864653f5acce1569bae27e89648671f26f7eca67ab8/librt-0.16.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1e511762a074005bb0aa569166779834e75e438370226930d0ce1866d4b6a33b", size = 515158, upload-time = "2026-09-29T00:53:41.012Z" }, + { url = "https://files.pythonhosted.org/packages/df/2e/23ff0dece76f07a4124413a57682efa0bbeb5765ac0122bc0955513f82ca/librt-0.16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1e8591bd8a5a628cd7f07954c6a1592359a878bf032957a8e9057a41d644311", size = 534183, upload-time = "2026-09-29T00:53:42.451Z" }, + { url = "https://files.pythonhosted.org/packages/26/c4/e11dea21d9a29486eba78887380374189d472734fa32cc50cb37ca44d3d0/librt-0.16.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4aaefb4ba6c07e1aeebb2795c8958148f1d6f9af3b555b53d23d766edb6d67a", size = 540663, upload-time = "2026-09-29T00:53:44.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/75/e873ae158a8b7f5359be33e7fd6c1fbe02d9a78a3e89a77de6b0e837f476/librt-0.16.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:d92db7a0f6aee44f1baee94750457e8d2d1c6ccea41842de6268d34e8dc7eddd", size = 545908, upload-time = "2026-09-29T00:53:45.812Z" }, + { url = "https://files.pythonhosted.org/packages/ba/36/8939d3f6a93e11bd9592e6fe28d2b44f1c2dc4bed6e22e72359a91e18ffb/librt-0.16.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:378dfaffb38e59c24a87cde5713cd865d51ff7383fa12947f3907f306ea1ca55", size = 523302, upload-time = "2026-09-29T00:53:47.596Z" }, + { url = "https://files.pythonhosted.org/packages/71/14/35309f44a077f0f42ade0e2e7cd88c0cea760c661af205c01ea90d3c0e1f/librt-0.16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3e0c39bdc85370422e8b637be76eb1fd07d30967551b03e62267dd156f553152", size = 565911, upload-time = "2026-09-29T00:53:49.278Z" }, + { url = "https://files.pythonhosted.org/packages/78/0c/df6255b94967f3159ebc46f08d8e783c12da6ee269ddb74b2efed63c640c/librt-0.16.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:1b384b90ab79a7bc30b566895809a636e0666f21f3cf12b54823d025b7e83839", size = 79119, upload-time = "2026-09-29T00:53:50.938Z" }, + { url = "https://files.pythonhosted.org/packages/6b/44/d30d5a5461378c9d33f36736c6791c3b4c4ba4b1ffe0da7350aedcb2c9a0/librt-0.16.0-cp314-cp314-win32.whl", hash = "sha256:52327da75a94012e7f932f913d20d3876bed3c102be00e6c3e8600ff7bdd58a7", size = 100140, upload-time = "2026-09-29T00:53:52.205Z" }, + { url = "https://files.pythonhosted.org/packages/c2/98/769712f356a1e897df3581bb0c3100375d054a000de26099360ea65b5111/librt-0.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:3f0b8114c44b2ac06ff5dacd08e07e8e807ff4f46083f2a1602685122559be41", size = 120464, upload-time = "2026-09-29T00:53:53.495Z" }, + { url = "https://files.pythonhosted.org/packages/bf/d3/ae2abccc8bdc8b063c1613a77686e17b74e9d3d60cfe6f12c63fa2821b9f/librt-0.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:8caf96a4ef8fb27d0ac0d1ad8337d26a240acd4a02fe4345d0a8f264753e8f99", size = 110889, upload-time = "2026-09-29T00:53:54.817Z" }, + { url = "https://files.pythonhosted.org/packages/e4/26/0737d4be058dd6376eade7dd8b380d4b869b6394cee929393a5a431c45bb/librt-0.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:953107e2f68d0f3512c48f898b0dbf0ce5cc52bba0f318d847c985dc555ee4cc", size = 159838, upload-time = "2026-09-29T00:53:56.22Z" }, + { url = "https://files.pythonhosted.org/packages/01/96/9bc96531d7c620e9949af904470f02e3fa8f35129ab8e8f281c51eaa3788/librt-0.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ad37d5b9abd49c9a655dcda7ea52a8a752884062ef1ee71ae17c2f2a0f81fe6a", size = 162018, upload-time = "2026-09-29T00:53:57.532Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fa/b0289dcb186eb3f97221ba00da5f4bd3ba7fa5e99752d48aa8d336615334/librt-0.16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c4aa329c17bd1aaea4f6e89335d8ccd494b3a5830b6654462273e50e11023f0", size = 703983, upload-time = "2026-09-29T00:53:59.024Z" }, + { url = "https://files.pythonhosted.org/packages/d9/ab/05ebbbde7530fc5eeb58fbd1581522a9f64f132fa703c4c595eed6e14760/librt-0.16.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0ead24d2562a49473dddd9efef8581f020007eb0054389c3ee3ffad38b1ca4c9", size = 683847, upload-time = "2026-09-29T00:54:00.671Z" }, + { url = "https://files.pythonhosted.org/packages/a3/75/f52aeecd4dbadbddf80725ba7de126d8bd5d0eb66247eae17a81ed90dd4d/librt-0.16.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:02118f56a9c36ddd07dfd9b919d9ecc117ba20a90987d56aa4c429fa34509188", size = 698045, upload-time = "2026-09-29T00:54:02.247Z" }, + { url = "https://files.pythonhosted.org/packages/e6/55/fa277a835cd6eb42380591ceb85f48c5b4d2b2d7e2cb9869e1e17d24d237/librt-0.16.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4e29522c62e28595ff7e324c6834ade51127707f0e255b18d1c1cf03d39c1048", size = 724640, upload-time = "2026-09-29T00:54:04.078Z" }, + { url = "https://files.pythonhosted.org/packages/25/e4/2cf64354f3fde8ebd591b3b48f96510bee24ac5f7d1e567adbbe210abdbd/librt-0.16.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3ff4b2367926b69c6215635902cccb04048e73094e9862900d27cb2c6bbff143", size = 731748, upload-time = "2026-09-29T00:54:05.741Z" }, + { url = "https://files.pythonhosted.org/packages/f8/c9/c180af3e94e01aa529fa93d7733fec2abc47f222345400cf21d5481d5f8a/librt-0.16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c6f1b27bf1632a7e016af9f145f82be95e1edd7721a646505c21059257cb5a04", size = 754960, upload-time = "2026-09-29T00:54:07.38Z" }, + { url = "https://files.pythonhosted.org/packages/95/d6/01073aa78c58f356b10d9c57b3fe9abb143df338142bcd316df417b89db0/librt-0.16.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:5696d7f52e7b37217cb3a8f92c744fe835942602fdd4c1a8bc4741d3bfdce15e", size = 747000, upload-time = "2026-09-29T00:54:09.06Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d8/1de3783908658d697a8cfc00582f61299ffba7796d7260c112e4700b1109/librt-0.16.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:6072e92dd876ff6ceeb6cf371e35e51f479349837391341f479b08df4564242b", size = 749569, upload-time = "2026-09-29T00:54:10.702Z" }, + { url = "https://files.pythonhosted.org/packages/b7/32/e817f66c96d6caa8bb8435ff93c4c220624d59efc506be59ab98fcd01d0c/librt-0.16.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:39ca4f2f2fe05de8e63493da592d84311adabe5bef52b193851981da9816b302", size = 729483, upload-time = "2026-09-29T00:54:12.25Z" }, + { url = "https://files.pythonhosted.org/packages/7c/c9/23992ccd2b9d22798fdd0f61353183a47414e651da782ad83eb680f50833/librt-0.16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f9807485a908f00355820f18e91e045ffdcdc5adb68aaec40a1e2b88c5f7bba1", size = 776584, upload-time = "2026-09-29T00:54:13.837Z" }, + { url = "https://files.pythonhosted.org/packages/67/3b/e8af957f08e6e2e8e566b099e43d2748418aa565df6ee1d9d3331209fdfe/librt-0.16.0-cp314-cp314t-win32.whl", hash = "sha256:94be5cb7bca4df6201f4183e9e4fa2086c655283d20b38cd84500a69057575a7", size = 104402, upload-time = "2026-09-29T00:54:15.568Z" }, + { url = "https://files.pythonhosted.org/packages/ca/1c/946e6443d7cd32347a086043395e421e52fd603c9163d2ec970ceab8eed6/librt-0.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d46ca272b251d033dd4527b0dec5f261a28a52bd5fa0f99c117b0a1f8588cc2d", size = 126227, upload-time = "2026-09-29T00:54:17.165Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a3/bc4f9959d3c62bcbf9e5fd3470a8bbd8bf33224ed4c25d7fba173201b8ac/librt-0.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b9d6d4b14e92d876f8026b54c20c445f36425214c1081dc76f74e40db386b82b", size = 116493, upload-time = "2026-09-29T00:54:18.567Z" }, + { url = "https://files.pythonhosted.org/packages/b6/4b/10fdb42dfab4c1533e1570e686b18e86ff4328b406361b39fb3016667638/librt-0.16.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:6fe436af2eaf630474f491af5d032cbe45f93fcff5c3b9fe4ab194a7255b20ff", size = 149829, upload-time = "2026-09-29T00:54:19.933Z" }, + { url = "https://files.pythonhosted.org/packages/8e/30/a90ca13f1d3d91af1680000a4038536907018fbd51764767287a05d28b8b/librt-0.16.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:8ff5d26c529336be9bd7ae04483235d77778ee7d6444a95353102b542601ce81", size = 154522, upload-time = "2026-09-29T00:54:21.306Z" }, + { url = "https://files.pythonhosted.org/packages/5c/dd/bcf364eacfa070bb1fc88d503111ae7177914197ba1fa717c748926e7930/librt-0.16.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:909d8e3c1faee44cb762b1c519ff8613dcc5ceae5c99987a00917b5a31fd1d6a", size = 497593, upload-time = "2026-09-29T00:54:22.798Z" }, + { url = "https://files.pythonhosted.org/packages/18/c1/2c4e81e347bdabfe8346bfc6dc37ae5e154d61c88e30848ec0606025a5e0/librt-0.16.0-cp315-cp315-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:6d4a64283ee61824b5790de882bc68e2d9d7a5143537cb7a966f7354f71646d4", size = 480459, upload-time = "2026-09-29T00:54:24.364Z" }, + { url = "https://files.pythonhosted.org/packages/ab/d7/fef2a3cb8400701be496f6e459876f650b3451f407e30cb243de571ae615/librt-0.16.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5810ba811297fdf37a1531a57667cb8ace0842013ca8606bf9eb7c24cf4be154", size = 507814, upload-time = "2026-09-29T00:54:25.997Z" }, + { url = "https://files.pythonhosted.org/packages/e1/6f/53762927a32e9dc9eb1d1c1f3528da281290c86d96929a8af652f671266e/librt-0.16.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e56aaf8c167548dc8e5d6f3bd0f48dcdd299a23c73be3f744aab79d99e9c7f5d", size = 525114, upload-time = "2026-09-29T00:54:27.747Z" }, + { url = "https://files.pythonhosted.org/packages/6c/67/0b9d031f303c4e8c691a9a8ef9d272f13b530c11cc563b819df621b6a348/librt-0.16.0-cp315-cp315-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f36c58e33b304b525c6c9c5076399c6ebf1109e17b9051a05a407b091b9215b", size = 520465, upload-time = "2026-09-29T00:54:29.331Z" }, + { url = "https://files.pythonhosted.org/packages/ae/d5/2056a3a85864e882eb17a203a10ddb26fa748bc9718ec67e79059ab46cae/librt-0.16.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:242e00b3d4fa37c3d3c1ca5f5c9adb7d909ddb1eac9c41f2787320d00caa0af2", size = 537257, upload-time = "2026-09-29T00:54:30.915Z" }, + { url = "https://files.pythonhosted.org/packages/54/57/e0d79790c163cbc0909e209a6f62e30bb713b647f905a176cdc64848fd4d/librt-0.16.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:0253721561787b8df8443eb347b7a6461015354e5bdd37ee38a41fef220d2bb0", size = 527442, upload-time = "2026-09-29T00:54:32.478Z" }, + { url = "https://files.pythonhosted.org/packages/aa/50/1c0c95aba7af51f4752ea34fbf2eb79b36e7cae3a72536248e8c735de735/librt-0.16.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:3e483a8d69ede8067db70c0e83007423b6925de6fd53afed01d66160f2e9398c", size = 548139, upload-time = "2026-09-29T00:54:34.183Z" }, + { url = "https://files.pythonhosted.org/packages/98/91/a8a43dd5138d4f55f88846b8f0c85454a4fad69952cebfcff9948f831290/librt-0.16.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:69ba927445cfaaffb4081003ef5224c55a5c2ab67ef956f416ef744916e44121", size = 529708, upload-time = "2026-09-29T00:54:35.761Z" }, + { url = "https://files.pythonhosted.org/packages/2d/41/d5226881ab2b7c20d9d587b37bdd4a0ec8775a96d00ca87ac9f385587db4/librt-0.16.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:d6a365f2ab45a984d0e00eee0dd17f599ceab8cadab6ea07b6111c8132fc0e42", size = 567781, upload-time = "2026-09-29T00:54:37.409Z" }, + { url = "https://files.pythonhosted.org/packages/bb/bf/2345ba57a626e8c78c4ddcc724636a8df5a593fe46c1ee76bbf477e32b0d/librt-0.16.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:f01f3805f2dae4781c0c34b440e31740d082950bdaf89a6f601ad589a28af57a", size = 79177, upload-time = "2026-09-29T00:54:38.792Z" }, + { url = "https://files.pythonhosted.org/packages/1e/40/99e77936cc9207f629b077bf7cbc5e2f0827cddd7c29792f04ef881bd2c3/librt-0.16.0-cp315-cp315-win32.whl", hash = "sha256:b0e3e721c75d2e79a76d4422c79d7ba705fe1bbafec907037fe7a657a480a0e3", size = 100127, upload-time = "2026-09-29T00:54:40.251Z" }, + { url = "https://files.pythonhosted.org/packages/56/1e/801fe26bc622061b9dfd010e166d94142cb774b733217e6d98d1c0cf2638/librt-0.16.0-cp315-cp315-win_amd64.whl", hash = "sha256:bc02954b1295de798bbdb0b4e2d8a28c2117de8b5c73dcbeb27dc32572dfb971", size = 120480, upload-time = "2026-09-29T00:54:41.722Z" }, + { url = "https://files.pythonhosted.org/packages/bb/a9/d533983055bd36e112627384c2c038845d1df882540b8bcefb566475640b/librt-0.16.0-cp315-cp315-win_arm64.whl", hash = "sha256:c5db585d43449a5f54303d4b2774e45e1babd975cfe1630a3d708c0b80c3e560", size = 110915, upload-time = "2026-09-29T00:54:43.052Z" }, + { url = "https://files.pythonhosted.org/packages/55/fe/d62238fa9c653b0e0613290349467cb65711b5800e8e474f45e942a0ad95/librt-0.16.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:f06c689cb14afd9b612727553a5ec5a40febf113ca41c4413a2b0b334285884b", size = 159714, upload-time = "2026-09-29T00:54:44.497Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ac/f31efe7818700be72ba4f9af8a80fa67c39808c8d26dacc55dc1f6f35172/librt-0.16.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:13b4e8aba90b0b1c82474e9844aa9ffe7ad3faa484350e1da64cb8188d903134", size = 162086, upload-time = "2026-09-29T00:54:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/d9/16/4d7487bf86a9d7e8e18f37ba5538789233ea693637379b75701bd35ec9de/librt-0.16.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a269c46ae327d8e6f8c1f85f7516cb52c0fa48127565a1105a4f4a05ff2a0b4", size = 712727, upload-time = "2026-09-29T00:54:47.486Z" }, + { url = "https://files.pythonhosted.org/packages/77/74/50cd550ccc1a517b9ed62347625c01ddf8c4afad66490312677c011458f6/librt-0.16.0-cp315-cp315t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a33e0dae1f8592146a4764d54ce842b278732d21a84e17c3bbe6b1bc158a2248", size = 681106, upload-time = "2026-09-29T00:54:49.126Z" }, + { url = "https://files.pythonhosted.org/packages/df/5d/7293f712975ee6fdd2251411fd9ef1c62bc99c7b83ecbe62dbc999b1fab7/librt-0.16.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:47ada6ea32636492c61aa8ad27ae3b9404bfe7a97e3ba946d1984236cc741da0", size = 705921, upload-time = "2026-09-29T00:54:50.905Z" }, + { url = "https://files.pythonhosted.org/packages/67/f7/8aab946f11d59d1bece9ffc65d994b200c789a8ca5a95c17e17e609f912a/librt-0.16.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43bd6e642d8a248c114327f98dd25ac5a7cb5aa168ef02f0559b91874df16b8", size = 731786, upload-time = "2026-09-29T00:54:52.584Z" }, + { url = "https://files.pythonhosted.org/packages/43/76/1c42ab31e7cb8384ebf6d3af607213c495222474f4940443ae7639ab7685/librt-0.16.0-cp315-cp315t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c3d1bb7841a816ace6449bb26d3f9560dbfa20e71c568d23f0f62bf1e68f50b1", size = 744964, upload-time = "2026-09-29T00:54:54.217Z" }, + { url = "https://files.pythonhosted.org/packages/6f/2e/4b19982d933d2dfced671e840b219e4c1cd3f507df6e6dabb51bbd4e3850/librt-0.16.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:3931f7a3db322e7f44e02a280e3949326ce9579ad388ee8d691dc7c76da9fb70", size = 765305, upload-time = "2026-09-29T00:54:55.815Z" }, + { url = "https://files.pythonhosted.org/packages/b8/1b/e872583de2dcb3ac7746e7a2321aeeb168274f3952a87dc66e05ddb29faf/librt-0.16.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:f4462528b6000afe8f16907b5c7c2553abf1df005ba5140e6eb394541c3624c3", size = 744959, upload-time = "2026-09-29T00:54:57.532Z" }, + { url = "https://files.pythonhosted.org/packages/10/de/a18c6bcfb297af2674233e90b3c3661c3a0af0f1434a0c966d2ef708a835/librt-0.16.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:80039ba9b6a7d5f1a0175a4cca6bbefead87bd854c80abad1cb30afe47a830db", size = 758138, upload-time = "2026-09-29T00:54:59.484Z" }, + { url = "https://files.pythonhosted.org/packages/22/1c/0df1d732539c297bb1a093e1fe3204d2faf76a9022cf4e1d1c2fce059790/librt-0.16.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:7a1d272724b581bb6bc769dfdafed6da2ecc9886ba2450311de55a4ac2e1e9cd", size = 744268, upload-time = "2026-09-29T00:55:01.179Z" }, + { url = "https://files.pythonhosted.org/packages/a2/f7/ccaf31331f20c91a5bd9bd48ffc3f743f9c81bb5720cc7b2a720ca204f0a/librt-0.16.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:fbe4fb8c5445f7496d7f7f6bb0807875d09d47e6771ffa175fb2df2895fb86ba", size = 785783, upload-time = "2026-09-29T00:55:02.938Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ea/7421d9e6db894cd6cd788b604b3394a163ded6b942052b6f96c23ccf08e1/librt-0.16.0-cp315-cp315t-win32.whl", hash = "sha256:375bfe6b572a8f6cfc398709356046173bf27e64c4c5edaf5f7062f051fb4bf9", size = 104355, upload-time = "2026-09-29T00:55:04.533Z" }, + { url = "https://files.pythonhosted.org/packages/85/6d/7c31a506eb847bc58aeb2402d58e17605e821f68ab5df5e66fe274a6df41/librt-0.16.0-cp315-cp315t-win_amd64.whl", hash = "sha256:bd3150023d3dc2bc70f3784e59ffa1140d56ddba3d8125b3d6f9f85221279bfc", size = 126134, upload-time = "2026-09-29T00:55:06.017Z" }, + { url = "https://files.pythonhosted.org/packages/36/69/7a5d10ac409c4da0355e054a14371871da9b5557fcc42772cd00181c6cce/librt-0.16.0-cp315-cp315t-win_arm64.whl", hash = "sha256:8ceafb70f2a4f0826f11031942e59c0728fd98da112dc346d4352bde1e486866", size = 116391, upload-time = "2026-09-29T00:55:07.484Z" }, +] + +[[package]] +name = "mcp" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx2" }, + { name = "jsonschema" }, + { name = "mcp-types" }, + { name = "opentelemetry-api" }, + { name = "pydantic" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/8d/e0d339616f4810e9051d4aba6887afab289ab1f81875fe908b606cdfd0e3/mcp-2.3.0.tar.gz", hash = "sha256:8b147a50441cf059dc88c684e0aeed3687f0aa0f39c6cde7b90330effd2b34d8", size = 4171955, upload-time = "2026-10-02T22:06:56.091Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/d7/4a2878f128df729db3ca04e71bfce26405cc2e0c9ebe19c592feeb4c61b9/mcp-2.3.0-py3-none-any.whl", hash = "sha256:dd0c44c089d16453e8ae31a3877a0054d7a2314caaa81f5e0541b9b1734b2377", size = 368664, upload-time = "2026-10-02T22:06:52.119Z" }, +] + +[[package]] +name = "mcp-types" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/2d/7c251e34207f6c51000312fc8839111ac45cfe02023f90b44e7f1051dd8e/mcp_types-2.3.0.tar.gz", hash = "sha256:d1e46549edb35ee19a94940fcee6d1addd7e589ab7ea92dda83f5d84781fc362", size = 65898, upload-time = "2026-10-02T22:06:57.863Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/48/32583066b155bb5d4507e7887dc88ee23118cc4de8e2a12870c49719e2a8/mcp_types-2.3.0-py3-none-any.whl", hash = "sha256:968efdbdaedfab06adae40d378a34395f1090c5921d4be3c9cde283aaf76d91d", size = 69108, upload-time = "2026-10-02T22:06:54.099Z" }, +] + +[[package]] +name = "msgspec" +version = "0.22.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/e6/6dcf9306ff3c5e486578f3bf29ed11dfbdbbc2a8bf0caf7e07d392887fda/msgspec-0.22.0.tar.gz", hash = "sha256:0a13624a4969159fe35d8c2a3d377b2b61bbd8585e327440d5e52725affcce38", size = 343188, upload-time = "2026-09-29T14:14:11.422Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/5e/78d4fa2073bb3a891753e7f915d51094e2ded5aa5e9b20402518929b373e/msgspec-0.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f3413e3647275f787b21b4dfb4836a59a1a5acf1018ab1d45843b1d7edf15c22", size = 200076, upload-time = "2026-09-29T14:12:07.599Z" }, + { url = "https://files.pythonhosted.org/packages/38/f8/59701da04584af4ccd55f42200da303ebf146cd6867186a8b9b1e127a4a2/msgspec-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:38c5b9bd347bc9abbcee40752be3c5117854e891ea7a1881a56d4b3dec58c5e7", size = 192337, upload-time = "2026-09-29T14:12:09.198Z" }, + { url = "https://files.pythonhosted.org/packages/eb/dd/bd4131da741aa349656fe32a5cca0c4266c58d7b5ad75485bed29565f7cd/msgspec-0.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57c282f474e17acf6bcf84f393c73afd45d6eba47cccff8b76b79c4fbb8a3b54", size = 222888, upload-time = "2026-09-29T14:12:10.691Z" }, + { url = "https://files.pythonhosted.org/packages/c6/46/01fe71c42b3342f00e2dd6c5a8837f5dc4d0e1596b4c74c054fb13075201/msgspec-0.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12a887c4c06e4a771a2db32c9a80c7bb21866b12458025f636dcdc2253331c28", size = 227838, upload-time = "2026-09-29T14:12:12.178Z" }, + { url = "https://files.pythonhosted.org/packages/62/8f/1a459825e0a5510de882af461459bd7f0525342b3c0bf1000e27be7aeef5/msgspec-0.22.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6c8a3f210421e29d8f7e9815f106cf59d758665b7fe5428e61152ce24fe65d7", size = 235818, upload-time = "2026-09-29T14:12:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/9d37b6f1190101b452f6c455e8715cc9960afad231e18cf9545af58710b9/msgspec-0.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ebd211d7af79ed8710c64e9e8d4c0d02749bc20170e7ab4e1c5801ca7c99d25b", size = 228019, upload-time = "2026-09-29T14:12:15.156Z" }, + { url = "https://files.pythonhosted.org/packages/c1/d5/33723137c96b8f244d8e6fc57a0a8d3b57b3599ce9b4a4dd58dc55a46d1c/msgspec-0.22.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:27d9ef46c80884f9c4f323e0b18bec464287e872121e70f2cbe47335780bf597", size = 236406, upload-time = "2026-09-29T14:12:16.908Z" }, + { url = "https://files.pythonhosted.org/packages/44/4a/f0e4a9ab970ce0a31f191acb772d3e1af67eeb73e1d73b70c079252aed02/msgspec-0.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ec108e96fdaa8fdbe5bb993ec97a9d1faa69b3a521eecd71a6e5acbe0e29ae69", size = 231028, upload-time = "2026-09-29T14:12:18.497Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e8/3de7345a8944a5bcfc9dd861d30fcea5f20f51057bcafacbbff9164e55fc/msgspec-0.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:21c887d4de397355f6635c2a037b1c067882dac5d132a1793d63bbf7cf5ca78e", size = 190753, upload-time = "2026-09-29T14:12:20.291Z" }, + { url = "https://files.pythonhosted.org/packages/66/c9/f0d3bd2dfc3753806ab70b8d00a1613019c39148a87da797771d7f72a0a9/msgspec-0.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a663a8d7f6ad56ac1dbcba91e046ba8ebab7773ae72ef3dd3c47f8226919184", size = 188939, upload-time = "2026-09-29T14:12:21.645Z" }, + { url = "https://files.pythonhosted.org/packages/9d/22/45c17acb1a85360b10afb95f66777f76bc2634993c66db8b7833832bd343/msgspec-0.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb1e129b81ac8fcf9ec649b081c6c8da1c7ea6f87cab336d46386abc2cd855c1", size = 198231, upload-time = "2026-09-29T14:12:23.016Z" }, + { url = "https://files.pythonhosted.org/packages/34/79/1cf725694125051e866066d74e6199206838d1465cbfc35081dc29b6e366/msgspec-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dce29a04966e31abf9b83b697c6d672486526dc5d03fcd6970cb56d5dc1fbeea", size = 190911, upload-time = "2026-09-29T14:12:24.636Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b2/e0ace038031a2988aa2e85c431c4d7aef734fbba4749ace6bc5bf310b769/msgspec-0.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b962000e11dd34fb210a5a2c57a8a62b2d92b381c8cb3b05c075a83e38f8d645", size = 220343, upload-time = "2026-09-29T14:12:26.111Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e6/16ddb09185d79dc00177994cf0bdb1cd8e5cc44a1d1bfba61bdda5f382cb/msgspec-0.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6db3806b3b76ca78064255eac6fa101a8a64fe6f698d80fbaf81fdfa21217d4", size = 225251, upload-time = "2026-09-29T14:12:27.559Z" }, + { url = "https://files.pythonhosted.org/packages/16/c2/a6af0d38fb0e72f02851ed084c4b8175140cfaf3eaf48b38da0c3941db26/msgspec-0.22.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a88d939d3fe4b8c7314645ebcd6e86c8c8a512ea7820d6550355973e803bc0f1", size = 233488, upload-time = "2026-09-29T14:12:28.996Z" }, + { url = "https://files.pythonhosted.org/packages/0b/9b/b1c4208cdf487e2ba7af145f721b279444ff76af05a9f8fce992ed0588ee/msgspec-0.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0b31746da07cba0e330c6433a94a4699ad77d3aeb9638d1a320a7686b69f6249", size = 225688, upload-time = "2026-09-29T14:12:30.351Z" }, + { url = "https://files.pythonhosted.org/packages/83/54/b9240d908674ef7c41d02cb909731ad6d9931c23bd6a27d8d10776c6f964/msgspec-0.22.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:6ae370f92f3517f0e6f209ba7cc649c957b444868439197e046be07154667551", size = 234250, upload-time = "2026-09-29T14:12:31.887Z" }, + { url = "https://files.pythonhosted.org/packages/df/c0/d498798aaab3bd191a33955de47b40f07fae7667d86a33b705443a7e9491/msgspec-0.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a696f23f7c1ffb31fae308502e01a3965c3891d5c400f01d0d1096dbe77519e", size = 228337, upload-time = "2026-09-29T14:12:33.365Z" }, + { url = "https://files.pythonhosted.org/packages/fa/51/5e9ae5a5ddc254e15435749328161e95598750e5df644bb00fa9e2297122/msgspec-0.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:024138c51afd335d0b4dce401be33902caafac2b64f8c9f2509a378986175d98", size = 190962, upload-time = "2026-09-29T14:12:34.847Z" }, + { url = "https://files.pythonhosted.org/packages/12/38/fb64a18543bcbebc53a375cb00b1c93bf264a0b6c7bbe9e38b37cc5f0768/msgspec-0.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:4600dbec738ed74e4c9bd35503e84701200ea7db344cfdeda80677b3ee53eb64", size = 189458, upload-time = "2026-09-29T14:12:36.277Z" }, + { url = "https://files.pythonhosted.org/packages/a4/87/3e017dca361d09ed1cd09dc981a6df21b32e830fbec3470f7486d38b6be5/msgspec-0.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab1e9e7531e353653b906cdd12a0220cc288a1e8e3436aabc65f4508d91b14d9", size = 201301, upload-time = "2026-09-29T14:12:38.048Z" }, + { url = "https://files.pythonhosted.org/packages/fb/02/109165edaafb895668d87177972a32ade9126a54f3736123d8e44be9096d/msgspec-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b60b43425a47eb9cfe987f6874e354ca7c760e58e295b4e2273ff03574df28a1", size = 193044, upload-time = "2026-09-29T14:12:39.46Z" }, + { url = "https://files.pythonhosted.org/packages/54/a5/65de05f8804492f76ea121b21a125cdf1d97ec461c677bfa0ba354d6fbdd/msgspec-0.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5a169b5b03f0f2c7a296c002647db1dab75d2cd501bca34e32b71cab0261b56", size = 224035, upload-time = "2026-09-29T14:12:40.876Z" }, + { url = "https://files.pythonhosted.org/packages/4a/cc/aa1a47f8c92280d37498a5ea56a2a36606d034383e3e6472d64cbb56cf85/msgspec-0.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99c401861c5bb3a57f7d6423ea7ed4352cd57aa3f04f4fbe9f3e3e4564a10f08", size = 230377, upload-time = "2026-09-29T14:12:42.796Z" }, + { url = "https://files.pythonhosted.org/packages/61/50/f8bcdb3d613a4a4b92704297a12eba5c985cf572a64ee1a004d265759c69/msgspec-0.22.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08826f5e5b0fa2f7a88592c396a243cfcc63d37e19f9d4fbe3b3f1be2fbdc404", size = 237390, upload-time = "2026-09-29T14:12:44.282Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8a/473fa423f8fdd1b810b8652594323d7301df6920b62844d860daa0feff34/msgspec-0.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21460f54cee9208239b1a8421fdf25bffc77293e1daba88f585711ad839b9758", size = 227733, upload-time = "2026-09-29T14:12:45.839Z" }, + { url = "https://files.pythonhosted.org/packages/03/1d/272ce23adae6c71b3f763aed3ee6e115cccc56124ed8ee0e3e3d2681e2c8/msgspec-0.22.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cfc3d9557de9c806318725b702f3e664db33167bb42892079b693c69893fd33b", size = 236783, upload-time = "2026-09-29T14:12:47.234Z" }, + { url = "https://files.pythonhosted.org/packages/f6/26/29e0b9a8605c8819a3c718158e345a616ac42c092dd7d7ab248c2f2b0a72/msgspec-0.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0b25dcbc108783cb72503ed705b9fbb8c3cb02ee5801923f44b5f038c91cc365", size = 232728, upload-time = "2026-09-29T14:12:48.792Z" }, + { url = "https://files.pythonhosted.org/packages/e1/a6/99597c281d716da6c662b48dcc3f734669f716b41d5df2af367dac9e7c21/msgspec-0.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:6ad64f5c260866b0d543f89f50cee43628989c1433c5de7ce820281fa28a2611", size = 192885, upload-time = "2026-09-29T14:12:50.274Z" }, + { url = "https://files.pythonhosted.org/packages/46/80/85fff923d448b886ec3a85900c578d9367f08dad54fe48879495b4c6d055/msgspec-0.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:0922714feff5300aacd8ecd65fa828317ce4bf5212b3139258c0bfc0253cd80e", size = 191223, upload-time = "2026-09-29T14:12:51.699Z" }, + { url = "https://files.pythonhosted.org/packages/7f/62/5374fba2ede0408f4bd8b9b3a6c8464f8d0ea7ae9a2a064bd81ca492bd1e/msgspec-0.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f13c127a945479bc9db057eb253b8851075c8e1ae07ffc967bfa1c5676203a86", size = 201355, upload-time = "2026-09-29T14:12:53.145Z" }, + { url = "https://files.pythonhosted.org/packages/cc/e3/357baa8d2a9164a98dfd7ef9d3a58125df0ed981be909945bdd337be7194/msgspec-0.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5aa24eb475d070ecbbe5b21080fc3ce4b0b76c60de25cfe0c9678d8fb44bb42f", size = 193097, upload-time = "2026-09-29T14:12:54.52Z" }, + { url = "https://files.pythonhosted.org/packages/fa/1b/9cc07718d1dee8ed5e89a265801d565bc0f15ead435ccb198f9c7bf92574/msgspec-0.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:627bfdfe5a4b3d916b3360b30f4cddeee3a084f56593e33527c6872fa8322ff9", size = 224112, upload-time = "2026-09-29T14:12:55.983Z" }, + { url = "https://files.pythonhosted.org/packages/46/64/f33fdfe95aca76601194a7064d14816c7c22c4eccc1b03a5335785895fa3/msgspec-0.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6c310ef83e7e291b01a63298828f848348bb99e84a1098c4b3923c05674d032", size = 230472, upload-time = "2026-09-29T14:12:57.648Z" }, + { url = "https://files.pythonhosted.org/packages/8e/b3/8ceaa9981c230adf43c45a6e8da25da23a381eddc7ed05aeaca1d5e7928b/msgspec-0.22.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c1e76c6bd523141b9c05c2f8a70979cd0efedbd68855a66f292f8892c0b8fc7", size = 237382, upload-time = "2026-09-29T14:12:59.414Z" }, + { url = "https://files.pythonhosted.org/packages/88/a6/7b5c4fb39e0bf2dabc8be923c33c39b07ba769a0ce6f0afbbdfaadb1f2f2/msgspec-0.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc374dedd5f85a5f4de2386dc5f737894ccb8c1ac18e9566ce66fd9839e6285d", size = 227717, upload-time = "2026-09-29T14:13:00.88Z" }, + { url = "https://files.pythonhosted.org/packages/b8/5b/2334ee638880e756c8bc54a1177bd65877c786433693a43594ef5ecbe2d8/msgspec-0.22.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:feafe612034d49e9144340c0b5168ee4e22c2af4aaa2c1db11ae84e1aac9543b", size = 236781, upload-time = "2026-09-29T14:13:02.468Z" }, + { url = "https://files.pythonhosted.org/packages/6c/e5/b4c5323b17ecfce45350695d40fc93e16856db957a53cbcf2f53007d6e12/msgspec-0.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f48317f05312bfdf78248f53933f830f07ab75cc1c813ac3ca4220cb3b5b019", size = 232777, upload-time = "2026-09-29T14:13:04.025Z" }, + { url = "https://files.pythonhosted.org/packages/01/33/e591f9d3d8d6c9cfc02ae95f3e3c44920f2d18050f3f252c244e0f293a0e/msgspec-0.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:0739b068f31f2004a364f97679ba91f2f5ecd6ec2a5b4b890188ab5c57d20672", size = 192829, upload-time = "2026-09-29T14:13:05.519Z" }, + { url = "https://files.pythonhosted.org/packages/d1/cd/a011a5b8732cd781e2ea6da5b38d71ae4a9a329338411d1f008a58f5edbf/msgspec-0.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:508278300dd4efbd21cd3a4b2b016160a5feac98bc880d3673f6c06697baaf62", size = 191258, upload-time = "2026-09-29T14:13:06.909Z" }, + { url = "https://files.pythonhosted.org/packages/53/f9/ac027b35477e6b83bcee32b3d9675b37abfa130f098dd6500fa67d768852/msgspec-0.22.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:221cbcbfa4478152b91d37dcfd4830e2be92773e8139e883f43773450ebacef8", size = 201276, upload-time = "2026-09-29T14:13:08.311Z" }, + { url = "https://files.pythonhosted.org/packages/13/6b/2bffffa31662b1353a62e672442865d51c291ad778352fd490de16361dc6/msgspec-0.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dd9568695911055440d2bb7099ed9098fc181d335daa772d0eb3fe8f31ba4efb", size = 193233, upload-time = "2026-09-29T14:13:09.943Z" }, + { url = "https://files.pythonhosted.org/packages/14/bc/4066416ff6aa918d1ef9295edee0041e4629e4079ad3839bdd8a68fd87f0/msgspec-0.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f039ef5207b847f075a0a43020ee6140cd47505f890e47e157f2deb485c2dc96", size = 225101, upload-time = "2026-09-29T14:13:11.391Z" }, + { url = "https://files.pythonhosted.org/packages/63/ba/a8d390d5bd4c7d9ccde87c95cf071ada934cc9ca2c6af4d3d50b38f2d718/msgspec-0.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4f7e09cceac7dbf4c0761b8ae7df51c55b5df5e9af7aff2c895aac1ebea015", size = 230505, upload-time = "2026-09-29T14:13:12.869Z" }, + { url = "https://files.pythonhosted.org/packages/9c/89/979664fdc913c624ef88a139b40e3a95ddf2a47c89e8b5c4147f69ee9c48/msgspec-0.22.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:614e2c827e0a3f934f3cf0cf4ba65210df8132b75a69a8a1f51bb3b2caf0ac5a", size = 237382, upload-time = "2026-09-29T14:13:14.317Z" }, + { url = "https://files.pythonhosted.org/packages/07/3f/7d44c614376ae008ac6099be5f589b322c4ad44e32c6dbb0edd256215028/msgspec-0.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3689b9dfcc663358ef23ba4299d7460f01108515b041a7d30d05908ac9c32f", size = 228962, upload-time = "2026-09-29T14:13:15.763Z" }, + { url = "https://files.pythonhosted.org/packages/0b/59/bf8504e6f63f6769d01fb66f8bd856cf0ed39a07fde354f440d711640054/msgspec-0.22.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d2f950239ff1fc7322c6f9634807310265149cb168270d3ddcdda5b6ada13a28", size = 236691, upload-time = "2026-09-29T14:13:17.195Z" }, + { url = "https://files.pythonhosted.org/packages/2b/40/5a9d2bde12af16a22ddbf371990a81d3e3c0dcd4bb4ef3b3f9616b033c14/msgspec-0.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3c789b5ccd07c0a3c09767108ee06e089b2875f2309a4569c2648f30a8d31dfa", size = 232750, upload-time = "2026-09-29T14:13:18.691Z" }, + { url = "https://files.pythonhosted.org/packages/75/5d/c0e6bdb81a87f6bd56a663a330c271af7670490c80d8d635d9fa21ad1adf/msgspec-0.22.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:a66b1766311e42371e509c996c3933b161c7ae0eabdf361af5316dec197e1022", size = 136814, upload-time = "2026-09-29T14:13:20.415Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c0/b0cfc6d33608e5ea8871f3be31f9146c56699e737a7d8862bf018484f278/msgspec-0.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:749899563d26b211379f142b8ffd7e2d7da149a51717798f0ce994dce50324f0", size = 197097, upload-time = "2026-09-29T14:13:21.869Z" }, + { url = "https://files.pythonhosted.org/packages/42/1f/571f7fe7c725380605d680fc4c0084212b23d2dfcf6be0f2277f14462c56/msgspec-0.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:10d0d1d464960d99a949f7ca01ef8928e51c472433a5f5ab74b2d695fb830652", size = 196779, upload-time = "2026-09-29T14:13:23.62Z" }, + { url = "https://files.pythonhosted.org/packages/ab/f3/3c87372bac651b37911e0dc6926c3958949d3fcb8cec1016adbc44d948b2/msgspec-0.22.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:e79725246291516a7359caad5fb743ddc0ec66ed40d2381fb846325b5031504e", size = 205214, upload-time = "2026-09-29T14:13:25.158Z" }, + { url = "https://files.pythonhosted.org/packages/43/4c/fbccd6e0fbbdf10c4d9b6bac8a26148dd5483b3ffff6d6c5a376ff1f5cb1/msgspec-0.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:38f7022fbe91954b31afe3888a0af1b652e0f370fafdeb1d425f4a814d789c9f", size = 196941, upload-time = "2026-09-29T14:13:26.637Z" }, + { url = "https://files.pythonhosted.org/packages/55/04/8db7186d3ae8818356bc623cc132db8b77da37ce4b1345f35719c8ad5726/msgspec-0.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b6d3ca19a8ff28d0a67a1824e2bff7ec649ec795c80a265f20ade4caa63080de", size = 229934, upload-time = "2026-09-29T14:13:28.285Z" }, + { url = "https://files.pythonhosted.org/packages/17/24/a249f3491cabbe77cc65a1a6f87c128582aa39357227149be61cac8e554f/msgspec-0.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8b98ae215a102cbf6635f7df45f5c4af12f77fad1f7b71b9808fcf868a5735d", size = 234378, upload-time = "2026-09-29T14:13:29.821Z" }, + { url = "https://files.pythonhosted.org/packages/87/ee/6dbcb1b5de8e9d47e8f0fde9a288628dc178c1749a570b98251218fa10c4/msgspec-0.22.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e0aa0cc3f18c35bab79bd7b87fde95d6274a9deddeebd1ea541f8066a5073165", size = 243118, upload-time = "2026-09-29T14:13:31.544Z" }, + { url = "https://files.pythonhosted.org/packages/79/03/7dd2d0ca988600e01fc00ad0cf20d1d44bc59369a913c988654c65f6582b/msgspec-0.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8c8e84789918fbc15a503b92a829115ddd7567ecd3e4778bd418c56abbb86c11", size = 234557, upload-time = "2026-09-29T14:13:33.068Z" }, + { url = "https://files.pythonhosted.org/packages/74/e2/43f3c63bff1650efcaaea31466246e28b46927323fc9ff416c68cc6e4047/msgspec-0.22.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:3ca7d4cd69fbb66bd2da6211d3e79d40542d196c16c6d99bf838f76767ad35be", size = 241288, upload-time = "2026-09-29T14:13:34.532Z" }, + { url = "https://files.pythonhosted.org/packages/8b/70/11b93815a59674f33182dc3e873d343ca0b37e25be52ecb28f52092f1fed/msgspec-0.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:28f53f3604dd3e70225f7563c831628dbb03299b428f8e62aadb4b628e386874", size = 236432, upload-time = "2026-09-29T14:13:36.083Z" }, + { url = "https://files.pythonhosted.org/packages/b7/82/7aad0f033f8dcb3f23868773c2ede803ae162a784828ccde75aa3f9b2f9d/msgspec-0.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:7293dee54de040cfa225c22151cc3d72f17cd674b5ebcb52f38fb9f5701592e6", size = 202062, upload-time = "2026-09-29T14:13:37.955Z" }, + { url = "https://files.pythonhosted.org/packages/e3/45/cf52577926d73e2369e25927e389cb4ea1461169c489f46d3248159b5be7/msgspec-0.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:c3c510aba9015c085e514b75a9b3f1ed7c4591ae5e379655821b8bba51f30cc7", size = 201686, upload-time = "2026-09-29T14:13:39.42Z" }, + { url = "https://files.pythonhosted.org/packages/c8/63/d93937e2aae34ff1ea33b62799d1963cacc1bf432d196d6130039657a122/msgspec-0.22.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:263e110955ed76fe0af2d79f819903b50a70dc0e7a752eb7aabe79d2e0a084fb", size = 202241, upload-time = "2026-09-29T14:13:40.919Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e2/46ece11a244cd56432eb2362ffbb8014f3f02963136d84d941f71fdc2a3f/msgspec-0.22.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:c6f06576eced70462179a4b4638e84cf69fdbba37f44d13a64a21739c131a830", size = 194232, upload-time = "2026-09-29T14:13:42.454Z" }, + { url = "https://files.pythonhosted.org/packages/cf/b1/1c385f2f93006cdc2af1511cc512c347cb22e2d4f11952c205230aedf586/msgspec-0.22.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8d67582478b0eaabb899f2fb255c878ee7de57dff80eb73ab24f1865524ec441", size = 226524, upload-time = "2026-09-29T14:13:43.876Z" }, + { url = "https://files.pythonhosted.org/packages/dc/fb/c80c8842d40347cacf89a60a4986b849dae1a6dfd25830441efdd6faa65b/msgspec-0.22.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:71cbbdb39631064e2f2f9e9ac2b1b69931d72276eb5f9da4ed025726296bdbb6", size = 231816, upload-time = "2026-09-29T14:13:45.329Z" }, + { url = "https://files.pythonhosted.org/packages/73/ac/90bbcfd890b4bda90c93f7e1b7fc24e84b270420486d9d43ae31443d15ab/msgspec-0.22.0-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f0a5c25516e2034b2db7767081759ff8996e214def9c43b3055f61e1be1caad", size = 244241, upload-time = "2026-09-29T14:13:46.851Z" }, + { url = "https://files.pythonhosted.org/packages/72/9a/eabdb5f1b5e6013b0e2f9f2a95790587f6864aa9ca37f9d7dece65b53878/msgspec-0.22.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:a1dab6a99c759d1391ab2993388c1892746a697254f4b5dc6c059ca6e3bfbc8b", size = 230198, upload-time = "2026-09-29T14:13:48.296Z" }, + { url = "https://files.pythonhosted.org/packages/e9/89/9f080532d4ac52f416dd7318e55c2053cc071853d17d58e24897a5b553bf/msgspec-0.22.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:a52eba5c9528fd181fcec39d22b67aaa1dccc6cfe8e24d3f5d41130e6d04289d", size = 242949, upload-time = "2026-09-29T14:13:49.829Z" }, + { url = "https://files.pythonhosted.org/packages/11/df/6baf9b2f3523ebe2b820820c7929fd72ec5f483a93147130338ecc353fac/msgspec-0.22.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:1e547966017265c0d23342bcf2e027305dde40ea042d16694a9b96b4f696a052", size = 233914, upload-time = "2026-09-29T14:13:51.5Z" }, + { url = "https://files.pythonhosted.org/packages/bb/37/9cf650779c8c1e53291ef184c838703930a4cabb1fb37e222c85a7d49fa9/msgspec-0.22.0-cp315-cp315-win_amd64.whl", hash = "sha256:0067057df265795f742658b15dbe53f3b6f21d19dcfa53676db11088cfa41e0a", size = 197910, upload-time = "2026-09-29T14:13:53.071Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ce/2f78c93d4f69e0167a19c2d40d4fbf7bbd6f074e1047536735832a4368ee/msgspec-0.22.0-cp315-cp315-win_arm64.whl", hash = "sha256:05dbc8268e50c9232ec72b9af1c7b13049aade4d1197764e38c427048706e046", size = 197590, upload-time = "2026-09-29T14:13:54.47Z" }, + { url = "https://files.pythonhosted.org/packages/3f/bf/282e9a443058b85b8f706c9a651e2d8cdd11cc09d16e8fa347b6c57b75bb/msgspec-0.22.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:b3113ebcceeb7693a915183c73d92c10bf5c62851dd187cab43bd025fb587419", size = 206298, upload-time = "2026-09-29T14:13:55.913Z" }, + { url = "https://files.pythonhosted.org/packages/ef/2d/2e694fa46f55319007f72013b17341ea3868be1c77e7a597176b202dda92/msgspec-0.22.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:0dfadea8bdcfafc614bd031de55a8ede22b43445cfff6d8b77cc0c07d3edc8a8", size = 198145, upload-time = "2026-09-29T14:13:57.412Z" }, + { url = "https://files.pythonhosted.org/packages/5b/2e/2fa279cb57cb47175ae604d572787f903d4ad3f0afa867201bbd99e6647e/msgspec-0.22.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7a738826936c72348c613061d260446f13c82b6fd7d5d7705b6911ab8dca2f3", size = 232362, upload-time = "2026-09-29T14:13:58.817Z" }, + { url = "https://files.pythonhosted.org/packages/a0/58/a7e759b11b28441c27f803b29d9b5f4b5ad85150c89354b5ede1baca9258/msgspec-0.22.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f2ddea9d78d09460f06c26a7a508adcd049761c3208776162b8eb79b8a032cff", size = 235885, upload-time = "2026-09-29T14:14:00.381Z" }, + { url = "https://files.pythonhosted.org/packages/86/56/8d7ee098e94cbd9f35fa643dc497e06a4a6307b9f562cfbe48103fc3b209/msgspec-0.22.0-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:884c28c80b0a511595b29a9b04a3a230c3797369e4a033e6d5c6d9b5427f8e09", size = 248155, upload-time = "2026-09-29T14:14:01.945Z" }, + { url = "https://files.pythonhosted.org/packages/b9/6d/1cabb4b8a5dbf696e2b24df9e482b2e0333bb3b1b13ebb5433813e6616ec/msgspec-0.22.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:f7a923bcde480065c8e25967464cfb2a687ee67000bb43157e2d57e40eca7305", size = 236416, upload-time = "2026-09-29T14:14:03.363Z" }, + { url = "https://files.pythonhosted.org/packages/ba/43/8bf0f558eb369f1f2d494b3d5ab9d0ae0907d07ecc0cdbe11b6768b02867/msgspec-0.22.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:65eea14bc65ccfeb8f3af62cb204841871e2961f002d7fa87dbe0f79dacf1c1c", size = 247292, upload-time = "2026-09-29T14:14:04.829Z" }, + { url = "https://files.pythonhosted.org/packages/81/33/2fbaadf98b5510cac4bb56d2b03937e0b1fb4bfcd1ae6aba20361f299583/msgspec-0.22.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:0666a1520cab86796612e794e71107e0fbf5e8ff3ddcdfcfff8f1d94b860d2f1", size = 238220, upload-time = "2026-09-29T14:14:06.408Z" }, + { url = "https://files.pythonhosted.org/packages/f1/cc/b6be6041098ab859a8472983ccc2c08339fc2ef53f28d4f5fe7f4f34276b/msgspec-0.22.0-cp315-cp315t-win_amd64.whl", hash = "sha256:885c6e0c89d6103648525fe62aa78d600054dedf7b3713d23b15d7ddb6d66a13", size = 202939, upload-time = "2026-09-29T14:14:08.079Z" }, + { url = "https://files.pythonhosted.org/packages/5a/c1/664578dd98be70cd4ab1a9dcf3a181b1376b83c65ec41ee162130b58c8c0/msgspec-0.22.0-cp315-cp315t-win_arm64.whl", hash = "sha256:268594d0bae5510572599a6ab0364dd9de43c867d24a30856cd9f5edb63d8dc6", size = 202117, upload-time = "2026-09-29T14:14:09.891Z" }, +] + +[[package]] +name = "mypy" +version = "1.20.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/af/e3d4b3e9ec91a0ff9aabfdb38692952acf49bbb899c2e4c29acb3a6da3ae/mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665", size = 3817349, upload-time = "2026-04-21T17:12:28.473Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/97/ce2502df2cecf2ef997b6c6527c4a223b92feb9e7b790cdc8dcd683f3a8a/mypy-1.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4", size = 14457059, upload-time = "2026-04-21T17:06:14.935Z" }, + { url = "https://files.pythonhosted.org/packages/c9/34/417ee60b822cc80c0f3dc9f495ad7fd8dbb8d8b2cf4baf22d4046d25d01d/mypy-1.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997", size = 13346816, upload-time = "2026-04-21T17:10:41.433Z" }, + { url = "https://files.pythonhosted.org/packages/4a/85/e20951978702df58379d0bcc2e8f7ccdca4e78cd7dc66dd3ddbf9b29d517/mypy-1.20.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14", size = 13772593, upload-time = "2026-04-21T17:08:11.24Z" }, + { url = "https://files.pythonhosted.org/packages/63/a5/5441a13259ec516c56fd5de0fd96a69a9590ae6c5e5d3e5174aa84b97973/mypy-1.20.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99", size = 14656635, upload-time = "2026-04-21T17:09:54.042Z" }, + { url = "https://files.pythonhosted.org/packages/3b/51/b89c69157c5e1f19fd125a65d991166a26906e7902f026f00feebbcfa2b9/mypy-1.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c", size = 14943278, upload-time = "2026-04-21T17:09:15.599Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/6b0eeecfe96d7cce1d71c66b8e03cb304aa70ec11f1955dc1d6b46aca3c3/mypy-1.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd", size = 10851915, upload-time = "2026-04-21T17:06:03.5Z" }, + { url = "https://files.pythonhosted.org/packages/3c/36/6593dc88545d75fb96416184be5392da5e2a8e8c2802a8597913e16ae25c/mypy-1.20.2-cp310-cp310-win_arm64.whl", hash = "sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2", size = 9786676, upload-time = "2026-04-21T17:07:02.035Z" }, + { url = "https://files.pythonhosted.org/packages/1f/4d/9ebeae211caccbdaddde7ed5e31dfcf57faac66be9b11deb1dc6526c8078/mypy-1.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c", size = 14371307, upload-time = "2026-04-21T17:08:56.442Z" }, + { url = "https://files.pythonhosted.org/packages/95/d7/93473d34b61f04fac1aecc01368485c89c5c4af7a4b9a0cab5d77d04b63f/mypy-1.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3", size = 13258917, upload-time = "2026-04-21T17:05:50.978Z" }, + { url = "https://files.pythonhosted.org/packages/e2/30/3dd903e8bafb7b5f7bf87fcd58f8382086dea2aa19f0a7b357f21f63071b/mypy-1.20.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254", size = 13700516, upload-time = "2026-04-21T17:11:33.161Z" }, + { url = "https://files.pythonhosted.org/packages/07/05/c61a140aba4c729ac7bc99ae26fc627c78a6e08f5b9dd319244ea71a3d7e/mypy-1.20.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98", size = 14562889, upload-time = "2026-04-21T17:05:27.674Z" }, + { url = "https://files.pythonhosted.org/packages/fd/87/da78243742ffa8a36d98c3010f0d829f93d5da4e6786f1a1a6f2ad616502/mypy-1.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac", size = 14803844, upload-time = "2026-04-21T17:10:06.2Z" }, + { url = "https://files.pythonhosted.org/packages/37/52/10a1ddf91b40f843943a3c6db51e2df59c9e237f29d355e95eaab427461f/mypy-1.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67", size = 10846300, upload-time = "2026-04-21T17:12:23.886Z" }, + { url = "https://files.pythonhosted.org/packages/20/02/f9a4415b664c53bd34d6709be59da303abcae986dc4ac847b402edb6fa1e/mypy-1.20.2-cp311-cp311-win_arm64.whl", hash = "sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100", size = 9779498, upload-time = "2026-04-21T17:09:23.695Z" }, + { url = "https://files.pythonhosted.org/packages/71/4e/7560e4528db9e9b147e4c0f22660466bf30a0a1fe3d63d1b9d3b0fd354ee/mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b", size = 14539393, upload-time = "2026-04-21T17:07:12.52Z" }, + { url = "https://files.pythonhosted.org/packages/32/d9/34a5efed8124f5a9234f55ac6a4ced4201e2c5b81e1109c49ad23190ec8c/mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4", size = 13361642, upload-time = "2026-04-21T17:06:53.742Z" }, + { url = "https://files.pythonhosted.org/packages/d1/14/eb377acf78c03c92d566a1510cda8137348215b5335085ef662ab82ecd3a/mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6", size = 13740347, upload-time = "2026-04-21T17:12:04.73Z" }, + { url = "https://files.pythonhosted.org/packages/b9/94/7e4634a32b641aa1c112422eed1bbece61ee16205f674190e8b536f884de/mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066", size = 14734042, upload-time = "2026-04-21T17:07:43.16Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f3/f7e62395cb7f434541b4491a01149a4439e28ace4c0c632bbf5431e92d1f/mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102", size = 14964958, upload-time = "2026-04-21T17:11:00.665Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0d/47e3c3a0ec2a876e35aeac365df3cac7776c36bbd4ed18cc521e1b9d255b/mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9", size = 10911340, upload-time = "2026-04-21T17:10:49.179Z" }, + { url = "https://files.pythonhosted.org/packages/d6/b2/6c852d72e0ea8b01f49da817fb52539993cde327e7d010e0103dc12d0dac/mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58", size = 9833947, upload-time = "2026-04-21T17:09:05.267Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c4/b93812d3a192c9bcf5df405bd2f30277cd0e48106a14d1023c7f6ed6e39b/mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026", size = 14524670, upload-time = "2026-04-21T17:10:30.737Z" }, + { url = "https://files.pythonhosted.org/packages/f3/47/42c122501bff18eaf1e8f457f5c017933452d8acdc52918a9f59f6812955/mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943", size = 13336218, upload-time = "2026-04-21T17:08:44.069Z" }, + { url = "https://files.pythonhosted.org/packages/92/8f/75bbc92f41725fbd585fb17b440b1119b576105df1013622983e18640a93/mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517", size = 13724906, upload-time = "2026-04-21T17:08:01.02Z" }, + { url = "https://files.pythonhosted.org/packages/a1/32/4c49da27a606167391ff0c39aa955707a00edc500572e562f7c36c08a71f/mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15", size = 14726046, upload-time = "2026-04-21T17:11:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/7f/fc/4e354a1bd70216359deb0c9c54847ee6b32ef78dfb09f5131ff99b494078/mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee", size = 14955587, upload-time = "2026-04-21T17:12:16.033Z" }, + { url = "https://files.pythonhosted.org/packages/62/b2/c0f2056e9eb8f08c62cafd9715e4584b89132bdc832fcf85d27d07b5f3e5/mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f", size = 10922681, upload-time = "2026-04-21T17:06:35.842Z" }, + { url = "https://files.pythonhosted.org/packages/e5/14/065e333721f05de8ef683d0aa804c23026bcc287446b61cac657b902ccac/mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330", size = 9830560, upload-time = "2026-04-21T17:07:51.023Z" }, + { url = "https://files.pythonhosted.org/packages/ae/d1/b4ec96b0ecc620a4443570c6e95c867903428cfcde4206518eafdd5880c3/mypy-1.20.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30", size = 14524561, upload-time = "2026-04-21T17:06:27.325Z" }, + { url = "https://files.pythonhosted.org/packages/3a/63/d2c2ff4fa66bc49477d32dfa26e8a167ba803ea6a69c5efb416036909d30/mypy-1.20.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924", size = 13363883, upload-time = "2026-04-21T17:11:11.239Z" }, + { url = "https://files.pythonhosted.org/packages/2a/56/983916806bf4eddeaaa2c9230903c3669c6718552a921154e1c5182c701f/mypy-1.20.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb", size = 13742945, upload-time = "2026-04-21T17:08:34.181Z" }, + { url = "https://files.pythonhosted.org/packages/19/65/0cd9285ab010ee8214c83d67c6b49417c40d86ce46f1aa109457b5a9b8d7/mypy-1.20.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc", size = 14706163, upload-time = "2026-04-21T17:05:15.51Z" }, + { url = "https://files.pythonhosted.org/packages/94/97/48ff3b297cafcc94d185243a9190836fb1b01c1b0918fff64e941e973cc9/mypy-1.20.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558", size = 14938677, upload-time = "2026-04-21T17:05:39.562Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a1/1b4233d255bdd0b38a1f284feeb1c143ca508c19184964e22f8d837ec851/mypy-1.20.2-cp314-cp314-win_amd64.whl", hash = "sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8", size = 11089322, upload-time = "2026-04-21T17:06:44.29Z" }, + { url = "https://files.pythonhosted.org/packages/78/c2/ce7ee2ba36aeb954ba50f18fa25d9c1188578654b97d02a66a15b6f09531/mypy-1.20.2-cp314-cp314-win_arm64.whl", hash = "sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3", size = 10017775, upload-time = "2026-04-21T17:07:20.732Z" }, + { url = "https://files.pythonhosted.org/packages/4e/a1/9d93a7d0b5859af0ead82b4888b46df6c8797e1bc5e1e262a08518c6d48e/mypy-1.20.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609", size = 15549002, upload-time = "2026-04-21T17:08:23.107Z" }, + { url = "https://files.pythonhosted.org/packages/00/d2/09a6a10ee1bf0008f6c144d9676f2ca6a12512151b4e0ad0ff6c4fac5337/mypy-1.20.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2", size = 14401942, upload-time = "2026-04-21T17:07:31.837Z" }, + { url = "https://files.pythonhosted.org/packages/57/da/9594b75c3c019e805250bed3583bdf4443ff9e6ef08f97e39ae308cb06f2/mypy-1.20.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c", size = 15041649, upload-time = "2026-04-21T17:09:34.653Z" }, + { url = "https://files.pythonhosted.org/packages/97/77/f75a65c278e6e8eba2071f7f5a90481891053ecc39878cc444634d892abe/mypy-1.20.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744", size = 15864588, upload-time = "2026-04-21T17:11:44.936Z" }, + { url = "https://files.pythonhosted.org/packages/d7/46/1a4e1c66e96c1a3246ddf5403d122ac9b0a8d2b7e65730b9d6533ba7a6d3/mypy-1.20.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6", size = 16093956, upload-time = "2026-04-21T17:10:17.683Z" }, + { url = "https://files.pythonhosted.org/packages/5a/2c/78a8851264dec38cd736ca5b8bc9380674df0dd0be7792f538916157716c/mypy-1.20.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec", size = 12568661, upload-time = "2026-04-21T17:11:54.473Z" }, + { url = "https://files.pythonhosted.org/packages/83/01/cd7318aa03493322ce275a0e14f4f52b8896335e4e79d4fb8153a7ad2b77/mypy-1.20.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382", size = 10389240, upload-time = "2026-04-21T17:09:42.719Z" }, + { url = "https://files.pythonhosted.org/packages/28/9a/f23c163e25b11074188251b0b5a0342625fc1cdb6af604757174fa9acc9b/mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563", size = 2637314, upload-time = "2026-04-21T17:05:54.5Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/6f/43edf3be2748d0e40974a5ee046a8bb4c9961a4bdd3e7d5c6dcbf32d1af6/mypy_extensions-1.2.0.tar.gz", hash = "sha256:c6e275a07f1c2fc096b62940452d0eaa3f3af7de45d704787866240a7c6d5a51", size = 6566, upload-time = "2026-11-03T16:26:33.692Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/99/7b1adac7e97ce7bbda34792dc6e9b2f5cc5cffd1875883f78a818b2a2687/mypy_extensions-1.2.0-py3-none-any.whl", hash = "sha256:993e7a20aa5671eb41a7132cf555af30062da99729601601b9a8382b11cae768", size = 5114, upload-time = "2026-11-03T16:26:32.731Z" }, +] + +[[package]] +name = "nexus-rpc" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/e5/25be948c92492cfc8ac24a524ad8828fe0cb9f71f46a974d7a37f0794a26/nexus_rpc-1.5.0.tar.gz", hash = "sha256:be2d6934414677bc9dd8e571fb5106c22b8769edc19562c3f49d9988edd24e1c", size = 97404, upload-time = "2026-10-28T17:12:21.82Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/e2/00541e7997f3dfb294f7be3dafea3307ea09b9d3fd332054fe4d30627efc/nexus_rpc-1.5.0-py3-none-any.whl", hash = "sha256:5d6e856972be812573948df02da2257344c407a8eaa99977b5d38be54448305a", size = 30406, upload-time = "2026-10-28T17:12:20.245Z" }, +] + +[[package]] +name = "openai" +version = "3.50.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx2" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/b4/ba46d04d1835d979d89ed7c0d7d94b4ca341b1333e9423c26d97962f6f98/openai-3.50.0.tar.gz", hash = "sha256:ff094ba851ce512e0682f259e8c5bdc4802c7ef5373f68e7fd261261bb4496f2", size = 2411290, upload-time = "2026-11-12T19:26:54.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/68/a1d793a10e8a5fe9d5710544475d686e90ed20848dd224b126e6bc64c1a6/openai-3.50.0-py3-none-any.whl", hash = "sha256:7002115247cacb167154a3da4c668d074e61014a661d2c1ac02583da24e2ce19", size = 2442350, upload-time = "2026-11-12T19:26:52.57Z" }, +] + +[[package]] +name = "opentelemetry-api" +version = "1.45.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/cf3a6b6448849656e1e5f4e2101f3988dc6789724050096aba1bc5ccf592/opentelemetry_api-1.45.2.tar.gz", hash = "sha256:d7b0008e21e5fd61cbe7a7f34ca3b6905a78ab677d9bc395e299359c75cda6b4", size = 73277, upload-time = "2026-10-29T17:29:37.888Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/2d/610db864d7513158cebb8f36cc88e67e73bd6c7aa1905ef18d89e1281c81/opentelemetry_api-1.45.2-py3-none-any.whl", hash = "sha256:2cbd737377e710ee740d261c94e93fa0f08e6d037c18f17513cc0a2d94479a34", size = 60335, upload-time = "2026-10-29T17:29:06.955Z" }, +] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + +[[package]] +name = "pluggy" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/db/7fc19e6f2dc92a966727031389fc2e08b558f0f25eb7403c1119ad4713cd/pluggy-1.7.0.tar.gz", hash = "sha256:d1eaa46ebb595891b860ab086b4d09c8588af65ebd4361b8e8f4bb8920b90ba8", size = 123304, upload-time = "2026-10-15T09:50:58.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl", hash = "sha256:7dd7b0d8832ba3cb632c306926ded123429211b83641b35dc5c41ad2d34f9bec", size = 27082, upload-time = "2026-10-15T09:50:56.808Z" }, +] + +[[package]] +name = "poethepoet" +version = "0.49.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4e/7e/160d391368d359f7dd6dd647575499c395687beb4a6b2ebfbfbf8a20b817/poethepoet-0.49.0.tar.gz", hash = "sha256:256b72c057b5f60341bbe09498212939e558341fac4ab59d0ce478aca04f0d2a", size = 157750, upload-time = "2026-10-15T06:08:57.094Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/f5/a2404b9cb3c299cd148533d07e1a378b60aa4ae640c69fb522596ca157ae/poethepoet-0.49.0-py3-none-any.whl", hash = "sha256:97b456f3c52c9af75d4bc504e0f5cfee44545a84f446ba645a0a8f6a0131422f", size = 195985, upload-time = "2026-10-15T06:08:55.332Z" }, +] + +[[package]] +name = "protobuf" +version = "7.36.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/89/5b8517baa72f84a67b8a307ba953c91057af618bf40bf676f3c03551f8f0/protobuf-7.36.2.tar.gz", hash = "sha256:497d0463ff3316681da6c0b9e8d06cb465d61abce00b613ab42226175644d1bb", size = 512737, upload-time = "2026-09-17T20:07:59.326Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/72/98342feb672507c8f3a69e34b4fa8961f608edba5c1a48a6f47156d92cb5/protobuf-7.36.2-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:cbc70b17ee27e28894c7fee8bb04be1abead49e936bc70eb60052531eee2079e", size = 456039, upload-time = "2026-09-17T20:07:51.542Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ea/91fdf7c2b8bbd49cde056f00a9df6773532987e1c00fe2830b895af95c7e/protobuf-7.36.2-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:e11e1f0180583a2af89db6a2ecd9e8dc40aa6d2988ca175bfd0e6d12ea72d74e", size = 344219, upload-time = "2026-09-17T20:07:52.914Z" }, + { url = "https://files.pythonhosted.org/packages/17/ab/5fd5f8ece73fad885c5a09aa849b32d70472f954ba3a92d3bb5974ea953b/protobuf-7.36.2-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:f4fee11ec330d238b34a05c9b675f693c20415d1c5bd7d5320cc2f8a798eb9cf", size = 357223, upload-time = "2026-09-17T20:07:53.985Z" }, + { url = "https://files.pythonhosted.org/packages/db/f3/3996583dd2906297a637af12114deddf7658af6e683fedb83be061983fb5/protobuf-7.36.2-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:89f23aa53c24553a2416fd4fd1ec06f74fa42b14b546d8883128813f775bbfd2", size = 343223, upload-time = "2026-09-17T20:07:54.931Z" }, + { url = "https://files.pythonhosted.org/packages/fc/1b/dcc64f358fcb51811b58ae40b3d28f820725f116d86487cc20bd4b130701/protobuf-7.36.2-cp310-abi3-win32.whl", hash = "sha256:912c1221170e16c08d1f086762f563dd61ff83c18b5fa6652952dfaded66f728", size = 442998, upload-time = "2026-09-17T20:07:55.826Z" }, + { url = "https://files.pythonhosted.org/packages/8a/55/b77bda4e5e5f5971fb51b07663694690e9afdb9402136c16a522bd621cad/protobuf-7.36.2-cp310-abi3-win_amd64.whl", hash = "sha256:a300819d441e078a5608c0d3c709796bb548136058fda017ae51d425b44fd353", size = 456514, upload-time = "2026-09-17T20:07:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/d52c7016b04b6c5108f26691f9d33ec82a9b65d041f1a9c771137693d618/protobuf-7.36.2-py3-none-any.whl", hash = "sha256:bdb3a345d48db958e6ce1f18e508beb0cc981d64f24088427549c866cd039f1e", size = 179806, upload-time = "2026-09-17T20:07:58.211Z" }, +] + +[[package]] +name = "pycparser" +version = "3.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/27/eeb9f307bf07060f3d9f9aa0fb8c15b01bdfded0a941e4b2719c7f94cacd/pycparser-3.12.tar.gz", hash = "sha256:f03387877eab82ce01bbdfd0ba7252ee55a90b1c708d5129e2ce915475cf1db6", size = 158785, upload-time = "2026-10-22T13:40:03.56Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/ae/315bbeee126a769cbde43cf4908acb4b40408d8bf8bbf334dd7368d8dadf/pycparser-3.12-py3-none-any.whl", hash = "sha256:a076f3f489b39b43ed13563a1dafe42447445bec2e294154ec5c57fcdda4c85e", size = 62600, upload-time = "2026-10-22T13:40:02.021Z" }, +] + +[[package]] +name = "pydantic" +version = "2.14.3" +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/b4/fc/1203d011145c98ddce3cd2f505bb54375ef8d43223053df6bfcf3c746f46/pydantic-2.14.3.tar.gz", hash = "sha256:1054f237f4d4733e5a30e1e2333bd33a2004ab9ab1510bd053a9a31bb48e232d", size = 925636, upload-time = "2026-11-05T09:17:04.686Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/b8/b864503789ef5c58597d2a7b4adbee62c26440ab1e682581fc6838df8947/pydantic-2.14.3-py3-none-any.whl", hash = "sha256:3f7b347782ab7e4cf4c798496ae3a82787d9f21ec12676a9c564355eeef8daa8", size = 494071, upload-time = "2026-11-05T09:17:02.426Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.50.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/0f/34f9ce4ba701dd0b815033ef94d99b56f66200e578ee6052aa3ac26f064e/pydantic_core-2.50.4.tar.gz", hash = "sha256:aead3b74b68396bf0feab5d57e050b730ded7df55853c1e048a87e00aaf033c6", size = 522249, upload-time = "2026-11-05T09:15:26.774Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/82/047d1c7dc4ec15130bbb3299d9e3cb85be945bb14db54e5ad5f1c91b5e75/pydantic_core-2.50.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:83d2beac85b757bfc404c5c0612df51c4ef33bbb7be65cf4a96b4da7cbc4ff4f", size = 2172546, upload-time = "2026-11-05T09:11:15.221Z" }, + { url = "https://files.pythonhosted.org/packages/95/ad/36732c76b5ad87647725681c8259376dddae8db9c3558e7cd99c25c7a9e1/pydantic_core-2.50.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76e0065998159244ff4c097a6c05ff08019c6fbb6e597a85846e73044f0215b3", size = 2131205, upload-time = "2026-11-05T09:11:18.084Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ac/8550a2ac336d3063759a73802f5c045d073125aea44927ba0fa790c55b0d/pydantic_core-2.50.4-cp310-cp310-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:25e7e1080f9f7ce7cd9d114b31470ce912771bd02f38c824721d3c35d312abff", size = 2284431, upload-time = "2026-11-05T09:11:20.005Z" }, + { url = "https://files.pythonhosted.org/packages/f2/d6/1a14b5da27def1db17274c215d4ab62c380b600689c4e54c4dc02c1b0cba/pydantic_core-2.50.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c4c5a1770ecfe8bdb6c6f09f8d81ea4de04f7efb012fac1efa4ad315dccc1fd1", size = 2131058, upload-time = "2026-11-05T09:11:21.561Z" }, + { url = "https://files.pythonhosted.org/packages/01/de/075e68a5dcf8fc64ffad92740512dd9cb81ca7c29c5ca32de89cfbe0447d/pydantic_core-2.50.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:2f6767122b37c780bb756a720a4eef2bc6a050a0a1143428834b3cbc89448bd2", size = 2216460, upload-time = "2026-11-05T09:11:23.546Z" }, + { url = "https://files.pythonhosted.org/packages/0e/ad/b04b1b589495c0678e2a721ad07f95be47dbeb789fb50babc0e8ea7ed964/pydantic_core-2.50.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:86c7c4615634b5c57a79770f5b738354e45ae219a89949aeae84f19169a05283", size = 2403715, upload-time = "2026-11-05T09:11:25.105Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c1/9c8a9f6b42bd6080cde7143b9c3685cfa0859cea4e4860bd126df22602e2/pydantic_core-2.50.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6c7d7b0b596319e9ce79ef96ce57a14054a2eac629c0588a0539187a218be799", size = 2454737, upload-time = "2026-11-05T09:11:26.787Z" }, + { url = "https://files.pythonhosted.org/packages/e3/df/cddc0729c366460763831e6087e88fc0197c78f34fc79ac993a41b5d0242/pydantic_core-2.50.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed9c0b6f236648dbf30d57e9f57e1f74878caafa04833a19f529b5ba5164b7a8", size = 2257210, upload-time = "2026-11-05T09:11:28.805Z" }, + { url = "https://files.pythonhosted.org/packages/7a/54/4526352e8b6a849f661f55f025615eaa0b90c6eccd7588a5eff344c498ae/pydantic_core-2.50.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:dfcf6e51000b34f7026322cd9ea9bc340d745637e27e0bbc22f504b4f6b967ee", size = 2282013, upload-time = "2026-11-05T09:11:30.463Z" }, + { url = "https://files.pythonhosted.org/packages/64/df/6d70c52df400d5af3ccab490a3a3810f6419d99a7a34552dfb2c544de29d/pydantic_core-2.50.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bd46bcc28ed85d3cf71581b42d90b4ebd1f2062f81879519fed209f8a4d1cfc6", size = 2322022, upload-time = "2026-11-05T09:11:32.344Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f5/4ea1184eb9fe6b5f76e18195b10ab73e9d23f896144773d21bf6938442a8/pydantic_core-2.50.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:696613a0c0e7b2b35356e389ba4195ce20cfe7a2fc2e992c87d974d973eabec7", size = 2493416, upload-time = "2026-11-05T09:11:34.204Z" }, + { url = "https://files.pythonhosted.org/packages/6f/dd/918b33fd36c3a27139fb08cfb778b2392ab9e3f091948b4e4b2a325b0913/pydantic_core-2.50.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2980a701be88f9128a7470189fa3bcda5387f76c298a6f8e000fc29eb2a6f4e7", size = 2499741, upload-time = "2026-11-05T09:11:36.054Z" }, + { url = "https://files.pythonhosted.org/packages/7a/9f/dcde466c089e54cb0067a469f54cc8d00a51dd36c6c75eb664a2f1ed07f4/pydantic_core-2.50.4-cp310-cp310-win32.whl", hash = "sha256:dd8bed00731a21e3106e5dd6bbffb6c775a2c0bbc7c0c9face534d830057bdc9", size = 2134504, upload-time = "2026-11-05T09:11:37.693Z" }, + { url = "https://files.pythonhosted.org/packages/ee/d4/d1da421fe867903d5ddffb4d4b4928fd08af99de3e6ec21e758f9990337b/pydantic_core-2.50.4-cp310-cp310-win_amd64.whl", hash = "sha256:e512c75ec5cf4475c1aa50ad975ecdf253f4963b06e152e1a11edd6de4010753", size = 2259729, upload-time = "2026-11-05T09:11:39.567Z" }, + { url = "https://files.pythonhosted.org/packages/df/00/85e80811834d7cbd9bc228f9b5cdf8ec0edd69b55351ad720aac8c412d8c/pydantic_core-2.50.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3e547ceec1da7382f01f9e13963023e53367a71802c88778b21b9645a077cbdf", size = 2170584, upload-time = "2026-11-05T09:11:41.139Z" }, + { url = "https://files.pythonhosted.org/packages/a3/f1/438de1fb99d717dcc6521922478dc16ef7b757302d4fc1f7faba3aaeac09/pydantic_core-2.50.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c813ada7a651193acda85b6bc1cf12abd4b37cd25c7c6a75d7183512cc374969", size = 2128471, upload-time = "2026-11-05T09:11:42.65Z" }, + { url = "https://files.pythonhosted.org/packages/b7/74/f21ab568c6d3f315988b40e53f809c95b63fafc5403332963aa6dfc2c054/pydantic_core-2.50.4-cp311-cp311-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:57fa0a9490785139f1a4e4f43891830d55dfe4408845c0c9cb5eff4aeaf75f69", size = 2281509, upload-time = "2026-11-05T09:11:44.359Z" }, + { url = "https://files.pythonhosted.org/packages/a1/42/24ed3ea8e2fb6463c9ba5b8b1f089e69040905c7fd81dcfa6f52fcc692d5/pydantic_core-2.50.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:30d4dc35bc0eea52202a90b3820445ee0072c820241bfa710a623a078c9b267b", size = 2128064, upload-time = "2026-11-05T09:11:46.01Z" }, + { url = "https://files.pythonhosted.org/packages/d6/d1/44f541b2c18a46df6f18281a557acefeb5bbf0733a0b80b0a5e835e10ee4/pydantic_core-2.50.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:789f88dea57841cdebef392b5d6e90444d767117e9ff2e0d9172df5c9de6690c", size = 2214472, upload-time = "2026-11-05T09:11:47.503Z" }, + { url = "https://files.pythonhosted.org/packages/bd/1b/e9a9ad8733e8051abca9e4acce37ba3ddd13930fd3b8730f4c7816fa979a/pydantic_core-2.50.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:d690121e80ab16e623415351ab24713534d4e500b3cadff216409cb6826063d4", size = 2401153, upload-time = "2026-11-05T09:11:49.403Z" }, + { url = "https://files.pythonhosted.org/packages/79/84/d88e8e6aa42d4d677666d6625db9177ad6851be9e7564a25aeb0b0cbcaf2/pydantic_core-2.50.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b42f64825f5d750228f08318a9bf4d723b7ecb4c472aedcb3013c3ec2dc1f949", size = 2453307, upload-time = "2026-11-05T09:11:51.118Z" }, + { url = "https://files.pythonhosted.org/packages/56/51/cbb7a1bc0196ff94d3478d265eed7449f811759988eb760d41f4edd0893b/pydantic_core-2.50.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0574866e05f6adeecc520d87e87dd06210c0a51706341f6ef8421572f5a64444", size = 2253434, upload-time = "2026-11-05T09:11:52.997Z" }, + { url = "https://files.pythonhosted.org/packages/9e/04/206b4881d631033ae88f005da6e11bd610c850e1850217342c03053d253b/pydantic_core-2.50.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:dcf2c7eaaa4cafe8e49b017e5bb49e76e084ab6f0cd712ee4019c812b5cf68b8", size = 2279365, upload-time = "2026-11-05T09:11:54.822Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7c/d815b5a3f83069b283d3b6a2e61de6e14baac3b20c6cd3418e9e629551cf/pydantic_core-2.50.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3b8ee2589a082739c522c1fe4be227412fcf108b903abb6672ae2f5570558882", size = 2319407, upload-time = "2026-11-05T09:11:56.46Z" }, + { url = "https://files.pythonhosted.org/packages/ee/00/c9a483276d1052d5142e1e01eccac97d939f83a3a3fd94c4766495a9b807/pydantic_core-2.50.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:aec3c2d741a8e3ee993de7f95f37afb51e31b169c95dbd29da4cf6688f340914", size = 2489900, upload-time = "2026-11-05T09:11:58.102Z" }, + { url = "https://files.pythonhosted.org/packages/2d/b5/8cd8ee29a1d63ad47f95593384df92476cef6896c644f280db0748b8e76b/pydantic_core-2.50.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8923fcd49f875e8a0448d7223d1fdf1a9973ae2d1f20d4158753ceb8909facd2", size = 2497732, upload-time = "2026-11-05T09:11:59.928Z" }, + { url = "https://files.pythonhosted.org/packages/60/31/050a48c0c373df16442ac07f3fa167414bb0e22f54a3e886f6d6cb7ad1d4/pydantic_core-2.50.4-cp311-cp311-win32.whl", hash = "sha256:fc04affef30be98c1f36f0c422a4061ef618fdc1dde79f5ad5458dd496fecffa", size = 2132347, upload-time = "2026-11-05T09:12:01.928Z" }, + { url = "https://files.pythonhosted.org/packages/c6/06/cebeeb7a8e87b42a3d8bc189d0d400e47df5d1736cb326764bae8a74c433/pydantic_core-2.50.4-cp311-cp311-win_amd64.whl", hash = "sha256:15bece43a02ad543c621c8305e67dedb5f27d378c5ba741e5a2c36523f8ef37f", size = 2255970, upload-time = "2026-11-05T09:12:03.888Z" }, + { url = "https://files.pythonhosted.org/packages/46/db/5b4bf14347da658c994483043fbabffedcab98929c41c261ec168c3c1f87/pydantic_core-2.50.4-cp311-cp311-win_arm64.whl", hash = "sha256:e498900bfbf7ddddd862f810440c09e9b81674739e8a11d619b40906dccd4a9a", size = 2196799, upload-time = "2026-11-05T09:12:05.389Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/d1f6b81709538f831ebe1c42f50fb89db3dfda06f9e9b1ea24e90fc69209/pydantic_core-2.50.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:32a991aebd165f227b7c7c60bb9d66c2c7de9c24308552da264c9938fde0f8d6", size = 2231444, upload-time = "2026-11-05T09:12:06.969Z" }, + { url = "https://files.pythonhosted.org/packages/c1/39/6618e541658d5a687a8d4962fc304350073a8f322bc4c62e8ed4c2d514d6/pydantic_core-2.50.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e20cbde2b6deb33a51d08cd168454dd9044ec7d4474056f4d98d63f51578783", size = 2108048, upload-time = "2026-11-05T09:12:08.85Z" }, + { url = "https://files.pythonhosted.org/packages/d9/54/1c941fffa4e14bfc9ab0cd4977075f0e03201447856130d08a33cd45e47b/pydantic_core-2.50.4-cp312-cp312-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:8cceae48ae7f94990c2f4e36291e9ec9868735e8b0f19e4489a17b0422338466", size = 2256866, upload-time = "2026-11-05T09:12:10.495Z" }, + { url = "https://files.pythonhosted.org/packages/61/a0/8028ef3e014be522ce43060aeac82a5be2288215e660866f5e6260acc123/pydantic_core-2.50.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:16902a147b3da4840612164a5e67947104d63e978f7a57c395877a5e26690b9c", size = 2125682, upload-time = "2026-11-05T09:12:12.278Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f2/b98bb73b1734ec4e42e786a766abc24f3c89620a0ddbaf8aa14e4b4c628c/pydantic_core-2.50.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:d6e3485a5e5c01fb5339c1ee3d4b9e21e2135a2a9d44caafb71acd994487ee7c", size = 2197108, upload-time = "2026-11-05T09:12:13.873Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d1/7a7142786781400143e8806633fe5b65e2d407c311041fc9494490f05b18/pydantic_core-2.50.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:629eae995185540406e3635df5d5cf1d44f19ea240840b835d3f44541ff694d8", size = 2380521, upload-time = "2026-11-05T09:12:15.539Z" }, + { url = "https://files.pythonhosted.org/packages/b5/34/cf710ca48d4498b04a3ea605d45892675dc2b14a94a25121a8d03f56d3e9/pydantic_core-2.50.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:41b2cd8b29dbb5a4c9828a70f50d1abfc92011115c813213d3a91205c6f731b3", size = 2457436, upload-time = "2026-11-05T09:12:17.447Z" }, + { url = "https://files.pythonhosted.org/packages/91/23/e0c6e3cd09a0ac809953fea698c816b9f03bf0b35b6820c9553e4f5d85a8/pydantic_core-2.50.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c81d98e27d9d9261909dcccab1ba5d6ea1c98b0bb9470e368604614fd1d36ddd", size = 2261727, upload-time = "2026-11-05T09:12:19.097Z" }, + { url = "https://files.pythonhosted.org/packages/f9/00/292a0ed96d5d2d6edb49468ef481336e5b7e54f13435d73b7c912c891c23/pydantic_core-2.50.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:ca83a32b2b41653d89f09a3d22b770bca96da72273bef8f7a68136bdd1e8b823", size = 2262071, upload-time = "2026-11-05T09:12:20.78Z" }, + { url = "https://files.pythonhosted.org/packages/f8/36/d4690faf8c082eacf7754e52ab6bebe1bc73b167272e28699c00480ee908/pydantic_core-2.50.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:817953730e4a73a09008b010c4a42396cefe799616a22df2ac21d3927b3080cf", size = 2298931, upload-time = "2026-11-05T09:12:22.614Z" }, + { url = "https://files.pythonhosted.org/packages/21/17/b180af6d17cf6c8e0d3d2661fc501a4a40959744ca4947d6ad0db81b1416/pydantic_core-2.50.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:664e74848c2f5fc27db8505fe24e1ecd3ac669412913bf73d176916f86cc08e2", size = 2474285, upload-time = "2026-11-05T09:12:24.842Z" }, + { url = "https://files.pythonhosted.org/packages/6e/bb/7e44d4ac6e1292f1859384c688713045bdf5448e0635c49c003d2be3bb6c/pydantic_core-2.50.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:219114f8dcdcbe4720d82ef016b1287ce5a0a67d7c69ca61524eb72e82aac344", size = 2498172, upload-time = "2026-11-05T09:12:26.434Z" }, + { url = "https://files.pythonhosted.org/packages/d8/84/5101f35294579c4bef7dc32fd4f097d91eedac2e195894266bdabc1e6dfd/pydantic_core-2.50.4-cp312-cp312-win32.whl", hash = "sha256:67f7cc397e1d5556a801d1cba74de0d27d827eb242e6c036c0ca273c745414f1", size = 2119592, upload-time = "2026-11-05T09:12:27.971Z" }, + { url = "https://files.pythonhosted.org/packages/82/88/d438a8fcf3738e9ff2670f724047ca3893ec899bcd5cdfc077a2aab133c6/pydantic_core-2.50.4-cp312-cp312-win_amd64.whl", hash = "sha256:ea2f61b0bc12f3592288503961b764579dea0658852d96a0b3d823614d808821", size = 2264064, upload-time = "2026-11-05T09:12:29.538Z" }, + { url = "https://files.pythonhosted.org/packages/77/d4/4a0c73dc3ef9e50b122b8c3527afcded15a44838f1c9668d2d4b72cc09f0/pydantic_core-2.50.4-cp312-cp312-win_arm64.whl", hash = "sha256:32d6dda86383645ec428ea64dfc277036f5cbc60a72238eab28b91a3fdb9a113", size = 2167792, upload-time = "2026-11-05T09:12:31.103Z" }, + { url = "https://files.pythonhosted.org/packages/1b/21/39f8d97e7ea8e3083606ee9bb1d7f3ec70ded780bb6a872aad76c7eb08b2/pydantic_core-2.50.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0f87ae9db5ba8d8de40dcb93982cf8dd544e6552a80d3345af51941cc2146b80", size = 2232841, upload-time = "2026-11-05T09:12:32.617Z" }, + { url = "https://files.pythonhosted.org/packages/90/a9/fc89a632331882c4cb91b949003418e6f5085ddcf2c9b875d499682bca91/pydantic_core-2.50.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1239daaf063e62213ca1f34d96663900bd7510c42a786e843fd328af70e46445", size = 2108326, upload-time = "2026-11-05T09:12:34.419Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6d/4f6d00e9e4ba2a7bfb206ae49cb32a3ea90c4d658e62f8775277d75e8291/pydantic_core-2.50.4-cp313-cp313-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:1afefe44ec042fbabfb4483ea258c04cf75ff4ff328d754f09b321fdaa232aca", size = 2256276, upload-time = "2026-11-05T09:12:36.212Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b8/936cb0d85a4082e902c7397db0f05c4372eac08372c8bd2d0b43088fcf17/pydantic_core-2.50.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a9ee990bf2cfbd81574c5d4f9f6c9d480a14b5b43d2c4f2ca13f4c6f48089394", size = 2125469, upload-time = "2026-11-05T09:12:37.913Z" }, + { url = "https://files.pythonhosted.org/packages/66/a1/f3b551ce233dcac73286b8b2ae45c23232a9f41c68663e2dcce4bb2dbcfa/pydantic_core-2.50.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:ab2822bcdcefb54d1e8ca29f5e3ec3afe160fb794bb3a3a73b09d11ed2671104", size = 2196397, upload-time = "2026-11-05T09:12:39.725Z" }, + { url = "https://files.pythonhosted.org/packages/3d/93/a8a6e7e20826567a73e6f52416667698aa3de704804d7a49d25272246af4/pydantic_core-2.50.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0001d6920e3a5d92758cda460d6dc9f918a78ad6d5d092f1c8b5395e6b775356", size = 2380442, upload-time = "2026-11-05T09:12:41.196Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ba/921d90c9d8ddfa653a35a92f2bbdcd650a9493f5c43f39cdfe4c56659cfa/pydantic_core-2.50.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:ad92d255a165cde5f9882ff69699eaf5495e0a1f32e97566bbbb80f11e46781f", size = 2457747, upload-time = "2026-11-05T09:12:42.723Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/1c8f2c8f1b03d3fe2fdd7962df8f5a744fe48c90b4c49615e4eb77d04141/pydantic_core-2.50.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fb2bb5b557080da398b866408117d98fe9731a12334afd0cdff221b458cdc5dc", size = 2260946, upload-time = "2026-11-05T09:12:44.185Z" }, + { url = "https://files.pythonhosted.org/packages/07/ab/5b78a78f0c0f11c8b54fac214ed49cbb4795c34881aa5e5a15715917936f/pydantic_core-2.50.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:92f8f63df729061bb14558ed740e7a222f6e2aa6c2e6a5bcfb4e706676562049", size = 2261921, upload-time = "2026-11-05T09:12:45.706Z" }, + { url = "https://files.pythonhosted.org/packages/b3/e8/dd5d154cf3401f836d8754fdc1b82ecadd37944e4355d855e5b54c7d8ae5/pydantic_core-2.50.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2a60ec2a57ac3ef3767e526e9bf706ec75aa3d79c4ea134d9d4fcf7c62a297a7", size = 2298475, upload-time = "2026-11-05T09:12:47.183Z" }, + { url = "https://files.pythonhosted.org/packages/b7/49/af059d9c4483cba168b17e5f84f1090a3e4547a02fc7698ebc231d48298a/pydantic_core-2.50.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a2f8716344bbbd10443391be0b1f2003b34949e3c4e6883005c2bc498ab6b64b", size = 2473226, upload-time = "2026-11-05T09:12:48.785Z" }, + { url = "https://files.pythonhosted.org/packages/71/ba/4e8cc3064e8bc2ea272cf80dfc57fa4e455f6835c1def21609a9b4f94135/pydantic_core-2.50.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:b300a30304c4c6aeab03ed06809819d9eceacdd58441d3c7dffe41edfd006091", size = 2498764, upload-time = "2026-11-05T09:12:50.316Z" }, + { url = "https://files.pythonhosted.org/packages/bf/96/0878499cfa88eb0b07762b488d3cc1b64cb156a4905db99a0d12096b2e17/pydantic_core-2.50.4-cp313-cp313-win32.whl", hash = "sha256:ef8a9ddb2e2a4eae4abb72dc0710eca6c2726779b0664bd10c3ce9fcfaed8c4e", size = 2119158, upload-time = "2026-11-05T09:12:51.834Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e0/95b651213e79ffe0cbee855590bf5cfdf8e3a0d90a7fc73a080e63f7cc38/pydantic_core-2.50.4-cp313-cp313-win_amd64.whl", hash = "sha256:4c29b8f2d6417069bb1f257e6b9113f5d64dc2f554cc200ea90b4fe2c5cdb733", size = 2262887, upload-time = "2026-11-05T09:12:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/08/d3/c3801dc75844a0b13d000bd575bf1b3b3a8aca9f63d19e1809f69c9c5121/pydantic_core-2.50.4-cp313-cp313-win_arm64.whl", hash = "sha256:a0c5210d038418a115e7cf8706bfa611bd14add2f5787274dba45aadca3155e0", size = 2167878, upload-time = "2026-11-05T09:12:54.677Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ed/c9a4982ac3bbc78f27498d9efb85b3dabd739a1724db53c35cc4c018e1de/pydantic_core-2.50.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5c074454dc6d4f6f4ba6561a56bf33a14db7651d882a5a5321ba3ae8868e082d", size = 2236841, upload-time = "2026-11-05T09:12:56.111Z" }, + { url = "https://files.pythonhosted.org/packages/56/32/92c174c17bfc5fc52d0c064e6a7969a727731e25829ede752fba8bacce0f/pydantic_core-2.50.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a4bb174bd65f795c39ad6642d9d5bdc43351b306b7d0a654ab0d0abb38be245f", size = 2108348, upload-time = "2026-11-05T09:12:57.638Z" }, + { url = "https://files.pythonhosted.org/packages/cb/cb/9f637d424a04d3edce7dd527da941950cce05a74b025eef3d54427b86c8e/pydantic_core-2.50.4-cp314-cp314-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:0ec20aca2b067369913716847f7dfe5f4dddf16a46965b7a70bbad5defaaba86", size = 2261331, upload-time = "2026-11-05T09:12:59.154Z" }, + { url = "https://files.pythonhosted.org/packages/91/ff/1117a3cccabb67305bd98d992a84eaf275939a9a043b4f14126ed7252e4f/pydantic_core-2.50.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:70dbc9f83c41f37a4faa62dae5203853847223a891da446f48ef2efcfd5df806", size = 2128667, upload-time = "2026-11-05T09:13:00.614Z" }, + { url = "https://files.pythonhosted.org/packages/93/45/6de1b4a6e8c415cd547f90d1408a711e7975116423e3eb3046061c62d7c7/pydantic_core-2.50.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:3de6c2a4df2af0ac9ab4dc1488746fe8b1a90e27955d870d9edda3187d20092b", size = 2198183, upload-time = "2026-11-05T09:13:02.259Z" }, + { url = "https://files.pythonhosted.org/packages/d0/bd/c6e64c315d35cc0783a0ea033a3dca062bc368905d0217eab6f6f63007de/pydantic_core-2.50.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3a48a5254d15a0ad050318bfc7a9566d4ec740118187e8f303e47a6840287eb7", size = 2386279, upload-time = "2026-11-05T09:13:03.988Z" }, + { url = "https://files.pythonhosted.org/packages/33/ab/0d56280c89d3841381e9c699c3fa80e3dd3288edf5e7fe4d149136835dca/pydantic_core-2.50.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:0af56dbf8822ba9f4efcbbfdc15478f8b6fb08f1609860cb50aa2cc8476b3c34", size = 2464343, upload-time = "2026-11-05T09:13:05.98Z" }, + { url = "https://files.pythonhosted.org/packages/56/9c/621ad8f88590a92a086b1168dce09369d9c5e079e2e635e6646d248af55b/pydantic_core-2.50.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f1d62fb172db35c6478c9323da34ff37d1f3d37d7d44337a9d94ba8c583e5730", size = 2265388, upload-time = "2026-11-05T09:13:07.808Z" }, + { url = "https://files.pythonhosted.org/packages/f3/7b/cef68a667bf29b86e293a80a601d488a84c8f45508915ad6201fcfe1a798/pydantic_core-2.50.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:5c1013647be741647895c102831dcf79c34209662dbb683df9efb864417ed7d5", size = 2267012, upload-time = "2026-11-05T09:13:09.273Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9f/3ae4d163d8feda2b52de88b5cd96b1c40f79d11503003b061136d2ece6ab/pydantic_core-2.50.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:aeb2e123cbf978a905e9f55155c1b8316fbbfa7cf64d04521fe174bab302d100", size = 2300478, upload-time = "2026-11-05T09:13:10.758Z" }, + { url = "https://files.pythonhosted.org/packages/e8/72/41881d31f6452430ed6792679c5391795910f0a27a6aebd424683e504ed6/pydantic_core-2.50.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:523466eba8ee10f7480f3c2057563145978011c583130d2cc01fb1e7af808e25", size = 2474874, upload-time = "2026-11-05T09:13:12.188Z" }, + { url = "https://files.pythonhosted.org/packages/05/ef/099874dd6d5ca1e49987dea87eecc05610183249450b1fff883d87f1e8eb/pydantic_core-2.50.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:c9e70012926e97069cdd09221861aff412aa94e4cac2e019055cdb79485b150c", size = 2502771, upload-time = "2026-11-05T09:13:13.981Z" }, + { url = "https://files.pythonhosted.org/packages/c6/07/a7d8615b8677bf69e24666bbce431299850c95a7b084649342582a1c3210/pydantic_core-2.50.4-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:e6da138a1ecc1341c8b788a5ee822670b03788e1f5decf7af9d461de6e508b8e", size = 1496509, upload-time = "2026-11-05T09:13:15.704Z" }, + { url = "https://files.pythonhosted.org/packages/ab/71/fdf2c25c3a4f89968263c1d896c933809ca8d2fb13443626648a17b2e296/pydantic_core-2.50.4-cp314-cp314-win32.whl", hash = "sha256:13b6dd62b5279f5b9d72f3bf918a35443ee8bd549241d9dc52d79a2c3b6d7016", size = 2122015, upload-time = "2026-11-05T09:13:17.09Z" }, + { url = "https://files.pythonhosted.org/packages/3a/9f/3554f0148b01ddaa5e678bf5fbce4679873534e5c0985cdf6c59c41dc9a9/pydantic_core-2.50.4-cp314-cp314-win_amd64.whl", hash = "sha256:b0cdb655fbd1562c2dfc9986f9103a106f7058cd0915afc6e656d4b1d73f3387", size = 2265109, upload-time = "2026-11-05T09:13:18.702Z" }, + { url = "https://files.pythonhosted.org/packages/ed/3c/196ef237d9ec5cca5ea9b773911dee1a387b9b1d4bfea05625b3e191a338/pydantic_core-2.50.4-cp314-cp314-win_arm64.whl", hash = "sha256:df1c174796cd63eb6b139f7daf783affe1f3ed659605850e843b24764c21bfc5", size = 2165745, upload-time = "2026-11-05T09:13:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/36/12/0ee82a41a3995f0b531cf14e0b823dc2365ba4a22330670fccaa72753f12/pydantic_core-2.50.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:edbca8789fd10f0de534b660a6b4c71f1fd18039679c918dad392ab044bb37e4", size = 2223917, upload-time = "2026-11-05T09:13:21.623Z" }, + { url = "https://files.pythonhosted.org/packages/90/2c/592f8404abbcc65a70e6a802d1dc8f11412af44913e5146cf5c345370074/pydantic_core-2.50.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:33b21f69540a12c2053cd1d95766d8bd9667a3897f7c712a642de8aa37eb0865", size = 2087372, upload-time = "2026-11-05T09:13:23.16Z" }, + { url = "https://files.pythonhosted.org/packages/ea/1d/c7111757c84b5b8c19b1e39ceb5e131e6e48e14169ec6004e0f8f28f43f3/pydantic_core-2.50.4-cp314-cp314t-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:ffb616d8d55965839670301dc3bae3abacff560a7e137a89056058a8a9da4d96", size = 2275190, upload-time = "2026-11-05T09:13:24.634Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ae/6c49cc9b8e66e61adee782d1763894bf64a357cddae82f725059b85bdc98/pydantic_core-2.50.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6c5687b59b6ba0f663c4f2f15470f68507d6b45ccd3a19ee6d224f95cbee70d8", size = 2110343, upload-time = "2026-11-05T09:13:26.168Z" }, + { url = "https://files.pythonhosted.org/packages/ed/39/0ee5261d1daa9ad526371181c13696add0565251b2823f6e00a9a9af397d/pydantic_core-2.50.4-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:c733087ecf7dde06b1d071679bfd5f3ba03266ba77814c1c7253bc48c9adedd9", size = 2207887, upload-time = "2026-11-05T09:13:27.681Z" }, + { url = "https://files.pythonhosted.org/packages/ac/b5/a0652c0e99590107e111088b6d059044380a787da9ffdd0b458ab4cd9453/pydantic_core-2.50.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94733cb447c37b0ce6f71b39ca1288d5f966a084023a5617b4cc35a852586859", size = 2404171, upload-time = "2026-11-05T09:13:29.864Z" }, + { url = "https://files.pythonhosted.org/packages/a3/85/d49f0b8e09a0ffc40dcefb604f9e6020e0d61b51a5f64a504f382fa84c39/pydantic_core-2.50.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b26f39a5be3cd91a2035818be3d0ffdf5282c3e1f5f53d3b80b7024021acee52", size = 2466049, upload-time = "2026-11-05T09:13:31.658Z" }, + { url = "https://files.pythonhosted.org/packages/e6/76/f2b04d7c53edfd8136053c812ea49914fbf4091196cb7baf9ea326fa2ff8/pydantic_core-2.50.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c856e439a9d54d514e81a7131229ba151cdf81df7c408e0f6c89340e5003dfe2", size = 2262062, upload-time = "2026-11-05T09:13:33.806Z" }, + { url = "https://files.pythonhosted.org/packages/24/60/016f14cea883adc33df90b8df220df934de49ded9640f45c8e92bb298494/pydantic_core-2.50.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:cbf0777d54672990b10cb8ca9b0523499e182d7e2ea39f86b138519a902aa601", size = 2263713, upload-time = "2026-11-05T09:13:35.337Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3f/b05828e978512973e7e215bab69e65e70d223d6309a17bf48084a50692f4/pydantic_core-2.50.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:edc17549ab592e33cfcaf285f047bb75fcd98f608aa9309af1187fd736ce9641", size = 2305044, upload-time = "2026-11-05T09:13:37.043Z" }, + { url = "https://files.pythonhosted.org/packages/91/c1/fca6c5f623ac45ba284713c614b30173aa0794899a0a457f866fa6f5e886/pydantic_core-2.50.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:23a9ba564b2db709ff16eca990992ba95a19069972b34bff87c81d4b473cd755", size = 2483506, upload-time = "2026-11-05T09:13:40.524Z" }, + { url = "https://files.pythonhosted.org/packages/94/c2/ec41f68d1208e53e1a73ead940c050c4316bbb555ba42676a5e949fd529e/pydantic_core-2.50.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:41f4f8ae714e4fea1d635880f8a1af715ecf080eed4d13c5b04a0f3210f9c61a", size = 2512832, upload-time = "2026-11-05T09:13:43.361Z" }, + { url = "https://files.pythonhosted.org/packages/43/22/58583b86309f70cce854e0e77c412839a56ac6c682d6060f5794afeff315/pydantic_core-2.50.4-cp314-cp314t-win32.whl", hash = "sha256:601fce6cea9ddeaa85329e12e7f351def38d233075f42a3012b9c48071b02941", size = 2101977, upload-time = "2026-11-05T09:13:45.735Z" }, + { url = "https://files.pythonhosted.org/packages/6b/28/193bc0994f7fe15f2874c1dfe2f3bc7872aed8283ffda2cf05fb9a5a06b4/pydantic_core-2.50.4-cp314-cp314t-win_amd64.whl", hash = "sha256:22e874666bdbd96d6bf629d32cd8ab376fedf1fd62585b52a42b9fa9fa6c20b6", size = 2240461, upload-time = "2026-11-05T09:13:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/00/ed/5615cfdd5620a4926440eb1a2b73db0dc895824ba565ccaa8d9eb3a8e974/pydantic_core-2.50.4-cp314-cp314t-win_arm64.whl", hash = "sha256:71395a43da5584bb70556f380387e15f845c0dcf76373c1afbfa0025737576ac", size = 2168505, upload-time = "2026-11-05T09:13:50.762Z" }, + { url = "https://files.pythonhosted.org/packages/6d/9f/e6673535165d965a11c1d5339f53995f90234019827eaecb41a57e6e463d/pydantic_core-2.50.4-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:7ac2769a7ddd661e07e26c12220adc31b17014c48234b2a5a88993b8d4109e09", size = 2241950, upload-time = "2026-11-05T09:13:53.055Z" }, + { url = "https://files.pythonhosted.org/packages/6f/02/65951efadbf7826e6a270843f97e3da2ef726de2b6d9cd7c5dfa4c48807e/pydantic_core-2.50.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:930ad463d3fb7a340d323bcc496ec32dae469ec9974c29ff569f19806b6b03b4", size = 2112412, upload-time = "2026-11-05T09:13:55.309Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d3/4fb6e2e4a9e4951f1e05b198366ed5330e0874e83d52077e8b985350fe0e/pydantic_core-2.50.4-cp315-cp315-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:b867f9fd183d76977f810c626d539265183c25b2284762893a31e8d6cacc81c0", size = 2263220, upload-time = "2026-11-05T09:13:57.555Z" }, + { url = "https://files.pythonhosted.org/packages/63/83/42e70436c523357b49393b73f161f2741acf36003c8efd2f941d2be56bae/pydantic_core-2.50.4-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49eb1241a969a5994db6880c18c012b6cbe2340343d8f529721571806583e500", size = 2134716, upload-time = "2026-11-05T09:13:59.413Z" }, + { url = "https://files.pythonhosted.org/packages/36/66/ed9b87f4751fd07e472c62cfe151f575abdf4c452fab943d35ba8efdc6c4/pydantic_core-2.50.4-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:477af785d1846e4e96bf56983b92ea7553aaeccdeb8acdaa55c7cee9eda0ae32", size = 2201901, upload-time = "2026-11-05T09:14:01.472Z" }, + { url = "https://files.pythonhosted.org/packages/2b/21/ff2e6ac7202b6d326e9f007c6082f6e6f3aaa063790ea2a355bdc985addc/pydantic_core-2.50.4-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:29358a8ca74e7d81158d14e2b10c904bd18773293ae3788716c0626fe0342347", size = 2389670, upload-time = "2026-11-05T09:14:03.431Z" }, + { url = "https://files.pythonhosted.org/packages/b9/34/e9348bc13d549641791a51843ed140fbb311d9066628e27477343412d5ad/pydantic_core-2.50.4-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:300753f71c73302e218124baf995f00058e085cd01869bd9610bcbd0bd4500c1", size = 2468951, upload-time = "2026-11-05T09:14:05.028Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5e/145a02bf2b9f66981108caf6cc9e78917e31e4caca14d1e3ccd3ff2c7293/pydantic_core-2.50.4-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:26cb012c4b8ca9f847bd0ac15978bd3d0f55b050bfbe6c47355edfc7e945029b", size = 2272957, upload-time = "2026-11-05T09:14:06.587Z" }, + { url = "https://files.pythonhosted.org/packages/fd/38/b71c5b245e923f6cf144e948ed2a9d6b7fb97d8905784db8d860f5176b8b/pydantic_core-2.50.4-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:9be1cfe715ecaa060ffd5d599ef410b94ef2697a0eed4cee562f788a2aa2175b", size = 2275028, upload-time = "2026-11-05T09:14:08.219Z" }, + { url = "https://files.pythonhosted.org/packages/ba/71/2504dacdc5cd3ea3ec5e68c9b6e097c979500bcff220b97046cc8ea3c1ad/pydantic_core-2.50.4-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:c81e35862aced259177a537ce1a6264c3a97bae1ced184ca9dc10ea754563688", size = 2305447, upload-time = "2026-11-05T09:14:09.795Z" }, + { url = "https://files.pythonhosted.org/packages/2c/90/904233a6c148bcac9f30bb96a2761e5fb52682d0d6de31a40c74a3714ceb/pydantic_core-2.50.4-cp315-cp315-musllinux_1_1_armv7l.whl", hash = "sha256:b2f2d13239487cecaae98c9f4d5904ad0737643cdf8873c88ec6a78b12214f20", size = 2477852, upload-time = "2026-11-05T09:14:11.443Z" }, + { url = "https://files.pythonhosted.org/packages/72/33/1cbdcbf343ca23b507e967b21dddc4c8bf17a74c8b5cad59228c85ba4efc/pydantic_core-2.50.4-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:e6b4b08d09054310a4dc389fa61ee22a7a5a292cb23ab8a985136ca8828bf924", size = 2506352, upload-time = "2026-11-05T09:14:13.165Z" }, + { url = "https://files.pythonhosted.org/packages/91/24/3ad9ea0876e6624c90967e5f1446e751b275cfdfaaf798a0d8e0b30025bc/pydantic_core-2.50.4-cp315-cp315-win32.whl", hash = "sha256:6619d49a89a15cd04334f76fec530c155923bfc60da74ff5d815a52629e1aa6c", size = 2127780, upload-time = "2026-11-05T09:14:17.52Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3c/0582532b90a12492471977bdbca1317eca5144f6342ea581d2cee73ccd35/pydantic_core-2.50.4-cp315-cp315-win_amd64.whl", hash = "sha256:e5bd109d095aaae989a0463d5ef0fcc43e9ddd5de125e656d3c119a79e466380", size = 2270596, upload-time = "2026-11-05T09:14:20.955Z" }, + { url = "https://files.pythonhosted.org/packages/6c/50/967cfe4f7043a993c0c80cd3563c598b5281d2276aa775d0f6ff7f714271/pydantic_core-2.50.4-cp315-cp315-win_arm64.whl", hash = "sha256:6118e5a5550e36582e19bac1a403711a748f9f18251b054c326d3db02aaa7a18", size = 2170880, upload-time = "2026-11-05T09:14:24.822Z" }, + { url = "https://files.pythonhosted.org/packages/9e/71/3ec5bd8a82f792482f16d79fdbe8ce85aa71bf7147e556c5fc5f8546818d/pydantic_core-2.50.4-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:d8201c9f9035520cdcc7dcad383b039ec8123969b87e01cf27c3dd883c02313b", size = 2233053, upload-time = "2026-11-05T09:14:27.942Z" }, + { url = "https://files.pythonhosted.org/packages/6e/2e/614dc37aedf8e9f811c1f2eedaf8eb09a7c196e2d137fe82bcc9d5662ca2/pydantic_core-2.50.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:1de7be9a66fc8536770481ab64a05aae5067c725f2b29a09543af00e6a417b97", size = 2090776, upload-time = "2026-11-05T09:14:30.281Z" }, + { url = "https://files.pythonhosted.org/packages/f9/14/aefc14e3e05013070ee959fc0b8703f6860a2776ed6e75b9c748f61aced0/pydantic_core-2.50.4-cp315-cp315t-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:789a6453e5ffd18a7ab6b8c3a0dd8403053829197ec258ecb90c604c21ffcc99", size = 2272481, upload-time = "2026-11-05T09:14:32.578Z" }, + { url = "https://files.pythonhosted.org/packages/13/c9/d56999aa7236dc9303091db2b1ef7303f0b022baf25ebf33cd200c0cb933/pydantic_core-2.50.4-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6348ad12fa741bcc9c2f173e99fbf27cbc038826d8c31a4a7d836eca27878ba2", size = 2113069, upload-time = "2026-11-05T09:14:34.69Z" }, + { url = "https://files.pythonhosted.org/packages/b7/d5/a62da9e72f272f198f6118c5457332bbd4b9fdc4b77858448120569756de/pydantic_core-2.50.4-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:d49127f137f871cde3c5650af33bbbdcee02679dae76e020902fa106f522b8ae", size = 2202754, upload-time = "2026-11-05T09:14:36.49Z" }, + { url = "https://files.pythonhosted.org/packages/37/4a/e7f21577f96e42ac3082cd014df398c1c913d482afac1c8ef17b9e039198/pydantic_core-2.50.4-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:2f5cc5bcb94063fbbdfd285a2b302da34af07491337c10bb35de1d5220fa1583", size = 2411472, upload-time = "2026-11-05T09:14:38.63Z" }, + { url = "https://files.pythonhosted.org/packages/38/ad/c57c5e13bbfd3404ec1af519764aadbc52ee01e39f9f9ae9d28317f982f2/pydantic_core-2.50.4-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b99bed995ed7c41515c11fe2beb86f8a6cc298e5537cae2f736de2f982f74ef0", size = 2475330, upload-time = "2026-11-05T09:14:40.519Z" }, + { url = "https://files.pythonhosted.org/packages/6e/63/65cf0e1f1fd8cfaafee3c005712b8020e063b0f3e3571493ea68db5ec44f/pydantic_core-2.50.4-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:faa4e96385c820ce9d7f7a623486e23d4134ae3c9ba2071e44e0ab5a5a5002c4", size = 2265718, upload-time = "2026-11-05T09:14:42.379Z" }, + { url = "https://files.pythonhosted.org/packages/72/5d/fd9fdec9b25e2ef1f788970a84f749f0e28f166573c87654fd5387d8e5ce/pydantic_core-2.50.4-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:d6a166978f22f10cb91413f70519861605219346decf65bc79008f9257c94afe", size = 2273454, upload-time = "2026-11-05T09:14:44.286Z" }, + { url = "https://files.pythonhosted.org/packages/34/8c/19705c6d5eb72ff8c25006df8a375da7466176a5968781789e5415384f65/pydantic_core-2.50.4-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:a70d5effebd81ebd8e15a8aabdc7980832367b2b00c36543bac378977004cc80", size = 2314862, upload-time = "2026-11-05T09:14:45.97Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fb/acefa27031aba2646b0d3026c4a1602e526489d39395ab17bc47f03234e3/pydantic_core-2.50.4-cp315-cp315t-musllinux_1_1_armv7l.whl", hash = "sha256:f8fd23653a252cefabea11cd041efd7b80bdf403e43a36dea53f9cb0aaa21f0f", size = 2479073, upload-time = "2026-11-05T09:14:47.526Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e1/db08fa1e5edf1779fa0612f8d09f9030bdc513f593687c883cb781b86caa/pydantic_core-2.50.4-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:7646353a5a7c553c04167aca628e2550b538f492765b03ccf34321a560acc43c", size = 2521328, upload-time = "2026-11-05T09:14:49.042Z" }, + { url = "https://files.pythonhosted.org/packages/0d/51/bf1ec5b96c076d8d514b08f3bcffb3397df7a0fcc729eb960838a8bbe27f/pydantic_core-2.50.4-cp315-cp315t-win32.whl", hash = "sha256:65376dbe1ab4dbc768ca2b72b7c13993562c035f5925fd3bc7ff9c8c585c515d", size = 2107629, upload-time = "2026-11-05T09:14:50.891Z" }, + { url = "https://files.pythonhosted.org/packages/18/32/43d5db1824ec2314f737ff8d9962ff59954a74616d8656a8cc78790e45f1/pydantic_core-2.50.4-cp315-cp315t-win_amd64.whl", hash = "sha256:f08ee8248aad2d054e5bc4c73554d4505464698f6d754f16198dbc5764e9821d", size = 2244906, upload-time = "2026-11-05T09:14:53.056Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c0/38c505004969c4641c3a077a8e72e354f02d5c06d8f51d7a103699704772/pydantic_core-2.50.4-cp315-cp315t-win_arm64.whl", hash = "sha256:da779ae50b46f3f7d1e1b5f362fdc0ad376a49fd55f6f3122dc2b6149936cdf7", size = 2179018, upload-time = "2026-11-05T09:14:54.448Z" }, + { url = "https://files.pythonhosted.org/packages/9a/18/2f6a25e9104d5af713d16671711927601da1f0db74e785be67bd590385c9/pydantic_core-2.50.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:c04e9d920e2674f54651fd260a15c5d2843526c73d5242ee4311325ddc9573b3", size = 2212591, upload-time = "2026-11-05T09:14:56.262Z" }, + { url = "https://files.pythonhosted.org/packages/4c/1b/ba8b721af10a7659e7fbbef320ff8eb55462557a72607858af5a9bd22cb1/pydantic_core-2.50.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:5bc1811ef2b4d4ba63e8e452f6e332b6374bd1d2cbbfdf66826c8cb1f71dd10d", size = 2048167, upload-time = "2026-11-05T09:14:57.681Z" }, + { url = "https://files.pythonhosted.org/packages/48/42/3f8638e7d90bdd9a7c7f3e5084723af59e7f43f15a292e242d35624073ce/pydantic_core-2.50.4-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b059a7968b1911be5a2d9a8234827ab0bef9d1e465d4b42da525894143550e8", size = 2119548, upload-time = "2026-11-05T09:14:59.317Z" }, + { url = "https://files.pythonhosted.org/packages/e0/8d/4bfbacaf191f7939893d165e18eb301731fa3366c59a6e57336c4f168fbd/pydantic_core-2.50.4-graalpy312-graalpy250_312_native-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bee28aab5a580eec673f7b68dfa8dece3dd37bd207ef8d22fafe23a8e9837296", size = 2279952, upload-time = "2026-11-05T09:15:00.907Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3a/d405a50e02ea81cd95e93fc97f43d351eeaa1707c28aa6fb58a9f1c1efa9/pydantic_core-2.50.4-graalpy313-graalpy253_313_native-macosx_11_0_arm64.whl", hash = "sha256:71e03d2ea1a221d020cfc5b6cc8bbbedef23a06704c70fcf4d0afb42965b22d1", size = 2049324, upload-time = "2026-11-05T09:15:02.379Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3b/03e7992115e17cae4131ecd71aadb8fd5e4a04cc2aead72c3c27e9b453d1/pydantic_core-2.50.4-graalpy313-graalpy253_313_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09450778e3ab58d3f565899fcfbf6c82926e31af29e28e799d1d50941316e0cb", size = 2119802, upload-time = "2026-11-05T09:15:03.901Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d5/b76fc647e473377ca6e693c21549ee3a4a7677e4962c28ae4659337c5114/pydantic_core-2.50.4-graalpy313-graalpy253_313_native-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4eb15be588407148ef5937dc8d9256a71f6b115e6cddadb7aa20b6ee384680dc", size = 2280639, upload-time = "2026-11-05T09:15:05.374Z" }, + { url = "https://files.pythonhosted.org/packages/d8/18/3ca5b3b8ce7a540305e6856d013e9c6b9c0fef93258fef6038db0beb233a/pydantic_core-2.50.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:39f489ce31272073e6e146f6781690f9c1ee8c126836e6403361ef91154d89fe", size = 2166960, upload-time = "2026-11-05T09:15:06.902Z" }, + { url = "https://files.pythonhosted.org/packages/a3/60/65d725001bffcde0170f6d37ae8bccce715e5311122eb6127f698115957e/pydantic_core-2.50.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b26e5f1793b96cdc60242bf38da86ff24a27a708aa396214cac9b534bf571c7", size = 2085636, upload-time = "2026-11-05T09:15:08.395Z" }, + { url = "https://files.pythonhosted.org/packages/ac/12/934cf1d44a3fee3c47231d4690dddee19264150c3aa59a3ddf66fa43454d/pydantic_core-2.50.4-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:7210398a1607ad607dc73196e575b3148b00a333cbb602ddbe1b60ba54554f43", size = 2284621, upload-time = "2026-11-05T09:15:09.82Z" }, + { url = "https://files.pythonhosted.org/packages/c4/91/52248eee98d76f8d9239166d24928f805292df16729109f4f189cdb59be4/pydantic_core-2.50.4-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2f42584e5785385f681163a42506cab0d51295e17599a648396c52a7046c1372", size = 2277673, upload-time = "2026-11-05T09:15:11.236Z" }, + { url = "https://files.pythonhosted.org/packages/9f/89/900a7970ef19843aa79522a95b461e9c365b7fedd7ee80ef28a84dae9a41/pydantic_core-2.50.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2039e323acee9abfec1fb5f661776e8965fe5928f3b7f8431900a774cba5c317", size = 2297354, upload-time = "2026-11-05T09:15:12.798Z" }, + { url = "https://files.pythonhosted.org/packages/de/ec/053d829437f70070cdd7116a0257cc0da651103328ff1e9e82954c45efd6/pydantic_core-2.50.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:71ade6d2e445839350d589ff99bbddfde16f3f32db28da5c894a09f99c8b2a03", size = 2469913, upload-time = "2026-11-05T09:15:14.319Z" }, + { url = "https://files.pythonhosted.org/packages/80/af/207c821cb0813e16d630660e0073491e3ab07370efaa6fa0b8b10fffb7b6/pydantic_core-2.50.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d7d406709ae924cf0b08b8282ca2f0e1c90526911d93bc040557cbc83b74c55f", size = 2503746, upload-time = "2026-11-05T09:15:15.736Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ca/8a053f24e22d8150349cabaecb2f35c89dcf07fbaadb5e48295184dd0e5b/pydantic_core-2.50.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7472c39480ccc11dda69a196d1b7c7a6b48eb6594b77f05d945599d3e47f731c", size = 2333540, upload-time = "2026-11-05T09:15:17.883Z" }, + { url = "https://files.pythonhosted.org/packages/ac/9d/41b374c62cdb87f84081e2ef2963a115eacc6284a26dbc96bb83568e83ec/pydantic_core-2.50.4-pp311-pypy311_pp80-macosx_10_12_x86_64.whl", hash = "sha256:b67d38e8bb0ade7e6e84575e4bb153fab35b122458077273aeb08fdf12be9d2c", size = 2166962, upload-time = "2026-11-05T09:15:20.276Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4f/51ef440ee346d4388a2c4a3ac4b271fcc7b1d3ff68be8da2ed2be4d488b0/pydantic_core-2.50.4-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:4eda7e3fdef382b7d4492745988a5baaf6953923a337f8b9dde5f76fa8afde20", size = 2085634, upload-time = "2026-11-05T09:15:21.953Z" }, + { url = "https://files.pythonhosted.org/packages/4b/5f/c9ed62b96beb9050db221d29e81856b6e28f277db601f8b0c3a08f8cf913/pydantic_core-2.50.4-pp311-pypy311_pp80-manylinux_2_28_x86_64.whl", hash = "sha256:474f7e598c7902165662a5d5160d3fce31037890a2975a9871ebf7c05862300b", size = 2277804, upload-time = "2026-11-05T09:15:23.836Z" }, + { url = "https://files.pythonhosted.org/packages/63/bd/b912a5e251ef10ed7be5e543a2bfbeebb0c79bf6302bee786f8dc3bf4341/pydantic_core-2.50.4-pp311-pypy311_pp80-win_amd64.whl", hash = "sha256:3e0b395648c6cb871bceb2906907f255dc876ec425f2d5b024e129985cea8519", size = 2333539, upload-time = "2026-11-05T09:15:25.354Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.15.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/ea/5194e52748b0da83d71e082d75496eaec6e58f419f5e184786ded517e6a9/pyjwt-2.15.1.tar.gz", hash = "sha256:4f259e80cdfb6b3fc18a7de51fd1ef9ec79652f25019bae68975ca2468a34df8", size = 121252, upload-time = "2026-09-28T18:40:42.598Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/ca/44de4e75f8aadc457f0634be3b542815078ded46dca30efb960edeecad6e/pyjwt-2.15.1-py3-none-any.whl", hash = "sha256:42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193", size = 33860, upload-time = "2026-09-28T18:40:41.429Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + +[[package]] +name = "pytest" +version = "9.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/42/bb4641652e58dedeac2ec51abc1f8cae4fee4ec63899a0999016229a0f2a/pytest-9.2.0.tar.gz", hash = "sha256:97c4f3b1f3c7414aa36465e09b8220f1d03627993bd51b1282e193753c80557b", size = 1797555, upload-time = "2026-10-27T21:30:20.728Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/39/1f5d8c8877a0fa327db959c19341d4be61660de1ab6b7391f9eb805bcd67/pytest-9.2.0-py3-none-any.whl", hash = "sha256:07acdcf88ec174f907dc01af70bf52d49b014bbab03faeeade31c96b18c2e588", size = 411776, upload-time = "2026-10-27T21:30:19.131Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2c/de/80724f9b5b9eca1a654179cae6edc51aaa0a355a9834a736e2a188b91780/pytest_asyncio-1.5.0.tar.gz", hash = "sha256:92c8fa83ca419fd0d4bd6a34df6520dae435841d3733c319343249492adf37f7", size = 146227, upload-time = "2026-11-12T16:35:49.845Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/2d/e600d8f47597a803641704aba367c7266cd621f86561ec36da9888f160ec/pytest_asyncio-1.5.0-py3-none-any.whl", hash = "sha256:557f255ee03cc84f84a8d5f438bc9c6e0f714bf86970ae743c7ecb5a38d7e07d", size = 17215, upload-time = "2026-11-12T16:35:48.446Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/26/2fbeedb218a787a5eea551c7532cac4e009f83d689dd2faa0d0353473f86/python_dotenv-1.2.4.tar.gz", hash = "sha256:f0d53e69935a851c0dcc78f3ab7aaccd8cabef0b92382b576b824212902873c0", size = 60824, upload-time = "2026-10-01T05:36:10Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/d1/38f3a3405989a89ac18390803e70c6ad7c7760da4f9b83cbeca0c44a0c72/python_dotenv-1.2.4-py3-none-any.whl", hash = "sha256:42269a8a5b3fd54ffa6f3d84b18abed50064717576b4ecf03dc4a55d8aa04fdc", size = 23266, upload-time = "2026-10-01T05:36:08.633Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.33" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/b7/15b905f6142a9a8a68f66900788da1a32e4d4254452783b37ad1eccb0506/python_multipart-0.0.33.tar.gz", hash = "sha256:b8112424a6abd162ec08d390565dd113ad699f2fe6fef3c987b8bbcbfe9e390d", size = 50499, upload-time = "2026-10-31T14:23:47.461Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/1b/6b1b4164ec734cac911460ab599bfbe46afedd42c5c9627fe4235942c314/python_multipart-0.0.33-py3-none-any.whl", hash = "sha256:8f16c07b36add9da6b2ee87d3efcaf0a45a6de9c3d0a5dd94e9ccf7675a7bc45", size = 32253, upload-time = "2026-10-31T14:23:46.33Z" }, +] + +[[package]] +name = "pywin32" +version = "312" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/1b/9cfdeac80ee45bebbbcb31f1b7b99a0d81a1c72de48d837be984e0e88b1d/pywin32-312-cp310-cp310-win32.whl", hash = "sha256:772235332b5d1024c696f11cea1ae4be7930f0a8b894bb43db14e3f435f1ff7e", size = 6361387, upload-time = "2026-06-04T07:49:14.329Z" }, + { url = "https://files.pythonhosted.org/packages/33/b1/7afc96d041d982c27bc2df6f853d43f01fd273e3d39d04be3647ddeb533d/pywin32-312-cp310-cp310-win_amd64.whl", hash = "sha256:5dbc35d2b5320dc07f25fa31269cfb767471002b17de5eb067d03da68c7cb2db", size = 6926780, upload-time = "2026-06-04T07:49:16.881Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3a/4140da9ad54108e517f4a16b2d83da3033e08662144623e1239587cb7db6/pywin32-312-cp310-cp310-win_arm64.whl", hash = "sha256:3020656e34f1cf7faeb7bccd2b84653a607c6ff0c55ada85e6487d61716deabd", size = 4307203, upload-time = "2026-06-04T07:49:18.993Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f5/10a6e845a00fc5e7afd0a988b744f403d4d57162a28d160a093c4d9322f0/pywin32-312-cp311-cp311-win32.whl", hash = "sha256:17948aeadbdb091f0ced6ef0841620794e68327b94ee415571c1203594b7215c", size = 6362659, upload-time = "2026-06-04T07:49:21.349Z" }, + { url = "https://files.pythonhosted.org/packages/35/c4/dcd2d62b5944b6d5db53413a5899016ccd57ffcb7278f3f81655d25d2027/pywin32-312-cp311-cp311-win_amd64.whl", hash = "sha256:d11417d84412f859b722fad0841b3614459ed0047f7542d8362e77884f6b6e8a", size = 6928825, upload-time = "2026-06-04T07:49:23.934Z" }, + { url = "https://files.pythonhosted.org/packages/b7/56/3cbb433fe4501cdba2eb9040f56a4e1a8243faa4186b25295564d1a7a79d/pywin32-312-cp311-cp311-win_arm64.whl", hash = "sha256:b2200a054ca6d6625c4842fc56a4976a4b47f96b73dbe5538c3f813a80359f47", size = 6721875, upload-time = "2026-06-04T07:49:26.416Z" }, + { url = "https://files.pythonhosted.org/packages/83/ff/32aa7d2ed0ab12b323aaa64f9b75e6ad4f8fd09f9ccfc28c79414d46838d/pywin32-312-cp312-cp312-win32.whl", hash = "sha256:dab4f65ac9c4e48400a2a0530c46c3c579cd5905ecd11b80692373915269208b", size = 6371877, upload-time = "2026-06-04T07:49:28.836Z" }, + { url = "https://files.pythonhosted.org/packages/03/d9/77040d3b43df3f3be32ea289433d660d2727f5ba327bc73be835127d9d60/pywin32-312-cp312-cp312-win_amd64.whl", hash = "sha256:b457f6d628a47e8a7346ce22acb7e1a46a4a78b52e1d17e1af56871bd19a93bc", size = 6914841, upload-time = "2026-06-04T07:49:31.85Z" }, + { url = "https://files.pythonhosted.org/packages/e3/cc/7b1ec671775756020a0ee7f4feeaf3c568f0ab86bd3900088cf986937a92/pywin32-312-cp312-cp312-win_arm64.whl", hash = "sha256:6017c58e12f6809fbb0555b75df144c2922a9ffd18e4b9b5afa863b6c1a9d950", size = 6727901, upload-time = "2026-06-04T07:49:34.244Z" }, + { url = "https://files.pythonhosted.org/packages/2d/41/12fbfd7f36ed2146d8bc9de96c2741296bf0d490b98508496cff322e274c/pywin32-312-cp313-cp313-win32.whl", hash = "sha256:7a27df850933d16a8eabfbaeb73d52b273e2da667f80d70b01a89d1f6828d02c", size = 6370184, upload-time = "2026-06-04T07:49:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/ba/db/36a78e3403099d31d9746d13fdcde5accc43c1155f375a34d15983a479a7/pywin32-312-cp313-cp313-win_amd64.whl", hash = "sha256:c53e878d15a1c44788082bfe712a905433473aa38f86375b7cf8b45e3acbaaf9", size = 6914298, upload-time = "2026-06-04T07:49:38.876Z" }, + { url = "https://files.pythonhosted.org/packages/84/37/c1697194092b76de9ed47ca124323f02c57ffc8a45c06f88a3d5acaf01eb/pywin32-312-cp313-cp313-win_arm64.whl", hash = "sha256:59aba5d5940842075343a5ddc6b11f1cdf0d1567fe745290359dfbcc7c2eb831", size = 6727640, upload-time = "2026-06-04T07:49:41.083Z" }, + { url = "https://files.pythonhosted.org/packages/fc/2b/1f3cded5822fd49c02f40544cbb5f58c7cfd6b1694869fd476cb6170ee97/pywin32-312-cp314-cp314-win32.whl", hash = "sha256:a77a90fbb6881238d2ca9c6fd797b25817f3768fe78d214a90137ff055a75f5b", size = 6468928, upload-time = "2026-06-04T07:49:43.188Z" }, + { url = "https://files.pythonhosted.org/packages/21/82/3bf86d2e2808902013132e1ce905a7da0da53790f3836c64bf44d55e24f3/pywin32-312-cp314-cp314-win_amd64.whl", hash = "sha256:a4dd3a848290ef724347b19f301045831d8e802fa4464f491b98b1e0a081432e", size = 7024157, upload-time = "2026-06-04T07:49:45.34Z" }, + { url = "https://files.pythonhosted.org/packages/a4/0e/73f6d6800b4f27655abd9e9f6aaeaefcddb2b946e4674efa2bab184a7f7b/pywin32-312-cp314-cp314-win_arm64.whl", hash = "sha256:9fce94568364e0155e6dfb781ac5d95903be8baf28670632beab1b523f300daa", size = 6839598, upload-time = "2026-06-04T07:49:47.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/61/caa39686032d2ebdd04ff0ab5cbe163126c0066d98e00c9018646e42393b/pywin32-312-cp315-cp315-win32.whl", hash = "sha256:5c1fbe4a937a73ae9297384a3da38518cbc694c68ad8a809b2e19acd350f03ed", size = 6471159, upload-time = "2026-06-04T07:49:50.035Z" }, + { url = "https://files.pythonhosted.org/packages/0f/cd/7e1de64a4a6f69c04214169657ccab0d93a670ea50e35eb8f489d7378249/pywin32-312-cp315-cp315-win_amd64.whl", hash = "sha256:c2f03a0f73f804a13c2735b99392b0cd426bb4f2c4d0178e5ac966a0f21618d5", size = 7025293, upload-time = "2026-06-04T07:49:54.857Z" }, + { url = "https://files.pythonhosted.org/packages/23/ed/4532e9388e65fa16b46776ef47ad631a64eda1631884488af707666350ed/pywin32-312-cp315-cp315-win_arm64.whl", hash = "sha256:a8597d28f267b39074aef51fa593530082b39cbe5a074226096857b1fed2dfb9", size = 6840337, upload-time = "2026-06-04T07:49:57.531Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + +[[package]] +name = "regex" +version = "2026.11.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/61/27/a5afeb952577b84ee5fe48b8b9ce47ae662dbb69cc5b76c28eff98f83b1e/regex-2026.11.9.tar.gz", hash = "sha256:316a70fbd70e999c9664681cb0eb5b4982b2cc0f623c7c86a8a112e1bd4c6277", size = 419513, upload-time = "2026-11-09T00:42:09.786Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/cd/83393cd04d1cdafb07dc6d3e4f6f40455c7d54edcfb7513a51c632a2fa32/regex-2026.11.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bc0414bae708e91fa635af848ea1fdc13e442f30497077da562014f88ac67436", size = 496166, upload-time = "2026-11-09T00:39:39.152Z" }, + { url = "https://files.pythonhosted.org/packages/13/90/65cd23cc77094ea6953a7f4f9808539611b71881b7d91729243fcaa43109/regex-2026.11.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:26f5137412492f043d13903b8ee3bbc464a73c67ec9432e187f5a5eef25c398f", size = 295747, upload-time = "2026-11-09T00:39:40.41Z" }, + { url = "https://files.pythonhosted.org/packages/3d/61/6523251b377f38be938342fd52da4aead1a1d1b79ce137d4412e5b1eb79e/regex-2026.11.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cf8ba77a8d23fac7912cd9890a49d8f006106147bb80312c9954558b24ec7f95", size = 292719, upload-time = "2026-11-09T00:39:41.711Z" }, + { url = "https://files.pythonhosted.org/packages/97/5c/59cceda8ed285cb277f90f22b220c89576a38ebd1f2d7f0af198af98a5c7/regex-2026.11.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c876a5e3992b5f14709c7260188929174247485ed3f940bd94d4beb70abbfed3", size = 799904, upload-time = "2026-11-09T00:39:43.023Z" }, + { url = "https://files.pythonhosted.org/packages/9b/d0/498c4f67179885a4baf3b4500ea371d671180c20e136844065c239fe95d4/regex-2026.11.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:49eecd9b855cc9e683fed1b258d4e22dcb8e7b3c82f09c5778ce154359607f09", size = 874027, upload-time = "2026-11-09T00:39:44.36Z" }, + { url = "https://files.pythonhosted.org/packages/33/b9/61d474ad8aa93022edb00f13fcaac44678a5ef858801c804ad7d201d048b/regex-2026.11.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2405a0e564a5a1226427b21ee02a70c64f33a10015a18e480039c8ddb46b4f00", size = 915647, upload-time = "2026-11-09T00:39:45.587Z" }, + { url = "https://files.pythonhosted.org/packages/82/14/ec0c0f9e600f308280b901b46dd6e0095b56017fc2216b2abe6182009655/regex-2026.11.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0447593c5f4e2b88446da7466c83fc913ddf191600bbb7aebc6c8856595ccfb1", size = 805976, upload-time = "2026-11-09T00:39:47.654Z" }, + { url = "https://files.pythonhosted.org/packages/5e/de/bf7d2789051771e637d36aab03adea41b1e5729fe0c2a3d1df5e85a422bb/regex-2026.11.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:727989b08b98af7592eb86af12ca4227c017579094d5893a76fdd20b319cd8cd", size = 807143, upload-time = "2026-11-09T00:39:48.999Z" }, + { url = "https://files.pythonhosted.org/packages/0d/14/1a62ebd94dc36f74e2bb7f37bb7d7faf3bfc013cc90c1b1463d157e20136/regex-2026.11.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b886e1b7be6c392ea99d1c04de9b7adb4774038a1041514dfc6d0c5fb1bee6c5", size = 780582, upload-time = "2026-11-09T00:39:50.189Z" }, + { url = "https://files.pythonhosted.org/packages/3e/9b/42ce68062387ad6cbc4e2c5dc36d7f25d7a4d7621b5a67b8cd1a913ff2b4/regex-2026.11.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1ec6903dc8054129ccc9404a5aaf19e4fab9af98b03604c69db903da3c045a4", size = 788733, upload-time = "2026-11-09T00:39:51.267Z" }, + { url = "https://files.pythonhosted.org/packages/ff/17/8e5268e78894c2a979a1599f9261eb0ede9952028d0a2689fbd80f2998b5/regex-2026.11.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5c31a4e92c02f5c9e85c37a3851b9f83a36d2afdaaa88368f0d29ac56d8c81b8", size = 863625, upload-time = "2026-11-09T00:39:52.437Z" }, + { url = "https://files.pythonhosted.org/packages/97/0a/82e544ef361afc84e85a47907210bd9d1ef5c887b11ae3e091317c699327/regex-2026.11.9-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3dbccc60fa24fa1bab1cffe580c9bcd6467a885b6260d5e419f6d13db8c3596f", size = 766599, upload-time = "2026-11-09T00:39:54.03Z" }, + { url = "https://files.pythonhosted.org/packages/bf/4b/6484189c5f81e668059464780802b4bd6023415655c9410e8668a0a52a29/regex-2026.11.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b5ce8fd914c9eaedfefa444ed329fc5a80c723136bda90e9e69159c536854c52", size = 855980, upload-time = "2026-11-09T00:39:55.184Z" }, + { url = "https://files.pythonhosted.org/packages/59/f8/954254f0c28b8113b304f0a9fec86274ad043b997161bb019ece9a00e5b8/regex-2026.11.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ff675c20d54564cb2b6c480c2e4b362dcc3ce74887ee2d2e63037658a0f01ab5", size = 793610, upload-time = "2026-11-09T00:39:56.351Z" }, + { url = "https://files.pythonhosted.org/packages/d9/2e/c30c186c8e0d55f4fa6cc882fb3f7eb3731ad70ffc5f99d40e99f261fadc/regex-2026.11.9-cp311-cp311-win32.whl", hash = "sha256:dd679c8172c94291ccee40e17b03b6e34c553ad855a57dc52e811c3bb83403f1", size = 269436, upload-time = "2026-11-09T00:39:57.808Z" }, + { url = "https://files.pythonhosted.org/packages/b5/6a/c23207465db6bb7a98211e82e5f79dc5473abea56464960f1c705bda888a/regex-2026.11.9-cp311-cp311-win_amd64.whl", hash = "sha256:b75ea29155c988eb375325a9fd9270a550ea3427acf275d350538c5f83571abc", size = 281159, upload-time = "2026-11-09T00:39:58.824Z" }, + { url = "https://files.pythonhosted.org/packages/df/7e/fe3c59e5adec098cddf9d65ed5f6543c070a344527a20d20e6731d4e57da/regex-2026.11.9-cp311-cp311-win_arm64.whl", hash = "sha256:b9c9746f25607eabe931c42f25535c9461589e25a80879a361f9c02f7abb6dce", size = 279986, upload-time = "2026-11-09T00:39:59.892Z" }, + { url = "https://files.pythonhosted.org/packages/1b/81/f9c9857e531301761b612129ca24fc2d6a9f83c5d804510f0a86d97a0ce6/regex-2026.11.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9538d0bc19f87d273c2a5397a08fcd2646d02ace31ba7974483d997e36654dcf", size = 498602, upload-time = "2026-11-09T00:40:00.967Z" }, + { url = "https://files.pythonhosted.org/packages/e5/3b/58288749f3fb7303b1ee293a598813c4d08a440cc14c0c55a8fe4f7723ae/regex-2026.11.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a919f782789b9b3460bc51a1c69125551bb6164a0e401865a037397b219705b1", size = 296962, upload-time = "2026-11-09T00:40:02.404Z" }, + { url = "https://files.pythonhosted.org/packages/13/c6/32671781f2e61f40c76cf81df160ef5471264ba8a5f28cbd342cd4887a8c/regex-2026.11.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2255e173ffb8da59f26ec3529bf7e8531320e71575b27c3fb062747eb3a12e1f", size = 294199, upload-time = "2026-11-09T00:40:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/cc/ba/80e68a4d9a3cbbfd7734744a25e26e42976f50baaec786ba7c420b4855b1/regex-2026.11.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e2513028297550486dffc6e0799f24f194957418b75cd0ac2c7ad484e0d12a2", size = 804924, upload-time = "2026-11-09T00:40:04.54Z" }, + { url = "https://files.pythonhosted.org/packages/9f/57/029cdaaed8616a6088e39e0fb5c4f688e1821470f2004a90ef4738fd91d2/regex-2026.11.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:16165b67939c4fbaff46bb0380828d4fdda6936d92861baa47257399459145a4", size = 878334, upload-time = "2026-11-09T00:40:05.807Z" }, + { url = "https://files.pythonhosted.org/packages/62/48/cbbe309d95982fb72a09eb8e31a32ec719de04d061dbacba94ca892a6ae1/regex-2026.11.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b7073a7a0134621c5cfcf2c63b55cfd569cd88a6417c08cf829babba200b989d", size = 919365, upload-time = "2026-11-09T00:40:07.474Z" }, + { url = "https://files.pythonhosted.org/packages/46/c2/59c5b6cf4b762d80de04cf60f2d0deba1da2f4172a91c41eaee8933080a8/regex-2026.11.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ab4a0c5cd3df4b93841ba4a419a5438b2a4c78a4e418682fc674e3f50d56b857", size = 808826, upload-time = "2026-11-09T00:40:08.864Z" }, + { url = "https://files.pythonhosted.org/packages/e1/7d/18e82a2dc59bbebbea7426d3d5034cb9790230c2826d55ce090e6c8fc38b/regex-2026.11.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e3fe4474aeaa34535077bd3cdee6294756e99f4ed498b09712b5f3dc0b2947a5", size = 783421, upload-time = "2026-11-09T00:40:10.376Z" }, + { url = "https://files.pythonhosted.org/packages/40/a3/4c561718974dad242b481c2d9399a2a2b5d29b357cb6bed52427d6c0c2d9/regex-2026.11.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b47bd645e6305d22a31dc063635de40796e93e72cfeb9a86108dfa806ce866fa", size = 791492, upload-time = "2026-11-09T00:40:11.582Z" }, + { url = "https://files.pythonhosted.org/packages/fc/70/80a9f43944dd81012c2e10f4ae295d91f8db6929e79b10f1232e00b10d78/regex-2026.11.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ad0795929a24ad49f096c464f0ff7fb4d66488c6f20c2b1da724e2067e3d5c2d", size = 866863, upload-time = "2026-11-09T00:40:13.188Z" }, + { url = "https://files.pythonhosted.org/packages/26/d0/93464b1455c35000058c368fa989f21d09e9fceeb2289d1be4df93ded78f/regex-2026.11.9-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:f1ed6b50f54b8467bb3085fb6d701f3eb77c9ad81071da498cdc7725b4867f92", size = 770521, upload-time = "2026-11-09T00:40:14.703Z" }, + { url = "https://files.pythonhosted.org/packages/1f/5d/34c5aec48033f7929beaad8623e19ad7957b0de7ebffeae96b9689f1fedd/regex-2026.11.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e6f945b6fcbf1a51760ab41b61159dba8bbcf3614ed921bd175d62529be5ec26", size = 861457, upload-time = "2026-11-09T00:40:16.091Z" }, + { url = "https://files.pythonhosted.org/packages/94/59/80c4299ca9422b44738f8559d850e96d09a201334be41499863877980cb9/regex-2026.11.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a5ac0114fa49c175803fce562ed243bfaf7f6943edf4b2a01ef82f0f61e7e397", size = 795558, upload-time = "2026-11-09T00:40:17.222Z" }, + { url = "https://files.pythonhosted.org/packages/4c/cc/f14bf9e6e0ed44f29402bd8319ca231f2dce52c76084fc8561abaeda2e45/regex-2026.11.9-cp312-cp312-win32.whl", hash = "sha256:2f1c997fcb7a74006e3c4f09199d9e835bb46eb12c1c909f8b8259627f7b5ec7", size = 269777, upload-time = "2026-11-09T00:40:18.606Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ef/e03271355a802f38f7510e31e006667fc8b95b771058d75a36e2afda61b2/regex-2026.11.9-cp312-cp312-win_amd64.whl", hash = "sha256:ae42ad9825cde4cd374abfce6bc82233b91099170c138b349554813e97da5100", size = 280891, upload-time = "2026-11-09T00:40:19.698Z" }, + { url = "https://files.pythonhosted.org/packages/a3/08/551312e6599ac812b4052b5c9d3b3097b716f853c01e8ae6a150f805345b/regex-2026.11.9-cp312-cp312-win_arm64.whl", hash = "sha256:3c3886844cf8d2ce2b2cddb849eff6c404bfa951c77bd63fb4bd7ed270f135dc", size = 280256, upload-time = "2026-11-09T00:40:20.726Z" }, + { url = "https://files.pythonhosted.org/packages/90/55/7b6278e0babda54fe4b619c13019aef42247de912180777d49afae8eefac/regex-2026.11.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:224b73a26a1e64cf87fc8da9ee7a9994f152b92a728a7786b5c002fcebb205b8", size = 498358, upload-time = "2026-11-09T00:40:22.495Z" }, + { url = "https://files.pythonhosted.org/packages/4c/96/e37e54801fcd80fe7d2929708cd0c459aef588bc887da2de58a7c3c963d8/regex-2026.11.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6852035568af30020a538f945863d2dca3d5baadfe9d9387f9d54de2f1b30a45", size = 296847, upload-time = "2026-11-09T00:40:23.623Z" }, + { url = "https://files.pythonhosted.org/packages/f6/69/8bb15a5dcc3f8c9a57b069847fd65552bb7d1de22861e283e0139e5d5c71/regex-2026.11.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:24c9479dc1215a1ebc96fedc9a0cd026240209e9b69aa223df411c443bdf1d93", size = 294046, upload-time = "2026-11-09T00:40:25.026Z" }, + { url = "https://files.pythonhosted.org/packages/19/74/b13616c437d4141e9b73ef8f44d2be36058dc83cc1316048a081b73eead7/regex-2026.11.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5e3b575067d381f59cd6896a0126aef5dfb80a16b03594e8fc86bdc9dcf1cec", size = 804872, upload-time = "2026-11-09T00:40:26.115Z" }, + { url = "https://files.pythonhosted.org/packages/45/e4/8a0597242b57ef983f50ce84a2f35ba681e72333ff34aadee2805de2deea/regex-2026.11.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:38225e2f77e094c5a118e64af55655192eb16966b1e61ed8f4674ea94bedbb04", size = 878457, upload-time = "2026-11-09T00:40:27.223Z" }, + { url = "https://files.pythonhosted.org/packages/2e/a7/3ed73ae7e9b7ae09f80d09a59ee21fefc4a3e33a08e2b039934e57b86a76/regex-2026.11.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b4957fa9142e4850f454e7e76366245677cb917c608d7f8088b0105c9948db2f", size = 919209, upload-time = "2026-11-09T00:40:28.425Z" }, + { url = "https://files.pythonhosted.org/packages/35/3c/d6f2b69b141b3d0aa82d69fbbfe2b38dc49138e3f6843cc763774202d024/regex-2026.11.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:32183c31b090e1ee2bbb60898d236addc0029bf04a6def37175c2bb5419dcfb5", size = 808929, upload-time = "2026-11-09T00:40:29.882Z" }, + { url = "https://files.pythonhosted.org/packages/68/68/2fbbb66b68d0babc7260c1538f5d422f9edbbbdf4d6e5c916f8fe7c0fcbc/regex-2026.11.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:86b0004630d82a7fa993a40c599d77180622ba356d68dc48eef8f47e8c836853", size = 783522, upload-time = "2026-11-09T00:40:31.095Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fa/50f1b4e85430cb4aba15320eb0a8204e48d175317168771b8c05a00373c5/regex-2026.11.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d6bf83cfc92e7f6402cc10b8542fc562d4183929bdf126a0ccea1c15b55a109f", size = 791604, upload-time = "2026-11-09T00:40:33.218Z" }, + { url = "https://files.pythonhosted.org/packages/96/6e/fbf5139423566e051cedd6f71c97bc16fb34653e921b6654e24a0c16df8a/regex-2026.11.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c88a1749323036f3b3f67c32c1df4c6f2889f1ad5508eb3b66032019e0acb14b", size = 866818, upload-time = "2026-11-09T00:40:34.861Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d7/d873103dffac3b4558437dd622edff275f88a0cd32d0bed5175ac0c389b5/regex-2026.11.9-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:0dbed59cc01d38778091845ac229dc24b909bc326b82d3043972b0acd1349bb6", size = 770466, upload-time = "2026-11-09T00:40:36.345Z" }, + { url = "https://files.pythonhosted.org/packages/6c/71/d681e7949e91de573808a0d609a84d99411fad3680d346e436f68528c4fd/regex-2026.11.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d0517aff825aaa7a242ce85938ff4a51fa73633ec14a0b6639f658b29a78e6f2", size = 861497, upload-time = "2026-11-09T00:40:37.572Z" }, + { url = "https://files.pythonhosted.org/packages/5c/17/052721465a9e96a340b73d6bc7bffacd7bd9d57b4ddc38ac95e6671b7882/regex-2026.11.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a1919cb74f076c93b3d7344ba8ec930e9aa6d70272801779ba0e9bb48eb12d4f", size = 795612, upload-time = "2026-11-09T00:40:38.81Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ad/f5eeae00983238972bbcd3b31122437f729299d4a7ecd661b07a61448a6f/regex-2026.11.9-cp313-cp313-win32.whl", hash = "sha256:bd66446f042de0b2fdc4870f590eed990285d7c5d677545dc5aa8fd0dcf5f0e9", size = 269754, upload-time = "2026-11-09T00:40:40.004Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e5/27d16943df68676aba97db64e58b10702627eac1b983d96342b80b71f6c1/regex-2026.11.9-cp313-cp313-win_amd64.whl", hash = "sha256:19d36876d5783f157f280e67e51038a63a30630bd069528070d83a711f170e99", size = 280845, upload-time = "2026-11-09T00:40:41.348Z" }, + { url = "https://files.pythonhosted.org/packages/6d/34/74fbef82831900af4da247486576f867e6ffdb531841adc771af5797b213/regex-2026.11.9-cp313-cp313-win_arm64.whl", hash = "sha256:2062558aead9322ce54479b0ac356453d1455a30ec40d16b263b4cb0dcd5fd0d", size = 280234, upload-time = "2026-11-09T00:40:43.225Z" }, + { url = "https://files.pythonhosted.org/packages/ad/76/0931cbbc2faec7eb35e1dc95bb6e3256ddd20d1995dc1474886954436d33/regex-2026.11.9-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8d8225ae4aec6154a9c895fc5ea7a4cd9347c955432ee3cf277871518b4a78cc", size = 498414, upload-time = "2026-11-09T00:40:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/18/57/e06c598bd5935bce31faae0c20dbb27f7b3551f77c871da1fcc9d5e8806d/regex-2026.11.9-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d8f8f23200309b13a48a43379890bcdd34db49dda203445d2c9e1ebc1919419", size = 296843, upload-time = "2026-11-09T00:40:45.828Z" }, + { url = "https://files.pythonhosted.org/packages/e4/bc/8d2e21baca091b2f2bba7b6127cb60395018579736d41958d7398f381e5f/regex-2026.11.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fb91fc79a06d276f7811b840840440c6a35178396bfab288a4814b9a05d08245", size = 294104, upload-time = "2026-11-09T00:40:47.187Z" }, + { url = "https://files.pythonhosted.org/packages/4d/14/561ec95a248d68b55598e80a8a7190fce98f8a1ab8c0aeb2fe46b153bb28/regex-2026.11.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f570129536763bce7bd06a24f555bfad989aca594459a09cd7d51e47b92e0b4", size = 805122, upload-time = "2026-11-09T00:40:48.658Z" }, + { url = "https://files.pythonhosted.org/packages/a5/f0/6398931f37cf3c3e9a1f8af33097ada8c17c7a991791e0b2e1df60f34666/regex-2026.11.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:992f562c10b100501d17abad5307cbe5049cecc9afe6f5bb58cc0c49be964a95", size = 878892, upload-time = "2026-11-09T00:40:49.846Z" }, + { url = "https://files.pythonhosted.org/packages/ed/49/eb615960e51bf824a24ef5ad3ea100f72ac5010aab835bd72fc0fbc5b9d2/regex-2026.11.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5f571e667b3a0af4a37e231c535bec1237f531433fbeea4efe7196aae04eb734", size = 918562, upload-time = "2026-11-09T00:40:51.008Z" }, + { url = "https://files.pythonhosted.org/packages/88/4e/97c82477691afa0329b51ee801e5002cbf91ceff4864200cf2895833586a/regex-2026.11.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8d6cc5be22e9c839547d6ffc88b515845cbf47604c74d293955eea96281adbc", size = 808261, upload-time = "2026-11-09T00:40:52.586Z" }, + { url = "https://files.pythonhosted.org/packages/ab/87/c6b8dba287886b6b535b05cbb860110b84d11b864725cd39a35c7066da2b/regex-2026.11.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f026e602009c42175f5d7ab5ae0dc34884b09e5c1bb9c540e37f5f60c330173d", size = 783703, upload-time = "2026-11-09T00:40:53.631Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d5/f9707218634b435d03839d0dd4c43b7b74426c7a010ea786846b36215887/regex-2026.11.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8088dcf06e237da99bdf1e802716ad29e9b4103b2b04f8dee87de3d6102a6c03", size = 791919, upload-time = "2026-11-09T00:40:55.058Z" }, + { url = "https://files.pythonhosted.org/packages/68/05/352600c95571b49ed1c7a9a73e9afc76459854439c8b5c1e811b3b81b6d8/regex-2026.11.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:063cfc54e91284d0d9808befba1b9624bf230c3d22c6dd32a539f7af81b391a5", size = 867716, upload-time = "2026-11-09T00:40:56.286Z" }, + { url = "https://files.pythonhosted.org/packages/43/c4/ff96ccc1515ed2fae491fd490dab2ea22c5536ae8de59f176eea07c6f886/regex-2026.11.9-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:80c7f5a8236a751b9f0cfd160c68199db71860daa8609ec30ba79862926c6884", size = 770658, upload-time = "2026-11-09T00:40:57.546Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b2/bbbee563872d031cbe160bddac0846195aa4cc9eb2d7d6b61510598c9c3e/regex-2026.11.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:590831103234f8b044dde16352234b24a7966ed685a8f5735da48f4044f5cb55", size = 860933, upload-time = "2026-11-09T00:40:59.2Z" }, + { url = "https://files.pythonhosted.org/packages/2c/4c/8d6060820dea2a10b3b026b462fd47986f54c0590deb13f16f1dadd7df27/regex-2026.11.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:961fe37a9e52575360599a4099ebb6d983b71a88e4cb44aef24e230adcb592dd", size = 795245, upload-time = "2026-11-09T00:41:00.592Z" }, + { url = "https://files.pythonhosted.org/packages/1b/32/5093383543e5c86b40e4117c0c6ee082715810b65283494aca48dc517d3f/regex-2026.11.9-cp314-cp314-win32.whl", hash = "sha256:4b9f3e8a40e00a8c46a23e780c06444fc19f048c44e0c533186bdd730a57eb92", size = 275220, upload-time = "2026-11-09T00:41:01.735Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d0/5a472f1404c2217c92ebbd98d65a273cc48adc0a1f2948743ed59a873c18/regex-2026.11.9-cp314-cp314-win_amd64.whl", hash = "sha256:4d2bb485c68c8688201e5d56f6a7cd563ad9ee40ad2a700e77a13fceaac3a517", size = 284464, upload-time = "2026-11-09T00:41:02.734Z" }, + { url = "https://files.pythonhosted.org/packages/28/48/4addd74faf7531f713a9e765cadd1ddcb010363924fee94038e289452e5a/regex-2026.11.9-cp314-cp314-win_arm64.whl", hash = "sha256:dd0777e5cfa8508b0c1a903bfeb3b5859c1842c0b01f3ff39064c6bce3297e6b", size = 284279, upload-time = "2026-11-09T00:41:04.021Z" }, + { url = "https://files.pythonhosted.org/packages/ed/81/c88102ab3fa7eee7badc3f1554224d9012a5cd0fc2d40e3af4a4bb10e713/regex-2026.11.9-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:eb7c95df50b4132f938599f39758da9ee7c73ee66a8f40974160f07567e7301c", size = 502240, upload-time = "2026-11-09T00:41:05.561Z" }, + { url = "https://files.pythonhosted.org/packages/3e/da/ba9d89af353c54727086f705fff7b60bc0994191b76f2a560d69d44c612d/regex-2026.11.9-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:18c2e3de5790cd5d1e1437f4805fda7f16b14931f86312d9542d6014434aa107", size = 298869, upload-time = "2026-11-09T00:41:06.884Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/d9079948364cbd94f693801c9673ed3f97052848380cd1b4271e6061ef4f/regex-2026.11.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8d5b856217efc33802ae7bbd1b5d98c3117dae9dd6a36b75027291e2b79f72cf", size = 296656, upload-time = "2026-11-09T00:41:07.998Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f9/dbadb579d336487bb226ab02686faa0142cc065426826d1d121cc01a59ac/regex-2026.11.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:401055fcdbef098c6054e80e52813f7c937f4209c8c727ec4135df6a6f585222", size = 819805, upload-time = "2026-11-09T00:41:09.202Z" }, + { url = "https://files.pythonhosted.org/packages/64/d7/7e433fcdd1075dfce9423accf478a636b4e9a2078311b86a25ee4973df1a/regex-2026.11.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e68a02b7ff911d611c43457b029c76d6bbccf3c1a4f7e336e086a3b6afa1d19c", size = 881836, upload-time = "2026-11-09T00:41:10.783Z" }, + { url = "https://files.pythonhosted.org/packages/15/40/89d31d421367742d2854eba4f6a3d9ae1b15630fae9c7c791415c9a9373c/regex-2026.11.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7f854a607c31bc87b64d9ca13a9d25c6fb57f59f802d2fe871c48b205d06f566", size = 924530, upload-time = "2026-11-09T00:41:12.035Z" }, + { url = "https://files.pythonhosted.org/packages/bf/d5/5e16185a5ba2ebbf83ae24935c32e3a3fc25191347f529c06908b267b6f4/regex-2026.11.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2d6a2d52cd32a7cdfef4b3e22417eb7bde912a5624128851bb96aef5dc657067", size = 824544, upload-time = "2026-11-09T00:41:13.233Z" }, + { url = "https://files.pythonhosted.org/packages/7b/14/0e753b59a7ffed4d67c50e2674e870293fdf6f7f95ce65352407427687df/regex-2026.11.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f4c1141775f79b742d9ad2d33e058cf06e7219030aadff8d4a132450cc7539dd", size = 793572, upload-time = "2026-11-09T00:41:14.551Z" }, + { url = "https://files.pythonhosted.org/packages/4d/6d/39a0e1ad06a26e04eb1fe2e00322c5d334d74c97a94a9dca01fb7e574eda/regex-2026.11.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e886209417f98c2b8b817d531e72731c3fb78f990a402759f4a4c7714f9c5415", size = 805935, upload-time = "2026-11-09T00:41:16.034Z" }, + { url = "https://files.pythonhosted.org/packages/8a/99/1667c4ce3af04e661a0eff2008126ceeca874bc52d7f2895e75b86e7d11f/regex-2026.11.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:85f327c6645bc0d924ef407d36aca97ed86bc41da28cabd77b347b1771b5eae1", size = 871258, upload-time = "2026-11-09T00:41:17.79Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2d/124b69673327013b19e6cfaf7f59bcd1e801c1ebf893a056e0a0f6e0f84a/regex-2026.11.9-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:9cb9d77c5c9101f1b6aa3d22f679f7a41a5af6ab536bc7290457e9bfc40eeeac", size = 776374, upload-time = "2026-11-09T00:41:18.85Z" }, + { url = "https://files.pythonhosted.org/packages/83/4b/e4d277fe50a30bec2b98bb7c7e055efb33060c984cc41a75cc2d6b4c9e3a/regex-2026.11.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0ac46eb63c366482a268121d873a08014304ea774d349b9bd2340a622b88a3d4", size = 864755, upload-time = "2026-11-09T00:41:20.316Z" }, + { url = "https://files.pythonhosted.org/packages/25/a7/b1626d1a273b29f6104265488a7b82ef22a81f8a1066fd8d91e098887213/regex-2026.11.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9622c0306e0864fa2cce4e5de97b3f0b7cbb86054c781550fceeb9f9c9bec290", size = 811588, upload-time = "2026-11-09T00:41:21.51Z" }, + { url = "https://files.pythonhosted.org/packages/d8/b8/2581a22a535eeaa301f8b3a71614d7bb8c30b821ba9373b991113b8b4c1a/regex-2026.11.9-cp314-cp314t-win32.whl", hash = "sha256:8cd7bda4894077adf35ec41df18d705e0f8028d40445066c870e517c754d78c2", size = 277284, upload-time = "2026-11-09T00:41:22.634Z" }, + { url = "https://files.pythonhosted.org/packages/10/21/a39d46035c96eaf530eabb6b57751c744c317c0af145240d3f1997d9ecc9/regex-2026.11.9-cp314-cp314t-win_amd64.whl", hash = "sha256:19c5affdda6dfe1bb2e5b0775cf5f4e71e664bcde2daafeec635d34d6f9704c8", size = 287062, upload-time = "2026-11-09T00:41:23.634Z" }, + { url = "https://files.pythonhosted.org/packages/20/64/1a9f6a13e365b1b23bd7ac264ceb8ecadbe962d3354a4cb4a303c88a749b/regex-2026.11.9-cp314-cp314t-win_arm64.whl", hash = "sha256:422c3dd2f8ff069c1050d4f4048bc8fa26fa92df09fcedbe13f77c922f5f7dc8", size = 286617, upload-time = "2026-11-09T00:41:25.402Z" }, + { url = "https://files.pythonhosted.org/packages/91/3e/a94fae619c5c72644732b393f241a4dc65e32a47c76a681b819390214d6f/regex-2026.11.9-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:022c80076ecb4a1e967c6ff9feeecfdfc364183281667056d1953cd345b0b9a8", size = 498649, upload-time = "2026-11-09T00:41:26.995Z" }, + { url = "https://files.pythonhosted.org/packages/41/e3/d468930952cc613f7d63ad375ebe9c1e1b850b3a4c87ed411e8db8cfdd62/regex-2026.11.9-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:639d43b0898a2fd99e10eb5736f022c0fe8ee25bf60250061f8720c47a54483d", size = 296832, upload-time = "2026-11-09T00:41:28.351Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ad/b53184107d89fc00a596d4306483a034d68353cb410cf7b19c7ee511cfa0/regex-2026.11.9-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:569ec6f0f774caeef4e0bbc8161aff2b7f07b1051ebcfc9c2c9eccebe7320b06", size = 294370, upload-time = "2026-11-09T00:41:29.497Z" }, + { url = "https://files.pythonhosted.org/packages/b6/79/0fc94e5a21468d15e649c57ac327ea307712a5a1216557f6a7508d16abfb/regex-2026.11.9-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f209033452abebfa65f0d388b1f6d4f2b4b2791b3a403bc744c03e80e996be9", size = 807071, upload-time = "2026-11-09T00:41:30.753Z" }, + { url = "https://files.pythonhosted.org/packages/83/6e/3006d97b837a876223484206e4d7700798421958de49c081ce280a38d5e4/regex-2026.11.9-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1dd7fa70f8b4956acb3e34eb9bea1c7ae3d31509abf979393181eaaa0f00e63d", size = 879313, upload-time = "2026-11-09T00:41:32.194Z" }, + { url = "https://files.pythonhosted.org/packages/cb/a9/f033d7aa75457dd3f6fa8f5701c5b4a5c685428bc7582b9c872a3e9e9c55/regex-2026.11.9-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b10639d37913075b553ba9638be170a87f70c32bd9da0655c3093e7ebc4a8065", size = 918900, upload-time = "2026-11-09T00:41:33.717Z" }, + { url = "https://files.pythonhosted.org/packages/55/7e/378e356b11dbd903d61844215bd16e63e0bdf253c4c8e66cd60ed504737e/regex-2026.11.9-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8e8fd96057e04660a655f2f7fbc9a9eff6ffe2970ff01c0145a0d425f2d1bf6", size = 809955, upload-time = "2026-11-09T00:41:34.98Z" }, + { url = "https://files.pythonhosted.org/packages/28/2f/5d689c2b7b0083a54e47786699a08a2548cf317316b86385ac0646a56a61/regex-2026.11.9-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:adce69b7696dec985977bc65d025c43dd7f2fa780f4f6e1d467d939884d7f918", size = 789193, upload-time = "2026-11-09T00:41:36.85Z" }, + { url = "https://files.pythonhosted.org/packages/d0/f4/231515fefa3051505ff3dd51d1eee987bcf637958d52dd7ae70d2daebe19/regex-2026.11.9-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:346ac3b9664ca04dd9ff7f422a33d87015657b0674a2830d804d78e987cc1f4a", size = 794166, upload-time = "2026-11-09T00:41:38.268Z" }, + { url = "https://files.pythonhosted.org/packages/9e/10/f935d85b0155d0c621113fcef32aa148ef8d078c16907fbda6950c5d3abb/regex-2026.11.9-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:0c816b00198ddca1040fa2657619b1c6b4c861d158cbad9de77d01fdc44550cd", size = 868221, upload-time = "2026-11-09T00:41:39.706Z" }, + { url = "https://files.pythonhosted.org/packages/a0/5b/9e243ee822c1680cd1468520280a8bc96fe7a8b8ca5a7ed7f213fe9da92c/regex-2026.11.9-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:aa62357d0dd25abd66100369828d6e28b50a5c44001a0fd20f95117c4038c648", size = 779357, upload-time = "2026-11-09T00:41:40.966Z" }, + { url = "https://files.pythonhosted.org/packages/12/67/2273b16db6d6a27bbc2a4f61ec9221d06adf3d1dacfbd6d8120f78e4c8ef/regex-2026.11.9-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:d1627410afc3f5a4b0c4d81dd1bdd1ed3de6407eda6d93524e643cf3891126d2", size = 861471, upload-time = "2026-11-09T00:41:42.048Z" }, + { url = "https://files.pythonhosted.org/packages/68/d8/58f8688bcdb9e482be40e25c13e7bb0701c2ce8c4514ce7af7c635877d81/regex-2026.11.9-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:970201018e997ed4a2626ad894b657035397fa91c1c86eff1b0c8cb9eb03d478", size = 797579, upload-time = "2026-11-09T00:41:43.758Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fd/382230164a17f509c155918771817378516a9ee8bd1b54a5ec4d3dacd7c3/regex-2026.11.9-cp315-cp315-win32.whl", hash = "sha256:1f4c9e7edad6ed655c83741413b350db98b1234a4fa8739bc1ed7167e19ce98e", size = 275213, upload-time = "2026-11-09T00:41:45.63Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1f/3b08cdef84fae97450baf8c60c0f86a85c613ddfa7b106df9186069248c0/regex-2026.11.9-cp315-cp315-win_amd64.whl", hash = "sha256:72770400851673eee68e9d6cfc45c18a20736894e3002506a987a244780df36c", size = 284471, upload-time = "2026-11-09T00:41:46.87Z" }, + { url = "https://files.pythonhosted.org/packages/24/ee/ddf3c916425e7de6774df90e9b36ea7be709233f6e617863f9eeaf855211/regex-2026.11.9-cp315-cp315-win_arm64.whl", hash = "sha256:1e8fe618fe5eeceb827db2b49750fad345be0e6145c5c41ed701ee27e081ff9d", size = 284289, upload-time = "2026-11-09T00:41:48.301Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d6/0e0566737f61954b66df56cee68a7412df3fd8f5a9325f08837cc641b9f5/regex-2026.11.9-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:6dc0dd2d31bc0e2301345c3b20f64a6d864218196c7c9ea2b96788e76b964578", size = 502328, upload-time = "2026-11-09T00:41:49.337Z" }, + { url = "https://files.pythonhosted.org/packages/9a/1e/7f95189e75cd26a634c9832b469f620bf45d02a6e2be184a1169ee7af61a/regex-2026.11.9-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:6a1ee54c1e237fee890ef7045951613147fcf77a2c898229de44f165e5650aff", size = 298773, upload-time = "2026-11-09T00:41:50.737Z" }, + { url = "https://files.pythonhosted.org/packages/f5/cc/7e5406a25fe6cf378e770858133b044e451349ef2c1f2977b68dd061a2db/regex-2026.11.9-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:2bebc58114814ed6d78cc3d27b70d4f2dceb8a2e5d23dcc7d831809e86d3bc3c", size = 296831, upload-time = "2026-11-09T00:41:52.067Z" }, + { url = "https://files.pythonhosted.org/packages/f8/5c/6ad943248f553ce669fd2a6ad75ec27476f90a40e0f5173337db7089c13d/regex-2026.11.9-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bc2cb2aa025b89ebe26c8dc3c1edb1c6b22be9dadea030a89f3a477faf646e8", size = 818469, upload-time = "2026-11-09T00:41:53.192Z" }, + { url = "https://files.pythonhosted.org/packages/a2/b6/be6a30c8bb5bc32fca59e9e6a7ace27fc5f8626b5b11a6ecca9f2d7834c6/regex-2026.11.9-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:499313286c8ae346f19e6af6f78f45c356de293b37540fdea5f4309a5aa59b21", size = 882408, upload-time = "2026-11-09T00:41:54.347Z" }, + { url = "https://files.pythonhosted.org/packages/37/3d/a6fc734740e030e1479ff081c5af669c3543f6f04008c792c5566b30f07a/regex-2026.11.9-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d77014fe27bfc3565a0ef706ced0f7d5471937bce9b7ce38c46deeaa9f4b3963", size = 923235, upload-time = "2026-11-09T00:41:55.551Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7f/c1860577baf5c3acea63fa812b7bb510825b28c44b474db00900e7cb3ab0/regex-2026.11.9-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e4498f31d71819a16b32e1a965bb587c7aaf153848c451d1e384ede77dc6a134", size = 822410, upload-time = "2026-11-09T00:41:56.803Z" }, + { url = "https://files.pythonhosted.org/packages/28/af/692419879b97a63e8342ccbd138329f900af38408f0b080d72a5a5fc9160/regex-2026.11.9-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:04e1c78cba5e3edd5400e9d7319a3036f652faa599bfabbf888fec584a9a4772", size = 795527, upload-time = "2026-11-09T00:41:58.169Z" }, + { url = "https://files.pythonhosted.org/packages/24/53/1ba04321ce8d15d219710e95f03feab54ca6a19ecad329a42efe7c46c873/regex-2026.11.9-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:5774579fca3431bd77518529d1ba54480d075396bdb0533daa0daa4db86e6594", size = 806153, upload-time = "2026-11-09T00:41:59.309Z" }, + { url = "https://files.pythonhosted.org/packages/e5/dc/3933476fab57134867d20d214d57393fbdb82a5a285083633bdf956372d5/regex-2026.11.9-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:34087251368439c8238cbf2bde88a8e89fd865a2a61266a5c64b360414c7fe04", size = 872784, upload-time = "2026-11-09T00:42:00.636Z" }, + { url = "https://files.pythonhosted.org/packages/e9/53/e44fc4efa4de6dd29fc0f2e237f944a392478611d54d1af43e45c7726626/regex-2026.11.9-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:4cb820ff2fdcf610278c74dcfd45857b5177429e30bd0b7d1164855a011cd58d", size = 784277, upload-time = "2026-11-09T00:42:01.72Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a5/a5e6f6b5904712646ed10023b75746d9492616f75f8ad9d8045fd28f04c5/regex-2026.11.9-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:cb152514d21e3a0440e4c30ff2323f3bbc48de3c1d1aaffe0494470cf5566a42", size = 865578, upload-time = "2026-11-09T00:42:03.197Z" }, + { url = "https://files.pythonhosted.org/packages/09/60/73045aa8b48f37e12f18eb6ee5dee9573a31f381f987f1a2d0ea27310537/regex-2026.11.9-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:38cd15cf8407a4220bf387657cbd38d961161d152c08c731fc2e05fb45a00ab9", size = 809333, upload-time = "2026-11-09T00:42:04.407Z" }, + { url = "https://files.pythonhosted.org/packages/71/de/9f668597839d8abb969846be416ed6982a4ba10b83445f241f27752456cb/regex-2026.11.9-cp315-cp315t-win32.whl", hash = "sha256:c68ed3497146b830d27642193612bb6ac51cb70dd73b5f35f07b37cec915b795", size = 277479, upload-time = "2026-11-09T00:42:05.728Z" }, + { url = "https://files.pythonhosted.org/packages/cd/af/af2858db09db8ff0b9f50e67610da2d71517defc62f6eada14a1405c8930/regex-2026.11.9-cp315-cp315t-win_amd64.whl", hash = "sha256:83ee345b9feb7973c408187778fdf2f2498b54a51bc0b92d08b9b7468ddfb5dd", size = 287021, upload-time = "2026-11-09T00:42:07.23Z" }, + { url = "https://files.pythonhosted.org/packages/05/29/f38c146b8e4b9e9d844cd5387ff82b18a35efe34795366a2a98429695584/regex-2026.11.9-cp315-cp315t-win_arm64.whl", hash = "sha256:2fd69109525129c1823765f34d650247bcd20065eb842832b99d3e512dec0ed7", size = 286551, upload-time = "2026-11-09T00:42:08.629Z" }, +] + +[[package]] +name = "rpds-py" +version = "2026.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/42/68/3bd46b8a5e01d3c2ebdf9c5e9497912e3fe0cde02bac21a7130ca866e403/rpds_py-2026.9.1.tar.gz", hash = "sha256:4793ef7f78268b124b73fa933440f01d258bbae01de9fa53e9080c9ab0425a12", size = 63948, upload-time = "2026-10-04T16:32:36.469Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/6d/ddb8dfcf41892ba9c672d27988592c038d30db61708230313831b6780a1b/rpds_py-2026.9.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2711d29b653b3bce48a63d18b9c6b53274669e6d6c4094dddeb4d9a0e45128b2", size = 344947, upload-time = "2026-10-04T16:28:55.207Z" }, + { url = "https://files.pythonhosted.org/packages/9e/b2/632c3d034961149eff606e3e357547620a9ab37b849ae5815f67838753fd/rpds_py-2026.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3231c4c0e521dafa5be0c9f114ee2c2ad46650836f2d72caa86801950c3e7044", size = 346770, upload-time = "2026-10-04T16:28:56.884Z" }, + { url = "https://files.pythonhosted.org/packages/6d/45/4df346410bb873d03ec60c3b891d1fceb8cabde1971d29844bbb625cc296/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e01b3c878c8641913e688edd1b3f08658c6783d29cf6b826bd3c0d1ae7a1ffaa", size = 377795, upload-time = "2026-10-04T16:28:58.193Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b8/a70f302207e4f29f873c01a986fcac5def49b9c8224328cef1ad7da6b7a9/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3e524c7874ac72884d28e16dd5b8d839fd09e0fe76b020d3fbca23212a7b8c52", size = 383137, upload-time = "2026-10-04T16:28:59.411Z" }, + { url = "https://files.pythonhosted.org/packages/13/e3/3aa28a48a01169d0e20728bf0e5ebbea3ae2774285b40853ae49b3ef332a/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761fdae6728ceb99ab182fad2f0cc1e262f610834dc891aea1d1a2a2e634776f", size = 496789, upload-time = "2026-10-04T16:29:01.011Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a7/414e8d088602d66ad6f47c65bf07fe5090992a8574cdd4571c1a83b647ed/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b723eb406dec5bc9ec516c73ab9c3239a3284e017f7eb89ee2b3258bd504fb7", size = 395929, upload-time = "2026-10-04T16:29:02.465Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5c/aac3b960fd55d37b49f77c0eebbe043eaf36762ebd48e7b46951e59c1c05/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:136a1c3fe4402b7008bc81cb62ee538481795b61a7e83df88dff3b3f02b726ff", size = 375426, upload-time = "2026-10-04T16:29:03.944Z" }, + { url = "https://files.pythonhosted.org/packages/d3/80/77987a41c40be7202a78d9aed4a650bfb05792237cfd3b9e22638d990fe5/rpds_py-2026.9.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:839dde845559254f34885267c6878f60d61d5205180226d976fe488d45fa128e", size = 381497, upload-time = "2026-10-04T16:29:05.308Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d0/640b02eb1ecd71a913c1178b6235287b31006266bca9a26862e80e497b54/rpds_py-2026.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56cd8b3f77d7b6812f533b662186a1f28316931166ddc00fb893b1b0db7e9888", size = 405740, upload-time = "2026-10-04T16:29:06.578Z" }, + { url = "https://files.pythonhosted.org/packages/3e/8c/d034195d45d215625a23f2ae85dbbdb86a49a93a51272061c97c80b9d316/rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:074a4d198bc34d9a8ea425114fc3ded6d11ec01f6a314a8db67454a5152d8834", size = 555850, upload-time = "2026-10-04T16:29:08.115Z" }, + { url = "https://files.pythonhosted.org/packages/de/f8/532f1addfc86207bc3cf23749638bb0d457bce891595012ed8a08d0543b8/rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6beb738155fe8ab8091afdfa5a3226b21c2b1593f1e50ebb90eb25b44dbc0391", size = 618661, upload-time = "2026-10-04T16:29:09.554Z" }, + { url = "https://files.pythonhosted.org/packages/50/5b/b0f2c9833a36a5b1f6ac5cb0e635fabacf713266f46ceefba2102da0bf51/rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57492a550a1d88d29d003247e5f78dd8cf04a701fac0e4c8db8745a6d2504e0a", size = 583530, upload-time = "2026-10-04T16:29:11.072Z" }, + { url = "https://files.pythonhosted.org/packages/bf/20/464ed4b98d3c8c6a334f3b5e24d3886de04b05658a95fc40a37e0e6e4775/rpds_py-2026.9.1-cp311-cp311-win32.whl", hash = "sha256:d95a354e02393eada6d7351184671aced9d4cce109dabf927cb7aa99624352a1", size = 208125, upload-time = "2026-10-04T16:29:12.408Z" }, + { url = "https://files.pythonhosted.org/packages/df/c9/384a9b62f8cd89c8e3e609b8c9052c140e8c01d807261e0517c904b8bf29/rpds_py-2026.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:ce4d4f52e2a4324396caddbd45a97d8d7be5f42edd25d2355282a9c34f9b2f7f", size = 226045, upload-time = "2026-10-04T16:29:13.751Z" }, + { url = "https://files.pythonhosted.org/packages/4f/3b/0a691f0dcb2301fa9c736346da2d0d4991778373283301c5c12b8f952f3d/rpds_py-2026.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:fdcd198979b4ecffcc1beba366a7fbcf4eb41243691a82fe52ceb0b902f09c12", size = 222636, upload-time = "2026-10-04T16:29:14.989Z" }, + { url = "https://files.pythonhosted.org/packages/5d/34/a828586ea3329fbb50895b50e9cf98ca3d924a9f41a0b26d443bff1b3794/rpds_py-2026.9.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:50906f5aea24b5a865cbd0a589698288631d9f3a54c3a937c83aefa95a0d14af", size = 346822, upload-time = "2026-10-04T16:29:16.258Z" }, + { url = "https://files.pythonhosted.org/packages/90/81/ac6a0d064982251856ce009c9e1dd51a34110b3c055aa7d1aad18b4899a3/rpds_py-2026.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e21c1429e205828ea886a2293a4a2c8e01f4c25d9893ca330e97a6cf73f52e7b", size = 341380, upload-time = "2026-10-04T16:29:17.706Z" }, + { url = "https://files.pythonhosted.org/packages/42/ff/bf7d54f362748fd6a49277b9d6531c394074110fedf50ad791ec59133fbb/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693b2728bbcc48d09a981a356954b0c47c53ff25b545856f28a889ea619f69a", size = 373053, upload-time = "2026-10-04T16:29:19.063Z" }, + { url = "https://files.pythonhosted.org/packages/60/de/74b0ccdbbd28687b9b5fdb34c1cabed88352182facaced9d6f5486b8b9ed/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8601470267d938bcb7f3ab1a336100af51a4fd5b6ed030ef52461bb3ef5e7e07", size = 377742, upload-time = "2026-10-04T16:29:20.543Z" }, + { url = "https://files.pythonhosted.org/packages/c2/6d/b979775a3057b2a5c26d75ecbff60a20a24ef081db6dd76836fca3f20247/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3890a6aa36e6baa53d5258a2a25d3ef8b37ad165a6ab27a892d7c3e3a432cd69", size = 487289, upload-time = "2026-10-04T16:29:21.958Z" }, + { url = "https://files.pythonhosted.org/packages/1d/5d/7c34734ce3ece943d9d6ee0122a74a21bbc75b47acb5b7053c83f6efb1ed/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b5b393eda5ea42cca1c1a6665f2a4882b4fd5d1777e41ce0545a107fb008c9d", size = 390710, upload-time = "2026-10-04T16:29:23.378Z" }, + { url = "https://files.pythonhosted.org/packages/1d/6d/b26eb1e75395925b3a142ed351cbed2ec8212f5c9d937aee3df32c701baa/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:addeda51556dac7c1a2f14cda62db8b621cd12afba3091d03a96c72932387eab", size = 371261, upload-time = "2026-10-04T16:29:25.1Z" }, + { url = "https://files.pythonhosted.org/packages/7c/98/b2fdfe10301a9e27af21e634337fbba7baac0bc17fe44619a17c26bba2cb/rpds_py-2026.9.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:d9edf30457d74eebfd76b045535e36f1cd89062566a128a0db2145ca042d787e", size = 376075, upload-time = "2026-10-04T16:29:26.748Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b1/c4b8d954e49c3c69cf8063c0c9e0919c99f3d2cecc46311606a1cbe835dc/rpds_py-2026.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:815d26356930846a40c7bc1366e7b1b0320ab8a063e66c11298a208bed0fd237", size = 399358, upload-time = "2026-10-04T16:29:28.282Z" }, + { url = "https://files.pythonhosted.org/packages/b3/59/9559a7293c97dff0cbb293efffbd36644103883ecc27741af8ef90c84ca8/rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b5a6f40f0a1486b4b36c888123afc67acdbd9f33235927acf5ff295429a0ba3", size = 550267, upload-time = "2026-10-04T16:29:29.851Z" }, + { url = "https://files.pythonhosted.org/packages/6c/9d/6dc60e49511de4c8b00e74f9c1d43aed4d27f712cdb1ade92f2c199cbf64/rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b5b8b0753718d258fd454283fbd57e14545d3b40583fa672e27cb4f987626bcc", size = 614018, upload-time = "2026-10-04T16:29:31.54Z" }, + { url = "https://files.pythonhosted.org/packages/fb/bc/92a4ecf27301b886232a413360f6b348a81d55ffef654f2ebda5970ebc3d/rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:46d80bc76b51a6c24f9944368c28d38b8bcbcea1da4f2f8d3ebc31a67e8c6ec6", size = 578509, upload-time = "2026-10-04T16:29:33.01Z" }, + { url = "https://files.pythonhosted.org/packages/08/53/efb97f2e6589b7ab8394385a73870e0b2ab2a195281c117b21fd6d7d4855/rpds_py-2026.9.1-cp312-cp312-win32.whl", hash = "sha256:befc2d6a953e563f8a7bfd87a42c22ebf8a3e980dcb7b6a4d17b70b0e914e8a3", size = 205401, upload-time = "2026-10-04T16:29:34.451Z" }, + { url = "https://files.pythonhosted.org/packages/1c/84/1700cc748d0eaa747486e4e82882d2575224e6e9a3148df583058850c629/rpds_py-2026.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ce8943f79c2210f7abcc28e86367b03b28d95027fd01c46d2472373ae70c86f", size = 222923, upload-time = "2026-10-04T16:29:35.709Z" }, + { url = "https://files.pythonhosted.org/packages/c3/89/0302215373c2f8b4408b0fdfee955368cc378a98ff59508d47a7e0dc2dbf/rpds_py-2026.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:501909f2e4a1e2dee528ef766fe3c469060ebc17e54a8383d404ba07a81a6f02", size = 218415, upload-time = "2026-10-04T16:29:37.263Z" }, + { url = "https://files.pythonhosted.org/packages/83/ea/ee88fd9e756ff93fb6b1182a47ec09504a242620e33ce1d20679efefe841/rpds_py-2026.9.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:a36b70596407634ca82d4b989a3729074a008537a0522e4c8046a67c729103e9", size = 346229, upload-time = "2026-10-04T16:29:38.82Z" }, + { url = "https://files.pythonhosted.org/packages/57/71/a097d6552f837500fc36e6b23d09cfb9890c3cc47531f9ca64e149799615/rpds_py-2026.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eba5d173f7d5708b22a93815017a4611873ed54db9f268077c0dd1ed99cfc858", size = 340514, upload-time = "2026-10-04T16:29:40.405Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b7/497e85768bf4e0d8ddbaa096a4cac31d1509251dee2728a8490aa367e0b5/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:457866b85daf5034296666168b84a69e0b2e89dc4f1af102b46f6448a60b9063", size = 372611, upload-time = "2026-10-04T16:29:41.778Z" }, + { url = "https://files.pythonhosted.org/packages/52/4b/74ab4108916250b6e198e0d3af05bc6835eb046315f22f7a0ceb49667c5a/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a3a52a3ba86436ab3aef510fbe21512abc2ddd1993005dfe50514bd2284ef025", size = 377712, upload-time = "2026-10-04T16:29:43.242Z" }, + { url = "https://files.pythonhosted.org/packages/0c/8e/067e77d9d7b3cc793c9d909b7e97e7aadbbb1fb094093b6876902cc96d38/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7841166b7fa64c9c56404617ae4341448847482d45933b13135d26c130519e5", size = 488106, upload-time = "2026-10-04T16:29:44.692Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b4/c5aae6c2dde269bf955f6b7d35065c655a57e47750d9668052ad67e74dda/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:926bdd3e3b5998ddf70cc64bc8cf57209571f9044542913afb673799fec77dd0", size = 390795, upload-time = "2026-10-04T16:29:46.129Z" }, + { url = "https://files.pythonhosted.org/packages/a0/36/76fab39973ee11e7f9f357c55138197bb01c86f6502cb76487e3b4f42db0/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7868b85224291c6cb6759f9b5adb9745f486d226f62b16a614dd5a2a5ab2b35b", size = 370794, upload-time = "2026-10-04T16:29:47.603Z" }, + { url = "https://files.pythonhosted.org/packages/3d/fe/cd2a80e6d7b871937a60e935c5d507aa390d143f4ff3636f640b9733d5df/rpds_py-2026.9.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:3cd182d7291d29b92c521a0069d9c01ba6193628a9a105531d11b40a6d731a33", size = 375673, upload-time = "2026-10-04T16:29:49.223Z" }, + { url = "https://files.pythonhosted.org/packages/6c/18/7464a9953724e55a3b3206062fa0ffdeaa519584c6aabf65d3956d94f131/rpds_py-2026.9.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e6ea1cda8d8c688278430e4268a42f5e5da3bdd74578dfadc0820c3f1766ce83", size = 398758, upload-time = "2026-10-04T16:29:50.601Z" }, + { url = "https://files.pythonhosted.org/packages/c0/86/1534b436700fd49ff411063b7c4d7e938adfabf90895b6cf1622d5a7d1f6/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5943980471829f6de242a20b109de3111ba6b77e3af0ffc587028ac854b05e6c", size = 550370, upload-time = "2026-10-04T16:29:52.002Z" }, + { url = "https://files.pythonhosted.org/packages/57/1c/e1fa82a8a01e3c5820f3ba98a8b2673f642128eb368fa88871b01dd2c909/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:76d3af9732d2dab69f28179b40ba2d87e2f1d5824b4a694780aa787d685e8f36", size = 613106, upload-time = "2026-10-04T16:29:53.655Z" }, + { url = "https://files.pythonhosted.org/packages/29/55/b20b8c4c3dde8755bfcd5b08492a02d0cd2e26929aedf6199ca2a377d42a/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:78326f4cb4427a56ba4996c0762b63be45f06b85f086526420d2b3a66e40f84d", size = 578235, upload-time = "2026-10-04T16:29:55.157Z" }, + { url = "https://files.pythonhosted.org/packages/55/42/df3f7bbc3f7ab37a8a9db8d6c2ff2c985422899f1f7926afbf7ec3c0b8b4/rpds_py-2026.9.1-cp313-cp313-win32.whl", hash = "sha256:172e47169583f46ce118cbec68e6795d0da0f4606b488b6434f8276bca0a058c", size = 205293, upload-time = "2026-10-04T16:29:56.669Z" }, + { url = "https://files.pythonhosted.org/packages/31/9c/ba5a9569d719bfdd6ce863df4133ac6a1658cf1b07cc3534c31db729fbbc/rpds_py-2026.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:3e93b2cd69a9830be33e03945cd7cda940a0a8bfcfbff41d6144f0cb0d3d8bd9", size = 222409, upload-time = "2026-10-04T16:29:58.049Z" }, + { url = "https://files.pythonhosted.org/packages/35/72/f28ca566f6c23c35bbf7445f65eb0364577b25a026305995e24f78b83d94/rpds_py-2026.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:d151e148117294133bf8af7eeace085e7e87432db15ab6adf640330298a47f6f", size = 217681, upload-time = "2026-10-04T16:29:59.449Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f2/67b94be1532767803415c1c5a1fd88ea487643d74a673cec1ba140af77bb/rpds_py-2026.9.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c9d1aca01f49170fdcf5c92761b1fafe97f554b721ca4570c5949fff778f0d4b", size = 347178, upload-time = "2026-10-04T16:30:00.865Z" }, + { url = "https://files.pythonhosted.org/packages/04/37/b751de2b59b0197a1d92a5dd491de88e8a5e928c2e6562581974f1e85263/rpds_py-2026.9.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3f0e9ac28fc067d4d34b88ae43c48e9489455c97fee9633d851f7eeed5a05d35", size = 341878, upload-time = "2026-10-04T16:30:02.564Z" }, + { url = "https://files.pythonhosted.org/packages/72/e2/5873bc4643c250db9e05d48dc0c93763d4aa68bc3b81164cb1af3b45b284/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07deecbfce94c78473018bc7d10b337cc651d12df87a1eb2cb3e4024bc9c33d0", size = 373963, upload-time = "2026-10-04T16:30:04.026Z" }, + { url = "https://files.pythonhosted.org/packages/51/03/5acf7632158247f3f6386ff0af3a1ee48167d575037e8d0920594b76d92b/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:821b2755db9194409254012f429c56643416fb96ef9be090be82ec8826b7f477", size = 377975, upload-time = "2026-10-04T16:30:05.555Z" }, + { url = "https://files.pythonhosted.org/packages/e6/00/63fda451b8bffa5808fc8bb311ee7c073b340974b09f273c7b2a145d3d62/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c91c210ae7645626c608400e3519b4a642f837cce09ca830db3beb2e9f274d4", size = 490347, upload-time = "2026-10-04T16:30:07.156Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ae/c093ffd070ba0fb02f76c565d06fecc65ad6e4afdbae78f7031076d3cdac/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:54ac2158a6f96cfbabff0b2eedaf94b90c5ec7ca8317fcadc61e1c2b2e0ff6ef", size = 392962, upload-time = "2026-10-04T16:30:08.77Z" }, + { url = "https://files.pythonhosted.org/packages/22/9d/d08a1128ab199b2f0cf25bfeb0639bd05119fff4b7c47bec24ef9a8ec23f/rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac2f5dbafd585dfe31f86a23ebf0d3ba480a9d49ebc87947267b5608d4ea0cd", size = 371623, upload-time = "2026-10-04T16:30:10.501Z" }, + { url = "https://files.pythonhosted.org/packages/53/c7/4758ddcbb75609414bbccfcb11d612436f9b3ee821bd2f33f0f1604ee648/rpds_py-2026.9.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:8aa5dda18d39b6143eb24809d158f9252c88f402749b6f1b62a506cc7d96cc35", size = 376997, upload-time = "2026-10-04T16:30:12.124Z" }, + { url = "https://files.pythonhosted.org/packages/87/e4/947bd7f608ff60faf46dc9d389c3dffd0e3d767d78a0be19978448ef0ce7/rpds_py-2026.9.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c90e7fa02e8f5de0d10c17595c568ada48c5302e749462c0ea1a4c362111a86", size = 400169, upload-time = "2026-10-04T16:30:13.804Z" }, + { url = "https://files.pythonhosted.org/packages/4b/35/fe93e020a0543b5670472c18d7e6af3197c08da571240ce1965c84f85c0f/rpds_py-2026.9.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6d198bad4e49dd6732fbd636e2fc5c082f45c8cad0b4acb756b00c82c76072e", size = 551615, upload-time = "2026-10-04T16:30:15.332Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4f/5d2a0136bb03b2a56a39dc6ff92d58a6e3e53a2e17079238b86228882f16/rpds_py-2026.9.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:96beca19ec79de272e8668585380ff9092c47077c1d7a1e098e00bbd921f4785", size = 614655, upload-time = "2026-10-04T16:30:16.92Z" }, + { url = "https://files.pythonhosted.org/packages/09/1c/3f1025aaf70d9bf7272cc41f8b64ee76b48bf01726248138430e16f23b38/rpds_py-2026.9.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5cf77eb04f20b720be95265a3e00eb2a14814074255cc27069c551b2db53118", size = 579602, upload-time = "2026-10-04T16:30:18.555Z" }, + { url = "https://files.pythonhosted.org/packages/53/0d/5c72e6204f76610608706da32b6b7e11ef7e10317558a7bb15a122e008dc/rpds_py-2026.9.1-cp314-cp314-win32.whl", hash = "sha256:a03d57b86d2a51d0a66c92177e2be154ad015f357791d306e714569999cdb4cc", size = 206315, upload-time = "2026-10-04T16:30:20.05Z" }, + { url = "https://files.pythonhosted.org/packages/a4/0b/489d48abbcc7d70cf3fbf662d9d22abf1f4650761c0a9ae05260800800d3/rpds_py-2026.9.1-cp314-cp314-win_amd64.whl", hash = "sha256:837c6b305e26fe0f75b15c92cf3b2ba29e0ae19dc40b1c557b026cb426347d0c", size = 222852, upload-time = "2026-10-04T16:30:21.604Z" }, + { url = "https://files.pythonhosted.org/packages/91/16/bbb05a7e6a10cf79ba639be7f799d770ee15f64175cc61d081b218dd402a/rpds_py-2026.9.1-cp314-cp314-win_arm64.whl", hash = "sha256:fce4b85234a0cbad67bf8e6e1201ee815d172c9aebad75f25645bc4d834f8e31", size = 218709, upload-time = "2026-10-04T16:30:23.036Z" }, + { url = "https://files.pythonhosted.org/packages/22/ac/ac507a0a4ec478ca470440a09583db4be5259ba7670aeba0620822f1e57a/rpds_py-2026.9.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:3a72c11530d71abfb66c8d7696a2f86c43e63fca8b948f1a784ac490f4ec688e", size = 349596, upload-time = "2026-10-04T16:30:24.558Z" }, + { url = "https://files.pythonhosted.org/packages/e9/f2/817a46b658d5070f477f722c298ee9a24525b0e4017347964146ef5fdd0e/rpds_py-2026.9.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:068c37bba854ec2fe42f7365c640af11dd9895890ccbf2df5070d0c059bd7f96", size = 343114, upload-time = "2026-10-04T16:30:26.048Z" }, + { url = "https://files.pythonhosted.org/packages/6c/42/6ade976b13ac1b4cb3bf2eb603f1be2fe74df19a29988d4c2b386be59d6f/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fca4eb6df565e2a928f1c7dad92d27db8f9df0f449e76423ed5d7e713ed445", size = 374197, upload-time = "2026-10-04T16:30:27.699Z" }, + { url = "https://files.pythonhosted.org/packages/d9/70/77cdf1d3f1a07faabe936016ae623aec7981f73108a8fe7a203ed2e21998/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c933c6678c6f116ff8af47a4c6db0868b8ace74af0343016c0ef00f00272ea69", size = 378025, upload-time = "2026-10-04T16:30:29.451Z" }, + { url = "https://files.pythonhosted.org/packages/3f/6b/18a44a3beaa9b7931acb04af7bd9539836477c630a794452d4826d6185d4/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:028ad274ea951dac64491b5d1e65712a4aeabfdbdb9fccf797b57bd899b0c495", size = 491969, upload-time = "2026-10-04T16:30:30.995Z" }, + { url = "https://files.pythonhosted.org/packages/73/27/fb39cfd6bddaf741b024f813890374578ff8ac1f1adc473659c048b03b05/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:740d0a99cf9de0b17a3943388e9294a59becf75e7c43421f387bd3c7a9901f7c", size = 393905, upload-time = "2026-10-04T16:30:32.628Z" }, + { url = "https://files.pythonhosted.org/packages/ed/71/0fa7bb77b57af0d710273964180d11b503f62b8a5c358eb2d8c3f62feff6/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0da298fb372dc192610a4b9ecbc68a0cd8b675bbbd1fc519d01b41cfd658333e", size = 374569, upload-time = "2026-10-04T16:30:34.257Z" }, + { url = "https://files.pythonhosted.org/packages/54/22/f41cfac269af3b449513ef1bc3d7f32fde52abbbd2d01c7e76b47743acd9/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:eb61be926bb81567c1f48bdc8aa22b9855048dc2efd53871f9f7e6e9a5632346", size = 377482, upload-time = "2026-10-04T16:30:35.997Z" }, + { url = "https://files.pythonhosted.org/packages/b4/fc/312b49006e7f8f9ca5f96647577b8aa6f3df30519c46bd448f5c425af0b2/rpds_py-2026.9.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42e75466f83cd43f6026c81eab74246efb2bdadafb307b85700632d06c68f299", size = 400040, upload-time = "2026-10-04T16:30:37.76Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a6/18cca7a878dc7fa95165a83343fd4d7b65643fd22e54e47340a451121d5c/rpds_py-2026.9.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:617f59cde379b4f648a09797b7f683d04b90a46344cddab85639da5aff0f5531", size = 551849, upload-time = "2026-10-04T16:30:39.443Z" }, + { url = "https://files.pythonhosted.org/packages/e4/6d/1f5685e20f39604691bdc3c05aaa6b8bd2f954e9e996477adf2376768e33/rpds_py-2026.9.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3edae8c5ddfdb6985d49ae9d150516e5076888879022f91a26c2de9276ce0bdb", size = 614842, upload-time = "2026-10-04T16:30:41.231Z" }, + { url = "https://files.pythonhosted.org/packages/c6/25/98652109fd9f7e10268dd4571aa52b81987001b806f37ef1a18260de714a/rpds_py-2026.9.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0f045bb053c9057720d72c56dffe30dffdc05997b2897a827b9325f0ab6623fa", size = 582606, upload-time = "2026-10-04T16:30:43.345Z" }, + { url = "https://files.pythonhosted.org/packages/19/03/11ca09099bab5f53373a80a334c424ec917c13d050760a59499d2af5171e/rpds_py-2026.9.1-cp314-cp314t-win32.whl", hash = "sha256:bf35d0568abda97233239ce32896d3ad53fccc537832c104e30c94aa5fb93569", size = 203341, upload-time = "2026-10-04T16:30:44.954Z" }, + { url = "https://files.pythonhosted.org/packages/6f/8a/88909e3ffd9f47f5b58211473875d8c3c09079f0058c46fb72c55a702a26/rpds_py-2026.9.1-cp314-cp314t-win_amd64.whl", hash = "sha256:1e8d4d79d828299bf44a55db22a9388ab967b49d17132c88eab0f4360b48da8e", size = 222843, upload-time = "2026-10-04T16:30:46.486Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b7/a662f367d4896dd0a10cef2fc91f10b7f08af1c10858e287e019563f338d/rpds_py-2026.9.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:1d77b649e6f7cdf12ca5c2a98dad0ad37f9ea9b6f960408a92f0cb12bb3d04d9", size = 347463, upload-time = "2026-10-04T16:30:48.203Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3f/ad45d03df4f84ebae5439577ee81f3999d182711e82235c8037b6528890e/rpds_py-2026.9.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:00ba2d8c7dd4ee537978ddf4b3fbd712bef2d8751603f7f3146b3f4287768e25", size = 342051, upload-time = "2026-10-04T16:30:49.872Z" }, + { url = "https://files.pythonhosted.org/packages/7e/31/3dcd68c13d4bcc59c1f7eb33ac8e80698f06f3eb0d8a1e06419836071c20/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec450527cbf485e13c8d3602a54f428ab0432fdade0ede75efd74b735421c871", size = 374143, upload-time = "2026-10-04T16:30:51.508Z" }, + { url = "https://files.pythonhosted.org/packages/cf/0d/68c1f058a250fbd1380ebda9fc227cbf50117adf8ffe8161ef383ea79f68/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:306ee1850d8105b5baf977e78d45fcadd12c1a54678d614c9baf217708446e91", size = 378604, upload-time = "2026-10-04T16:30:53.206Z" }, + { url = "https://files.pythonhosted.org/packages/d7/d6/2d4c59b85397cb4800594fadf692688ccf5ce556adc930e7a5bf21061a5e/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef6b65b03247c54692ad4fd9ee97cb772781927db72e3cb05e70b3db6d1ff14f", size = 490790, upload-time = "2026-10-04T16:30:54.925Z" }, + { url = "https://files.pythonhosted.org/packages/da/04/7e05dc3aebaf52f4e026766bd668fdd09a9d0e23f64a14686b36b3501892/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a575404ebc9cf2e91edd32eaf570ec1430eb900d4f56724ba7dd4bc1fc9c176d", size = 393146, upload-time = "2026-10-04T16:30:56.625Z" }, + { url = "https://files.pythonhosted.org/packages/57/ca/e2e9a0a46a74ed51a0498ba1fe10f4ea6b2d9a155f372a2f91e51f18cf10/rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c16ab111bc27c646ba8aa005d0527754edc538ebb636f0b1bf8e244b48d1945", size = 372022, upload-time = "2026-10-04T16:30:58.295Z" }, + { url = "https://files.pythonhosted.org/packages/ba/cb/8f8774df5134e23424372838bcc5c7ed4127d723e1ff52f7bebd4dcb2563/rpds_py-2026.9.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:7664419f27db41d4f1c43a78dccda7dd6e8ef2428df3ee01d0c2a07a6b071297", size = 377092, upload-time = "2026-10-04T16:30:59.984Z" }, + { url = "https://files.pythonhosted.org/packages/90/02/8d7095d73bf9114219be40230baa5df00611e0a82ed9517779ff9c19f82b/rpds_py-2026.9.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b26b03d9d2658ee2fa234f8f4f19f38a09773fe5261028025032e26d4d35af0", size = 400830, upload-time = "2026-10-04T16:31:01.721Z" }, + { url = "https://files.pythonhosted.org/packages/ec/02/8206856f8f363cd042a8315dc86b3912f5dcb6d3c66bbb24b69c2bfb0775/rpds_py-2026.9.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:be3e47e2d91aa3942ff9bf4077a505226005abfc39b6f7554a91c1b9393986b9", size = 551478, upload-time = "2026-10-04T16:31:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/41/6b/36211f1bb1f0b0313f496d92f5905b74ea107f27cb16fa3355a82f04575e/rpds_py-2026.9.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:6307a0da524939decb8ca4a3933b8ab62525794411d6984fca6726e732804af6", size = 615173, upload-time = "2026-10-04T16:31:05.281Z" }, + { url = "https://files.pythonhosted.org/packages/35/77/cda0c4a6f055446b692f0ed5692f73707cafd3dff82672ede31b1a9b59de/rpds_py-2026.9.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:159a7aab5c5e8b112c8830f54717ce56da1252ebdbb526f5be2df2309280b9e7", size = 579653, upload-time = "2026-10-04T16:31:07.065Z" }, + { url = "https://files.pythonhosted.org/packages/74/ec/d8385f446240aed643b9e92a5055cff3015cc04a13c61f73b2883c478ed5/rpds_py-2026.9.1-cp315-cp315-win32.whl", hash = "sha256:dbc2673f9223d420c91145599b3ba45a8a50c207d1976908e5fb5ddb0c9b9429", size = 206498, upload-time = "2026-10-04T16:31:08.989Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a5/71b5cd00e0521e3b6b81828baea368c62b6b700ebdd9554cd7d41ddf12fa/rpds_py-2026.9.1-cp315-cp315-win_amd64.whl", hash = "sha256:75c38c50ab9aca840225d9a9a3810bf11d04bd5c1f186cabbb8aee56db3e9b15", size = 223121, upload-time = "2026-10-04T16:31:10.84Z" }, + { url = "https://files.pythonhosted.org/packages/33/58/dba857c3bc8221b31b62eb170a3080f4f191de79f047200389b7ed1b06a7/rpds_py-2026.9.1-cp315-cp315-win_arm64.whl", hash = "sha256:a431156bb41865fc14cd5d79bb9d7bbed83110b0159e34e62ae30951f96c0009", size = 218741, upload-time = "2026-10-04T16:31:12.592Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d0/320ab28ccc1415eeb509d68682b0014fb74690cd49f1c2d29a232475af50/rpds_py-2026.9.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ef0d8c843e2827d6c120ab4687e9423fb1d893db1df27b7c1506615bcb9734a0", size = 349719, upload-time = "2026-10-04T16:31:14.48Z" }, + { url = "https://files.pythonhosted.org/packages/56/88/f5b12f1358f443c08b7ce3cc8391d82d335f4872580e2e097847fd36087a/rpds_py-2026.9.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:45bc6bccf78b20fd834237d18db64965d7ee68ba7f60440a26c7ab71e7b8d51a", size = 343267, upload-time = "2026-10-04T16:31:16.827Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0c/c765b0059d532acb3b9c45d781ccc22f15a96dbe443d00903f643ba9df10/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d55198263bb51f557550c6ed2e6d1cb6a6fed6eb5c9120b741c5926bef8a45d", size = 374555, upload-time = "2026-10-04T16:31:18.931Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8a/cafddfda77564a10cd21184640c3bffda6a2b8d20972fb5dedd5e0166328/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8763f20692da7df39b0afdd1ba3042b004c50a45994f76c2d9a25641f7673db", size = 378346, upload-time = "2026-10-04T16:31:20.75Z" }, + { url = "https://files.pythonhosted.org/packages/b9/01/5e626016eff72c183bf6c96539240cace15d402468647a453ec08415b2fc/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e43d4a1f673e8a1cbd8533e809e02b4bf9d4f2280269bb640436556312121250", size = 492373, upload-time = "2026-10-04T16:31:22.614Z" }, + { url = "https://files.pythonhosted.org/packages/3b/9c/15a2469e9389242f46896b3f0a01d68caea8a5a35c011fcb05ef333aae73/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea394a937f17a54c51239348bdbe2e3518124c8d4a8951ba04a311d3095bd18f", size = 394371, upload-time = "2026-10-04T16:31:24.768Z" }, + { url = "https://files.pythonhosted.org/packages/63/f5/c100ff77e1e6366e947c75969c258fdfc4b7bc5bbfe7351e62ffbf2a1228/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdeaa99ce822dca76cfb1b993e9120c5ea212f2eb66d48950ad63c349668a018", size = 375132, upload-time = "2026-10-04T16:31:26.588Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f4/fe0269c9de253e99c81cabc12b8971a5feaa083debdaff1221e06264d9e3/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:b4f062343e7ad3fa94f2c66e5ae667dee47ee74dd41a9057c4fbe163236a123d", size = 377642, upload-time = "2026-10-04T16:31:28.677Z" }, + { url = "https://files.pythonhosted.org/packages/05/65/b34a7b257baccff8f4a24a722933166d4941d5ebdc9c3f4bc4ffcd5ce4f4/rpds_py-2026.9.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22ffd29a63d71fb1b81552c21f2c2b734949b7ac751a9be70675a939a900839b", size = 400855, upload-time = "2026-10-04T16:31:30.802Z" }, + { url = "https://files.pythonhosted.org/packages/98/32/844e54176b6071b90b38a564e6940bc6eb8f97b2890dc709c19db9dec0f4/rpds_py-2026.9.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:08dae4a4095150a7c4545a1fb40b98e1ab1744fbc2770d92c977b9dadaa49ab6", size = 552573, upload-time = "2026-10-04T16:31:32.709Z" }, + { url = "https://files.pythonhosted.org/packages/17/73/6041d20729dffbfdf155c02d65be58bc225a1c1fb548fd87c23ef306138f/rpds_py-2026.9.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:9a0460d43603d1fd9ef59c30278531e15d78581721ddb538fa560aa7817ea4ad", size = 615840, upload-time = "2026-10-04T16:31:34.565Z" }, + { url = "https://files.pythonhosted.org/packages/af/9e/418094adaee6b056ce199051b255448ed872829051e341b2294c80da0977/rpds_py-2026.9.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:1c2d1f6da5128eabf34e963d7163a818846075a52568250d006c4c953b40f903", size = 582764, upload-time = "2026-10-04T16:31:36.665Z" }, + { url = "https://files.pythonhosted.org/packages/3d/9b/1698ebf6b840ddfe8b472198abbed6ace35c6e398faeecd6c47dae742a4e/rpds_py-2026.9.1-cp315-cp315t-win32.whl", hash = "sha256:5c6ee90dee3e85e055ddfd502d611643d9b0fd94c818220bda84ec3dacd9b27b", size = 203795, upload-time = "2026-10-04T16:31:38.53Z" }, + { url = "https://files.pythonhosted.org/packages/8a/e2/91f70d804c61f8eac39a417e82aa1024f655ab9e8bdd7393b196242bc41b/rpds_py-2026.9.1-cp315-cp315t-win_amd64.whl", hash = "sha256:fe5ad0664ec772b02c45859041aa17655709cced7a31005817fbbbd988c25567", size = 223037, upload-time = "2026-10-04T16:31:40.468Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ad/f0eba548e297987964ef8088403db875b333963bf8189f3f563b6936b20e/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c0d2cb595a420b34d5086db0add011e26e2c09d6a024afbac4228bf8f863a30", size = 384112, upload-time = "2026-10-04T16:31:42.643Z" }, + { url = "https://files.pythonhosted.org/packages/33/89/891fa4d81f1eae7fe262ecd0aba2c51de94a116c7f3ccfb034fa75f4c537/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3d6ed6a98cfd19155996605474982cc470d7601746a6439078f1a5a3fa8b050", size = 389662, upload-time = "2026-10-04T16:31:44.642Z" }, + { url = "https://files.pythonhosted.org/packages/4f/46/912cf623b2263f9c9c2d04e8508fc38dbe89983faeba5fb36ca4558ae550/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8171b44a054e5c67fd748ada04187f1250bf35b95f85e52ab64bcf3331a923bb", size = 504637, upload-time = "2026-10-04T16:31:46.667Z" }, + { url = "https://files.pythonhosted.org/packages/9b/5c/7f3cfa7c5f01765c9f47c8398210ed0c1eb686c0e99c223cb1c6776d3fb9/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fda1d96e542c37b6c804547dbf489c129fe7c97183a76a5ec275909ba1a063df", size = 402607, upload-time = "2026-10-04T16:31:49.068Z" }, + { url = "https://files.pythonhosted.org/packages/f0/76/2e915d4115ee453c33e6af17bc1df7e22ddc52974e178eeeb92a78c09983/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:270bdcdaac5d5b6f73c5e22e7e135c7f2a50e789f71d9e241d5be8d90026e19a", size = 382762, upload-time = "2026-10-04T16:31:51.24Z" }, + { url = "https://files.pythonhosted.org/packages/0d/e5/844382925d7b84a4074ad79fc2b9d05eb6231ea243b34f247e68e519b606/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:84a6ecc0c940169190d2c23bd969debd48c94dbc855acd60188a68d71d421608", size = 389563, upload-time = "2026-10-04T16:31:53.549Z" }, + { url = "https://files.pythonhosted.org/packages/81/82/d6e3951602dc2e24790ef9d299aa24bf5e2493df5043f2c6da00480cc447/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ab4b2fda7c2b542f7f9d886cc6a838c5079d2b76f72e6081411faba11adde2c9", size = 416842, upload-time = "2026-10-04T16:31:55.76Z" }, + { url = "https://files.pythonhosted.org/packages/ea/1c/8d248e4ea671d525ab7daa0703ad81cc2175076fa0ccd7ef088facbd2b01/rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:44b32a7c4f0da3d28af31c259e38ddcff096f855e205ed0671d02fcf44f1ea1c", size = 562434, upload-time = "2026-10-04T16:31:57.874Z" }, + { url = "https://files.pythonhosted.org/packages/ad/38/e7ac0db898120d15dea25e3661b1242a40cb3b23d1f4db86c15a01fe0ba1/rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:a3dbc5ed9514908d5046107d7b1346bde71eea61de6e0e4919c19354f97e769f", size = 627909, upload-time = "2026-10-04T16:31:59.977Z" }, + { url = "https://files.pythonhosted.org/packages/03/c8/37e555514932cb1ee36c75349187aa882a98dee5d5654c761699ed8a0d27/rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6eae33003518fd4cb4f83a218d5371469dd3001aa3b87128c005b07762f7fe5e", size = 593658, upload-time = "2026-10-04T16:32:02.14Z" }, + { url = "https://files.pythonhosted.org/packages/12/5c/5f69a91add94a8a33acea68a2b034a464946a654ff7296af9d957ef0d6a5/rpds_py-2026.9.1-pp311-pypy311_pp80-macosx_10_12_x86_64.whl", hash = "sha256:0483515261947e4e8b8e1375bf7463e7eb6ccfb3d86e7b554d90cd5285f20f32", size = 351769, upload-time = "2026-10-04T16:32:04.675Z" }, + { url = "https://files.pythonhosted.org/packages/41/22/35ea8bf3b2682b90ab687c74e4828418ff12298a5b0b0a0c033f61c86a30/rpds_py-2026.9.1-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:4cfaf02209061880210819934de2f4f6aa83dc04dafe6770276acc240a56da31", size = 351719, upload-time = "2026-10-04T16:32:06.834Z" }, + { url = "https://files.pythonhosted.org/packages/a3/90/a9ba81408369d34c1aca73319c15adc91ed45c1d21e9e8ee4832ffb7fe0b/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cdc537c8633d7fd92a82e2e0d2ab74320a3f63d5e59fb9cf08711e08fe151c4", size = 381109, upload-time = "2026-10-04T16:32:08.994Z" }, + { url = "https://files.pythonhosted.org/packages/b3/44/5192a0bed94cec86bd2a5e1cc5a1bb8f7eec3aec907eacd2deb13e87e326/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:10e208f2425d973938afcd56e28a7c4be32e27b6a60b5d381f49fb9d8acf9759", size = 388222, upload-time = "2026-10-04T16:32:11.321Z" }, + { url = "https://files.pythonhosted.org/packages/92/cd/5356549711448f18b52f7a11ffbe90f1228774996fbf3c6cd1b18d22abaf/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c0dbbcc19735fe5f8b0a54c07659d154a9e69f47e15d0a6ab7299215daf62cb", size = 500384, upload-time = "2026-10-04T16:32:13.478Z" }, + { url = "https://files.pythonhosted.org/packages/7a/88/ddda9d28adfe33119c75b2e733d4ba7e326f41d7e1b1093951771768ca48/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:684fd492fff4fead00587544e059be2bbcb6f93454f21fa2a91b66fc7508be82", size = 401661, upload-time = "2026-10-04T16:32:15.653Z" }, + { url = "https://files.pythonhosted.org/packages/32/c3/bb59ba16a6b4a57995d15b8c04c00f28df3b938c4dcdde48fbe0f73edb0c/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1028417bb44037eb3069c1009bd7b7277212876cda22fbe565b0bca9fab6d2c", size = 380748, upload-time = "2026-10-04T16:32:17.727Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b8/0580faa1c5a32ddc160130dd7ae54d2d007272a89a40a6b5db3e50f9f585/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:492e5e428cbe126221611f47e068f01660352feec4ad18bc0f5ea9b2ae88fb14", size = 385731, upload-time = "2026-10-04T16:32:19.855Z" }, + { url = "https://files.pythonhosted.org/packages/bf/70/f73564642bbe3322c7eeef2c2f258cf040b71c41a430b532e59d04a1b838/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:88b5268892fde430d5531f95bc560b6efbbd67c929662c586afd729a96e7461c", size = 412131, upload-time = "2026-10-04T16:32:22.285Z" }, + { url = "https://files.pythonhosted.org/packages/8c/5d/17fff2e1f8f68721a2afb5cb48f7e442c751bb6b4883157c5abaf618cd08/rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:01445c8d194aa032a08e944f16567672da1c62dbdbefd8b6d0693032e290cf68", size = 558193, upload-time = "2026-10-04T16:32:24.4Z" }, + { url = "https://files.pythonhosted.org/packages/2a/d0/869ab6fb08531f97aae4a780b4d61b4190d8aef48fe054ee28bcf7114466/rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_i686.whl", hash = "sha256:eef6a03b0b6d08d0835ccfa8ec8d1bc70525e3801387567137b50c557695e6da", size = 622448, upload-time = "2026-10-04T16:32:26.844Z" }, + { url = "https://files.pythonhosted.org/packages/ba/a5/9672e532fe02cd3b92c78cfd8743abca939d96853006c63835009a1ecb6f/rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6b9bf3135b4ad5981df9a73d71a35272d650a2985ae9c2746357b24d59de2448", size = 591126, upload-time = "2026-10-04T16:32:29.274Z" }, + { url = "https://files.pythonhosted.org/packages/80/ef/3a9f8e4279c7920af561deed4cb7ebebed2794a8f608cf404d5eef14a105/rpds_py-2026.9.1-pp312-pypy312_pp80-macosx_10_12_x86_64.whl", hash = "sha256:56c6952a9b15047466d0c2347c446a761d4527f89976156341e68f0ce5cc08b0", size = 357108, upload-time = "2026-10-04T16:32:31.641Z" }, + { url = "https://files.pythonhosted.org/packages/b7/55/4b2fa381a583760aea5c92841e4e928a358e1c6511b129219e9c2826a226/rpds_py-2026.9.1-pp312-pypy312_pp80-macosx_11_0_arm64.whl", hash = "sha256:b242c27c8f836305a4a72df9cdd564386ac57b807bd252a063223331c9316b37", size = 346877, upload-time = "2026-10-04T16:32:34.061Z" }, +] + +[[package]] +name = "ruff" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/2b/69e5e412f9d390adbdbcbf4f64d6914fa61b44b08839a6584655014fc524/ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5", size = 2449817, upload-time = "2024-08-08T15:43:07.467Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/eb/06e06aaf96af30a68e83b357b037008c54a2ddcbad4f989535007c700394/ruff-0.5.7-py3-none-linux_armv6l.whl", hash = "sha256:548992d342fc404ee2e15a242cdbea4f8e39a52f2e7752d0e4cbe88d2d2f416a", size = 9570571, upload-time = "2024-08-08T15:41:56.537Z" }, + { url = "https://files.pythonhosted.org/packages/a4/10/1be32aeaab8728f78f673e7a47dd813222364479b2d6573dbcf0085e83ea/ruff-0.5.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00cc8872331055ee017c4f1071a8a31ca0809ccc0657da1d154a1d2abac5c0be", size = 8685138, upload-time = "2024-08-08T15:42:02.833Z" }, + { url = "https://files.pythonhosted.org/packages/3d/1d/c218ce83beb4394ba04d05e9aa2ae6ce9fba8405688fe878b0fdb40ce855/ruff-0.5.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf3d86a1fdac1aec8a3417a63587d93f906c678bb9ed0b796da7b59c1114a1e", size = 8266785, upload-time = "2024-08-08T15:42:08.321Z" }, + { url = "https://files.pythonhosted.org/packages/26/79/7f49509bd844476235b40425756def366b227a9714191c91f02fb2178635/ruff-0.5.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a01c34400097b06cf8a6e61b35d6d456d5bd1ae6961542de18ec81eaf33b4cb8", size = 9983964, upload-time = "2024-08-08T15:42:12.419Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b1/939836b70bf9fcd5e5cd3ea67fdb8abb9eac7631351d32f26544034a35e4/ruff-0.5.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc8054f1a717e2213500edaddcf1dbb0abad40d98e1bd9d0ad364f75c763eea", size = 9359490, upload-time = "2024-08-08T15:42:16.713Z" }, + { url = "https://files.pythonhosted.org/packages/32/7d/b3db19207de105daad0c8b704b2c6f2a011f9c07017bd58d8d6e7b8eba19/ruff-0.5.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f70284e73f36558ef51602254451e50dd6cc479f8b6f8413a95fcb5db4a55fc", size = 10170833, upload-time = "2024-08-08T15:42:20.54Z" }, + { url = "https://files.pythonhosted.org/packages/a2/45/eae9da55f3357a1ac04220230b8b07800bf516e6dd7e1ad20a2ff3b03b1b/ruff-0.5.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a78ad870ae3c460394fc95437d43deb5c04b5c29297815a2a1de028903f19692", size = 10896360, upload-time = "2024-08-08T15:42:25.2Z" }, + { url = "https://files.pythonhosted.org/packages/99/67/4388b36d145675f4c51ebec561fcd4298a0e2550c81e629116f83ce45a39/ruff-0.5.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ccd078c66a8e419475174bfe60a69adb36ce04f8d4e91b006f1329d5cd44bcf", size = 10477094, upload-time = "2024-08-08T15:42:29.553Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9c/f5e6ed1751dc187a4ecf19a4970dd30a521c0ee66b7941c16e292a4043fb/ruff-0.5.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e31c9bad4ebf8fdb77b59cae75814440731060a09a0e0077d559a556453acbb", size = 11480896, upload-time = "2024-08-08T15:42:33.772Z" }, + { url = "https://files.pythonhosted.org/packages/c8/3b/2b683be597bbd02046678fc3fc1c199c641512b20212073b58f173822bb3/ruff-0.5.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d796327eed8e168164346b769dd9a27a70e0298d667b4ecee6877ce8095ec8e", size = 10179702, upload-time = "2024-08-08T15:42:38.038Z" }, + { url = "https://files.pythonhosted.org/packages/f1/38/c2d94054dc4b3d1ea4c2ba3439b2a7095f08d1c8184bc41e6abe2a688be7/ruff-0.5.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a09ea2c3f7778cc635e7f6edf57d566a8ee8f485f3c4454db7771efb692c499", size = 9982855, upload-time = "2024-08-08T15:42:42.031Z" }, + { url = "https://files.pythonhosted.org/packages/7d/e7/1433db2da505ffa8912dcf5b28a8743012ee780cbc20ad0bf114787385d9/ruff-0.5.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a36d8dcf55b3a3bc353270d544fb170d75d2dff41eba5df57b4e0b67a95bb64e", size = 9433156, upload-time = "2024-08-08T15:42:45.339Z" }, + { url = "https://files.pythonhosted.org/packages/e0/36/4fa43250e67741edeea3d366f59a1dc993d4d89ad493a36cbaa9889895f2/ruff-0.5.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9369c218f789eefbd1b8d82a8cf25017b523ac47d96b2f531eba73770971c9e5", size = 9782971, upload-time = "2024-08-08T15:42:49.354Z" }, + { url = "https://files.pythonhosted.org/packages/80/0e/8c276103d518e5cf9202f70630aaa494abf6fc71c04d87c08b6d3cd07a4b/ruff-0.5.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b88ca3db7eb377eb24fb7c82840546fb7acef75af4a74bd36e9ceb37a890257e", size = 10247775, upload-time = "2024-08-08T15:42:53.294Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b9/673096d61276f39291b729dddde23c831a5833d98048349835782688a0ec/ruff-0.5.7-py3-none-win32.whl", hash = "sha256:33d61fc0e902198a3e55719f4be6b375b28f860b09c281e4bdbf783c0566576a", size = 7841772, upload-time = "2024-08-08T15:42:57.488Z" }, + { url = "https://files.pythonhosted.org/packages/67/1c/4520c98bfc06b9c73cd1457686d4d3935d40046b1ddea08403e5a6deff51/ruff-0.5.7-py3-none-win_amd64.whl", hash = "sha256:083bbcbe6fadb93cd86709037acc510f86eed5a314203079df174c40bbbca6b3", size = 8699779, upload-time = "2024-08-08T15:43:00.429Z" }, + { url = "https://files.pythonhosted.org/packages/38/23/b3763a237d2523d40a31fe2d1a301191fe392dd48d3014977d079cf8c0bd/ruff-0.5.7-py3-none-win_arm64.whl", hash = "sha256:2dca26154ff9571995107221d0aeaad0e75a77b5a682d6236cf89a58c70b76f4", size = 8091891, upload-time = "2024-08-08T15:43:04.162Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "sse-starlette" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/43/a232e3cd804293bd4f6d01e8895bc0f6f83ea148d712baf9d783c7d93d23/sse_starlette-3.7.0.tar.gz", hash = "sha256:326416da2ab52e9a41b3f409d3ff897eda45aa68c9f3b7b17d0913aab8d219c3", size = 43869, upload-time = "2026-11-07T11:15:49.389Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/11/25cabac0a24fa76f58637d8671b549c90d818d69d26beecf0c59a3b186e0/sse_starlette-3.7.0-py3-none-any.whl", hash = "sha256:f1d3136caa12d129284fca3d2ded79bc9af943320b6472712955fea52a6d3eea", size = 18432, upload-time = "2026-11-07T11:15:47.86Z" }, +] + +[[package]] +name = "starlette" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/bb/3d8757f21e9a53d9035c9eb039a777047139a16bfdbedfc7a58241c3d914/starlette-1.10.0.tar.gz", hash = "sha256:b8399098d4c1e3522734f7235dc79b1fc967f96cf139c81e0fa30479e0685f9b", size = 2734356, upload-time = "2026-10-24T15:04:12.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/2e/947cfe02def9371c07cd5bf5b6b61c829e9aecb9904db02a66cfafbc1eec/starlette-1.10.0-py3-none-any.whl", hash = "sha256:478c0ab3b4ced6e82719849c118fa406f0278a3499c1a898cd15cd31bdc38ce4", size = 80571, upload-time = "2026-10-24T15:04:10.371Z" }, +] + +[[package]] +name = "temporalio" +version = "1.36.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nexus-rpc" }, + { name = "protobuf" }, + { name = "types-protobuf" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/82/e1cdd6b33af79e38d2e3db350821d62b6daf188bc905013852dd480929bd/temporalio-1.36.0.tar.gz", hash = "sha256:e65a9d30a0e3647cb92f2b19254447b5297e26f0f5edd323b27ee406367543e4", size = 2923753, upload-time = "2026-11-11T01:08:39.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/82/3696a76e35452f1960764027b41bff6c52ad5f70cd9d968dd49dc0efdd5c/temporalio-1.36.0-cp311-abi3-macosx_10_12_x86_64.whl", hash = "sha256:042f00e7ad1935c017ca4e964af1c88a1d45ac21f73e455b3f81b2c55a0bcab5", size = 14091242, upload-time = "2026-11-11T01:08:18.938Z" }, + { url = "https://files.pythonhosted.org/packages/a0/79/908bc9218f248f45581f0d4dce338ba5ae2b9fd1b5d20f2dd00b67e54366/temporalio-1.36.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:f3218255207aa37313adc0ae46332089411cb6e240d82a415ba39f8da4de64bd", size = 13648068, upload-time = "2026-11-11T01:08:22.372Z" }, + { url = "https://files.pythonhosted.org/packages/66/b3/4d6dd7d57901e7038263775c84312f523a31bbeb3a5d45958f782f18e36a/temporalio-1.36.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:318f6b3f2315cf1c06c7896ab6c312791b6f9a426bab0a80c42a57af156acc20", size = 14050456, upload-time = "2026-11-11T01:08:25.454Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b6/0572b6211bcceda6b657c13e726ad2ce2959a5228d002d1bd327610de5af/temporalio-1.36.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed457244ed5185a3e4a2ecccd40bff1d11583380b9b80ecf314589463540f3f8", size = 14524738, upload-time = "2026-11-11T01:08:28.079Z" }, + { url = "https://files.pythonhosted.org/packages/1e/9a/ffb5f15773b87cb461f5eeb02f5b6ff493e77e08067e9e0f7da4f57e22da/temporalio-1.36.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d45faa8cb1cc047990e4d61c755e34a843d557e858667da9b175c550373607ab", size = 14132850, upload-time = "2026-11-11T01:08:30.474Z" }, + { url = "https://files.pythonhosted.org/packages/46/e2/d3fde3487dc3430e3c3291e90e52788f47183fc80c2c1dc08e0aa4eade86/temporalio-1.36.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8cf5dfe59a54d2ff1e8a9e3397457661bf560dcd3b9b9ddc07f7c6757f8a3dac", size = 14627513, upload-time = "2026-11-11T01:08:33.428Z" }, + { url = "https://files.pythonhosted.org/packages/76/d8/95ec26dcb35ef2dd35eae52e9e5223f148b21c05344d46ca29d746ebefe9/temporalio-1.36.0-cp311-abi3-win_amd64.whl", hash = "sha256:74940ad05bebf84e1c53a9f10a69addd10b819985286cd5e05c8b48340ded815", size = 15472448, upload-time = "2026-11-11T01:08:36.551Z" }, +] + +[[package]] +name = "temporalio-agent-framework" +version = "0.0.0" +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python#b5ed079cb18e313199ef6437de491e536144623f" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "pydantic" }, + { name = "temporalio" }, +] + +[package.optional-dependencies] +mcp = [ + { name = "mcp" }, + { name = "mcp-types" }, + { name = "temporalio-mcp" }, +] + +[[package]] +name = "temporalio-mcp" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mcp" }, + { name = "mcp-types" }, + { name = "pydantic" }, + { name = "temporalio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/d2/6b1a7cfc08b6554862e241526cee3ad5a91bfe14baa854b6cf20c9885781/temporalio_mcp-0.3.0.tar.gz", hash = "sha256:c42ab0ab878fb2b4105822b28b199a438c1c8c9f81051a847234eb517b4ce408", size = 17497, upload-time = "2026-11-09T20:32:26.624Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/26/22398c7a39f43999167e8800f1860b867b014fea3903c8d6c2109cbc5a82/temporalio_mcp-0.3.0-py3-none-any.whl", hash = "sha256:90b030e50382a246546528dcf85557b3428837dd28e6f13675fa72ade021777b", size = 21388, upload-time = "2026-11-09T20:32:25.297Z" }, +] + +[[package]] +name = "temporalio-samples-agent-framework-mcp" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "agent-framework-openai", marker = "python_full_version >= '3.11'" }, + { name = "temporalio-agent-framework", extra = ["mcp"], marker = "python_full_version >= '3.11'" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "poethepoet" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "agent-framework-openai", marker = "python_full_version >= '3.11'", specifier = ">=1.17,<2" }, + { name = "temporalio-agent-framework", extras = ["mcp"], marker = "python_full_version >= '3.11'", git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy", specifier = ">=1.4.1,<2" }, + { name = "poethepoet", specifier = ">=0.36.0" }, + { name = "pytest", specifier = ">=9.0.3,<10" }, + { name = "pytest-asyncio", specifier = ">=1.3,<2" }, + { name = "ruff", specifier = ">=0.5.0,<0.6" }, +] + +[[package]] +name = "tomli" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/78/9ad63712633ed3ab5cc1a648d863d7e7da371e9425e209555a0fe711b695/tomli-2.5.0.tar.gz", hash = "sha256:264507556cd8b8c8e7c6ee037cdf443a463f03f4c958e57195e3d369711b8ff6", size = 17662, upload-time = "2026-10-07T12:23:37.892Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/ab99b60ee52acd949684febabc3005d0045d0f66bebd9cdebd67372d26dd/tomli-2.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c4dc1c1781f2f716de763d1e9a7b34c6a894e167e291c7c5d16c72f7a9538545", size = 163901, upload-time = "2026-10-07T12:22:15.601Z" }, + { url = "https://files.pythonhosted.org/packages/bc/00/ee01b7ed4579180fff07142d290257f25ba786f23f3ec6005f620933c2f5/tomli-2.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eff8babca5a7999bc137acbc7482a8b7e17ffca5075ab41f5d770ab408c7bfef", size = 163756, upload-time = "2026-10-07T12:22:16.957Z" }, + { url = "https://files.pythonhosted.org/packages/72/c2/4efebf65372f6583185f79799312109dddb61102d47e5c33dcfd1a297aca/tomli-2.5.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86665cee9c4835b7a7f1e8ec2c719b5258d4dc782887aded5a8ae7352a96843b", size = 268038, upload-time = "2026-10-07T12:22:18.135Z" }, + { url = "https://files.pythonhosted.org/packages/53/07/5850468e925d898abb36038666f9c333a94d2a223e802a8ba5b6d319d23f/tomli-2.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e369fd63331746182360977b1892bfc215476a30d61612d732425311639f56", size = 276422, upload-time = "2026-10-07T12:22:19.567Z" }, + { url = "https://files.pythonhosted.org/packages/b4/87/f293984cdcf83c054196d4fd3dad44fc68ae55b4b8c44bc76cef360c3150/tomli-2.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ad1ea345759240d6463efa0ed1c704402752e49aa21476620738d74d72d8aa1", size = 272616, upload-time = "2026-10-07T12:22:20.794Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ce/db582886b3c1219d3fec93ebd669332482e5aee7a91e0f7838d84f2d1759/tomli-2.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:96243987194634bd411066ce40c952e108f86af04db533ecd8ac3ff2a85b1885", size = 276593, upload-time = "2026-10-07T12:22:22.12Z" }, + { url = "https://files.pythonhosted.org/packages/bf/72/7619b87dea4261fc27dd7b54c4461c129c1f7d9bb7ba3aec89c797a431b8/tomli-2.5.0-cp311-cp311-win32.whl", hash = "sha256:610b27d99f28ec5f191c7064a48f3ddb179a1fe6ca73d571483ae859f57b605e", size = 101830, upload-time = "2026-10-07T12:22:23.651Z" }, + { url = "https://files.pythonhosted.org/packages/1e/74/220106da34502304b6751a2a9b8a9fbca6c3fd47e737a2e2e3da7c61c9db/tomli-2.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:c804ae44fe7b4bab5da295e4f980a1ff04670bca9d23fe0a4e887e08ebd741a8", size = 112742, upload-time = "2026-10-07T12:22:24.972Z" }, + { url = "https://files.pythonhosted.org/packages/27/99/7d9c8b41837a7773613e169504147375c157a290167aa59ad74a085f521f/tomli-2.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:cfac177ebd6236003846ea339981f71457cb6eb748f23381eb257e45092e3980", size = 109332, upload-time = "2026-10-07T12:22:26.117Z" }, + { url = "https://files.pythonhosted.org/packages/52/ed/7baa86f87493646a594de388c7c1c40a39dd0461f7e9c0359cbeefc91fe8/tomli-2.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f4a40d03fb9f63424f0979855bdeaf44dd7696b8d59501822c10ed30ba532df", size = 164854, upload-time = "2026-10-07T12:22:27.444Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b1/44c0341f2224397855723c7a8a39f718ea6fcbcc3dacc66e5aeca0f334e3/tomli-2.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ebf8d19b17bd0daeb7b7dec81a946a439b753942fd0210d6e96c532249eea6b", size = 164074, upload-time = "2026-10-07T12:22:28.679Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e2d5b7d3fba47adedb23de616c16d428ea076c79a3d8e1d95d649ffe197e/tomli-2.5.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf0b5e8e0f68ebb494356e577c06c139161efd8d3b9050f93b39b7c26cc54ff0", size = 274274, upload-time = "2026-10-07T12:22:29.804Z" }, + { url = "https://files.pythonhosted.org/packages/43/90/6090e706ff27a6f89f4a40578e3324b95c3cd8c4150868aabf33a8f414c3/tomli-2.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cf74416bdc94ae458b14e37286c1073081850ac8459a00d0c5efef5d44294c6", size = 286435, upload-time = "2026-10-07T12:22:31.297Z" }, + { url = "https://files.pythonhosted.org/packages/0a/9e/a2c40768df16c408f22430afb0a73e9d7e5f79c950884954649d1146b74d/tomli-2.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:61ea1ebe1e55a34ea8199cc8dbff398d35027b82271c8ac4802fd3a1fd5b1bcc", size = 278119, upload-time = "2026-10-07T12:22:32.601Z" }, + { url = "https://files.pythonhosted.org/packages/12/25/3c0cb485b98e9cfac495629b1c93c87ccf0b72fbe9d2689fd8fe62c6d5a3/tomli-2.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed53f7e89bb04f6d9e8e7799112360b0c4d5cbff067de0814c98c37c39b920f7", size = 286177, upload-time = "2026-10-07T12:22:33.745Z" }, + { url = "https://files.pythonhosted.org/packages/77/8b/0144c65f0e37e51c18d04ae15c21b19431c165002d0131fe9aa8b0b8b1e8/tomli-2.5.0-cp312-cp312-win32.whl", hash = "sha256:e7ad033e27a516a233bea839cdb77b80146facb3b4f40bf02cd0cac165cdd5c2", size = 102760, upload-time = "2026-10-07T12:22:34.887Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/5d6d8f42fc9a05fce69354e00ff256484192f5f2fc9a2165718fa0de61ec/tomli-2.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd05de8c1698f8413dd7d869492693a0bf2211543b787ac78cd5e7536af1a6d7", size = 112722, upload-time = "2026-10-07T12:22:36.162Z" }, + { url = "https://files.pythonhosted.org/packages/30/65/df18032218db0fb9b769fb23c8039a051f15c811993995ea04c350273a32/tomli-2.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:069435bd5480429b98c5e5afb02ab21c219b6f0064680671c6dc0d46817346ea", size = 109534, upload-time = "2026-10-07T12:22:37.296Z" }, + { url = "https://files.pythonhosted.org/packages/42/e5/51736d70da209350969e15aca5c5ab6e2ce1ea87a0a892a6c13aec172a86/tomli-2.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:943276cf269e0071948d9ff697159c1735e623c1151d88abb09b74659ef0cbea", size = 163328, upload-time = "2026-10-07T12:22:38.373Z" }, + { url = "https://files.pythonhosted.org/packages/ec/55/086f80dab4ab497602644274e6dea7ec5dd0b4e262e443a8ad3bb7edee2d/tomli-2.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:463b16086865b97facd8d0b3fb4cb7c544e3f58d2a69dc3113d6db9653fdb043", size = 162246, upload-time = "2026-10-07T12:22:39.673Z" }, + { url = "https://files.pythonhosted.org/packages/aa/eb/3ecc94459f3635c92321f4e7bde571323fdb2267c50e19e3188a281eae3b/tomli-2.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1245a6638fc4bb0a60af38a7d45413db34a13842027c77597c712c998c62fdf0", size = 272655, upload-time = "2026-10-07T12:22:41.08Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d7/494fd1f0c37a621f1ad9975c2efadb523e8101f144ed6edb2e7fe64738f2/tomli-2.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d8bac3d603c97e6854424e5b2b5b741bdbde387e09f162fb0446812b4a8362b", size = 283595, upload-time = "2026-10-07T12:22:42.222Z" }, + { url = "https://files.pythonhosted.org/packages/70/51/bb8d62b1317e6640866f6949b2d5855e5300f2c99d46de1cd245570bba65/tomli-2.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:21e4cae4114aba25aa0d4f85cdf486d290fb35c0954d7bba536248da64d43066", size = 276253, upload-time = "2026-10-07T12:22:43.625Z" }, + { url = "https://files.pythonhosted.org/packages/66/f4/f46bd7f0763cd47de2db697dca9257c6a4adfd1a93b018cc75c8190ed5a8/tomli-2.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bbaefc84548d754be821bba7c4141c4787dda182f9e77f2f87b71213529efa7b", size = 283582, upload-time = "2026-10-07T12:22:44.983Z" }, + { url = "https://files.pythonhosted.org/packages/ac/03/70f2bcb2923a6db37818d917e124270a7f4cfd38ea576f5aa753a91c0ef5/tomli-2.5.0-cp313-cp313-win32.whl", hash = "sha256:abdbf6313b8d9efe157edeb7ab6eae4de064b1300ad31abf73755154b30abe68", size = 102628, upload-time = "2026-10-07T12:22:46.508Z" }, + { url = "https://files.pythonhosted.org/packages/dc/98/d52024bb5b0ff68b4f0d276d867f634c84a67319a7e9f6b7708a37742333/tomli-2.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd4dc129784e0c5335bd4e61dfcc4487499a013419e655cf2da1d091b7e0efdc", size = 113301, upload-time = "2026-10-07T12:22:47.647Z" }, + { url = "https://files.pythonhosted.org/packages/6f/f2/540db3a70572a8c23a28aba3e9c358ce0ffffbafc990905c1343aa265b31/tomli-2.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:69491c143d2fe063046e0301e62a810bed338fa4d1ce0fd870c27dc1e09b0d84", size = 109744, upload-time = "2026-10-07T12:22:48.925Z" }, + { url = "https://files.pythonhosted.org/packages/e4/49/caf6b307766eb9567664a8707e9d6be5fcc0e8903f18781c6677a60d80c7/tomli-2.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d3182ee2d887e507bd67319a0a61105d1dd33facc111329559a233b772c1a105", size = 162899, upload-time = "2026-10-07T12:22:50.088Z" }, + { url = "https://files.pythonhosted.org/packages/d3/c8/68cfce773a2733a49c74f99d627fb461bd990756860099eac25617889585/tomli-2.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:521345fd1f19d45b8df87657aaa38b6f2ca3800059fadf428e7ebf479a383646", size = 162080, upload-time = "2026-10-07T12:22:51.558Z" }, + { url = "https://files.pythonhosted.org/packages/7e/b2/e5bb8651fdad593f670501a7d718b1a7f73f064d44dea15e04c04dfef45d/tomli-2.5.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e95c7614e705bfe2b04b27aa124adec59752d15813df37e2156747cab3a006b", size = 273380, upload-time = "2026-10-07T12:22:52.918Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d2/9e2d7f8b1dfe0e2b34c245986ebd55c4c553ea4ce6c47c443b332673253f/tomli-2.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ac2027d37c3afbdf4bdd377f2676f6f1d2122a5be1f1137b49dced590b37e75", size = 283228, upload-time = "2026-10-07T12:22:54.173Z" }, + { url = "https://files.pythonhosted.org/packages/ba/df/ec7b876b7b1a2718bd74a3743c076fff565b04029ba33e8f61fac262739f/tomli-2.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c414be4ed9d3cac80c42e348fa5a956117d1a48227f48026e31f59cb4a7671eb", size = 277189, upload-time = "2026-10-07T12:22:55.342Z" }, + { url = "https://files.pythonhosted.org/packages/7d/7b/e192d9eed0b9cb80da799f4d77052297fb9a2c3cc9b19f571f56ea88add6/tomli-2.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9b03d7dc168353b4132965bde20feceabaa470e570c6f59660dfae59b1f9eeb3", size = 283632, upload-time = "2026-10-07T12:22:56.735Z" }, + { url = "https://files.pythonhosted.org/packages/84/50/ff94454e75461d75623e47401ed323d65c10aab8fe9033242c20cd2fdf32/tomli-2.5.0-cp314-cp314-win32.whl", hash = "sha256:6f041843c4d3a37245c0c056fd955b186bf8b1fb85690cbe40b81230891dc34b", size = 103535, upload-time = "2026-10-07T12:22:58.084Z" }, + { url = "https://files.pythonhosted.org/packages/54/0b/bdacf05f963bd6026ebf6eeb0beda847d1d60e03e440725c64a4e08a0afd/tomli-2.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:f4b653094e18f9031102d3a1da5c729c8f222d85225b18037dac621695e46e1a", size = 114621, upload-time = "2026-10-07T12:22:59.2Z" }, + { url = "https://files.pythonhosted.org/packages/61/99/53f438fa6ae4f9d4ed0ddde3e7242b3bdc34b48c8f9948b72b9e9b127676/tomli-2.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:3f89d10c1ff6a38d992c27fc8a4816af71a909e08a40ec66934240b1e74347c3", size = 111572, upload-time = "2026-10-07T12:23:00.479Z" }, + { url = "https://files.pythonhosted.org/packages/b9/20/1f88f19427d380a40e90a770e087489eaafe4aeee070ae88ed2bbec00acd/tomli-2.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:e9e15b4a6c7dd6b85b5fbab29488a73f1f70de516942308daa266bf0e0aeb0d4", size = 171814, upload-time = "2026-10-07T12:23:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/d0/56/cbe5079c9f9a54b9b3e27fc82f08f3cb36edee75561679f53d2380c801d6/tomli-2.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e12bbcd32897272fb05929110362ae9ff4c1b9bb26bd9e971e71dcd3275b4c3d", size = 171324, upload-time = "2026-10-07T12:23:03.18Z" }, + { url = "https://files.pythonhosted.org/packages/2b/30/1d53fd3b0f1cb3ba542e345ec32c26aefdddc4e829e4f3429af8a4f27782/tomli-2.5.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20aa36de8f2cf87237143bc1fa1aae8d6612c09118f4da21c6a684db5dd1f6f9", size = 297441, upload-time = "2026-10-07T12:23:04.345Z" }, + { url = "https://files.pythonhosted.org/packages/66/d9/0800acb6a111686f764c1b91ef15cc42a20a66a46013bb42220f1d2c61c1/tomli-2.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22185fad8a1e622f064e78008018a0dd3323550dcb479cb7a1d296888d74024f", size = 307476, upload-time = "2026-10-07T12:23:05.671Z" }, + { url = "https://files.pythonhosted.org/packages/e8/63/30a8f3cd51b5bec37f04744bad0b0dc6160df84aad4f27b0e9283d66f221/tomli-2.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:984012f71908165449a951de2050d52f276bfe3aa5d5f570f63ddad814370374", size = 296113, upload-time = "2026-10-07T12:23:07.202Z" }, + { url = "https://files.pythonhosted.org/packages/ab/18/0b9ffc597e69c5a1e20a7823cb60d54b39a9f54e91edcb8574f022186758/tomli-2.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f79203b3965b4000e91808aaa7c040206093f2b8bf86f455982f2274c9ccf442", size = 307725, upload-time = "2026-10-07T12:23:08.508Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c7/18f8baae0b5607a60e8e19b4a7fedee43a8ff6458e3896dcbbadeeac9c22/tomli-2.5.0-cp314-cp314t-win32.whl", hash = "sha256:91294a9fb94a75542f6e46e4a2ae709bd8d9b51134098cae5cf3bea5478b6d03", size = 108546, upload-time = "2026-10-07T12:23:09.956Z" }, + { url = "https://files.pythonhosted.org/packages/72/34/4cca9739254130627bde87500b3f2b512154fe2f278efa7e2a5e10ad4bcb/tomli-2.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f15e3e0b835a6d68b10c86bf80a3149780498d6911c93c3ffd1861d19f9200f1", size = 117814, upload-time = "2026-10-07T12:23:11.486Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fb/afa530d47dd80a78fce43beac6bc6e00f84558eafcffbc6f37b21e80d056/tomli-2.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6664b7ae7af7294256c53960a6103077f4914cec8ff98479c352f622c6f6b2f0", size = 115188, upload-time = "2026-10-07T12:23:12.728Z" }, + { url = "https://files.pythonhosted.org/packages/66/98/316fdc00f8c0939e6fe50461dd343c162d3ad51d1286eb25b7db54361d50/tomli-2.5.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:a525685c2f97da40762b8695eb7aa0af4c8344ca1905c73e4e29cb04d34607dc", size = 162775, upload-time = "2026-10-07T12:23:13.941Z" }, + { url = "https://files.pythonhosted.org/packages/c5/22/7b10fa5bb01c9539f53f69b619361b19350acc73657772ea7ac70ba309a8/tomli-2.5.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:9dbb18c1cfb2f6517942fc9314437f66aa06d94436ffb1f06102ef3572f35276", size = 161406, upload-time = "2026-10-07T12:23:15.215Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e7/1a069d86dfd20f1f84f71c63faed9f83c1d890bc06c27d82dc7d888fb573/tomli-2.5.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:752e8b1aa6a4367ef8bf6a1a1e005540f7ed055ba36d7193796812ca5404eb52", size = 273855, upload-time = "2026-10-07T12:23:16.471Z" }, + { url = "https://files.pythonhosted.org/packages/ae/83/d1ef43d1687d092ab9c235455c76e6e709483b346b056f086095c7c263a5/tomli-2.5.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c47300f9bf791808f77d82747691c4bb09cb14bdf3060cca99b42cdc4361d5a7", size = 284910, upload-time = "2026-10-07T12:23:18.166Z" }, + { url = "https://files.pythonhosted.org/packages/cc/05/f4d9cf7de61822ece0c3873f30d291e324911c71a378b8bfe5ced13fd9f5/tomli-2.5.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:19b0dd8749f4ea2f112c5fcfb3c5248390c899d7e2e173f1d91abee1fa0ff391", size = 277723, upload-time = "2026-10-07T12:23:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/42/28/78262493141fa543151cf005760c3cb01d09fc28a11f993c05109902cb8c/tomli-2.5.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:57b1c3b01fab802e2899bc3d168dca320e14165e2fd9fd584760fb4ca5826859", size = 285115, upload-time = "2026-10-07T12:23:20.698Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b9/e1dab9a30bcb677b5cc5cee810609cfd64f24306a3055767dd3fda00b1e0/tomli-2.5.0-cp315-cp315-win32.whl", hash = "sha256:667e521b37a6c5ccaa044202c235b530f90177ffe2cd4a64ecc213c7dd535feb", size = 103475, upload-time = "2026-10-07T12:23:21.941Z" }, + { url = "https://files.pythonhosted.org/packages/4c/bd/31a3790c11d6ea95fcf5e6022ac0f8d0543c9b61120b730fc481bd43d3b4/tomli-2.5.0-cp315-cp315-win_amd64.whl", hash = "sha256:d747252933c8a65ef6bd8da0fbb7ce28a90eb6119d8cd00772cd528aa07b68d5", size = 114589, upload-time = "2026-10-07T12:23:23.098Z" }, + { url = "https://files.pythonhosted.org/packages/47/a2/4f6310fa699364f0e3af7ee3af88dddd9af066d33e716a0265bbe2b3ea84/tomli-2.5.0-cp315-cp315-win_arm64.whl", hash = "sha256:75dbcde8751b0a960aa3de173aa5e894d590755c6d7758b7e774c06f1dc3cbdd", size = 111493, upload-time = "2026-10-07T12:23:24.233Z" }, + { url = "https://files.pythonhosted.org/packages/68/14/00853f0b396d8971107ae1921bb5b322fdee1650d2f16bf06c20adb532e5/tomli-2.5.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:2419c2a189551987b59d80e63ec355671283336f41c6b9b89462df679c7d0c57", size = 171380, upload-time = "2026-10-07T12:23:25.512Z" }, + { url = "https://files.pythonhosted.org/packages/89/ad/fa6949321dadee46b27363974fb197b94c911c3b0f7a5fd26d7dc18fc2a0/tomli-2.5.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:0dc598040da8d42cf20f0be588ed7004f46db12a0ac6c32e03a59dccedaaadcd", size = 170553, upload-time = "2026-10-07T12:23:26.855Z" }, + { url = "https://files.pythonhosted.org/packages/53/aa/3056c919eb3e084df3752b2cf5f865dcc04af0b27dba2f66d7b28af4633a/tomli-2.5.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49096930c8d886c9bbdab62d2d0d17ce823ddeea522309a190b36245d5b49e01", size = 294428, upload-time = "2026-10-07T12:23:28.132Z" }, + { url = "https://files.pythonhosted.org/packages/96/b2/faeeb5d8769ea3832021d73e892c8391eae7b4b4f8b55a789127bd8b18a9/tomli-2.5.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8ade5023067f99fe72b88accd30d0ea05a158e9e32a11f124e731ea9695313f", size = 304909, upload-time = "2026-10-07T12:23:29.381Z" }, + { url = "https://files.pythonhosted.org/packages/f6/52/f094c09e73fb654b621716d019acb5d29bdfd1be01df80c281d552bda48d/tomli-2.5.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:b69564772b5c8f22ea5f498dff08cfa825045b4d4c4400529000bdf818aa3b2a", size = 293220, upload-time = "2026-10-07T12:23:30.608Z" }, + { url = "https://files.pythonhosted.org/packages/86/f5/0c30541078ca4b505ce3bd76ed931facbfec524dd018535d691d1af0a6d2/tomli-2.5.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:8ff3a2ca028c7eee0c777f9a092038d0a594a9fa04e215f929a22c329e2cb142", size = 305705, upload-time = "2026-10-07T12:23:32.181Z" }, + { url = "https://files.pythonhosted.org/packages/05/74/590e7d19d6a118fc5cc5704ff358e21d95b8573f6b9443b1519f29ca8825/tomli-2.5.0-cp315-cp315t-win32.whl", hash = "sha256:62fc1bc8eb03e3a9cadfca713d65614ed8e09d974a283295ffe3a831976b4dc5", size = 108432, upload-time = "2026-10-07T12:23:33.496Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b8/63a75cfb27a17c38550e44025d3a6e7be64516fd8608a3b75703bf37d81b/tomli-2.5.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f3fcbc57b1791fa6cbe5d8434179d51de12be1a4811469529f47f6e7487a2571", size = 117281, upload-time = "2026-10-07T12:23:34.648Z" }, + { url = "https://files.pythonhosted.org/packages/72/01/e8c1debb2173973372934c68fc8e46170ab60ef23ed4592dff4dec6e8993/tomli-2.5.0-cp315-cp315t-win_arm64.whl", hash = "sha256:d2ba24db8a9376921b5e87b4762b9adb0f3f1deaea68f2b8b0bb2c11efb9c3e7", size = 115069, upload-time = "2026-10-07T12:23:35.77Z" }, + { url = "https://files.pythonhosted.org/packages/60/3f/3e3f8fd0919249b0200c80fbc4f9a1e70be19f9883da71dfb7f8b9ab8aca/tomli-2.5.0-py3-none-any.whl", hash = "sha256:32a7b79ac57a2e83670ce329ccf675798bc5a2094783a63676866b70503f2e2b", size = 14765, upload-time = "2026-10-07T12:23:36.875Z" }, +] + +[[package]] +name = "truststore" +version = "0.10.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/9f/c5201d42a484c061e528825fc8e2d565f5abd50a4ced6fb7d29c4ec99b2b/truststore-0.10.5.tar.gz", hash = "sha256:30d36967ccaded5cbb38d602c433f53600036c79d502f4533a49b60a03bbefcd", size = 28091, upload-time = "2026-10-12T22:27:31.808Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/e9/3a7820be2bb0fe53b6bc9c3be26d3d1158004e4c3ab953aa6840b955b1e9/truststore-0.10.5-py3-none-any.whl", hash = "sha256:9aaaedaefaf06d8b206278cf8b5012bc897f485a874503501e12d776df78951c", size = 19017, upload-time = "2026-10-12T22:27:30.377Z" }, +] + +[[package]] +name = "types-protobuf" +version = "7.35.1.20260906" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/6c/e3e5b3e10bc328126a39637c138f9ebfd734bf14342b9f3540039b4ab995/types_protobuf-7.35.1.20260906.tar.gz", hash = "sha256:efd1a3862d4c967dad5512ef8d56b1530ac84f182c41735b94004756518c4998", size = 69895, upload-time = "2026-09-06T06:35:28.308Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/4e/f63e826c68f77ef875506d72f225918800346545ee99847bc28f3394f18d/types_protobuf-7.35.1.20260906-py3-none-any.whl", hash = "sha256:5155e48569e0dabff303fdf578db96cd31ea9a4a63b18018a4ceac6b0ae17462", size = 86419, upload-time = "2026-09-06T06:35:27.247Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/50/0195198eb1d75bab716bb0a813abea4cc394bfa17a92a189f021a34859af/typing_inspection-0.4.5.tar.gz", hash = "sha256:7f16c411e7930dc79596d42ab44672a3fe5f60eb34b781a1647e7872363120c7", size = 81194, upload-time = "2026-11-04T14:13:23.774Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/0c/ea2544850a7c135e15033a5c7a8e7211136184b7ae63e2239d557c2b3019/typing_inspection-0.4.5-py3-none-any.whl", hash = "sha256:bc210535442b39e1bf2805e0c7a17dfd6a4ace760c84c7f7d16245d23a6b1c39", size = 14874, upload-time = "2026-11-04T14:13:22.616Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.54.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/34/30e9280707135d2cfc589dfff3cb796bd07a3aeb1a3e415ba09dd89d7bb4/uvicorn-0.54.0.tar.gz", hash = "sha256:a2e33cbfaa0306f8e6b0c13e0cb89d7d7a2da3e62b90c66e18c33d9807b28620", size = 112283, upload-time = "2026-09-25T06:52:37.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/0c/b54a4fdd7f90a3af8b02ebc9ce6712c2c208b7926a2f7bad95c33ebbe943/uvicorn-0.54.0-py3-none-any.whl", hash = "sha256:505bdb0f318731d45f1f712071fc781a8981f6847a31c902c9f5e652d4f67faf", size = 87427, upload-time = "2026-09-25T06:52:35.829Z" }, +] diff --git a/agent_framework_plugin/mcp/workflow.py b/agent_framework_plugin/mcp/workflow.py new file mode 100644 index 00000000..a9d1991a --- /dev/null +++ b/agent_framework_plugin/mcp/workflow.py @@ -0,0 +1,36 @@ +"""MCP tools through ``TemporalMCPToolProvider``. + +Before each run the provider lists the server's tools with the +``temporalio.mcp.echo.list-tools`` Activity and adds one tool per entry. Each +call the model makes runs the ``temporalio.mcp.echo.call-tool`` Activity. The +connection to the server lives on the Worker. +""" + +# @@@SNIPSTART python-agent-framework-mcp-workflow +from datetime import timedelta + +from agent_framework import Agent +from temporalio import workflow +from temporalio.agent_framework import TemporalChatClient, TemporalMCPToolProvider + + +@workflow.defn +class MCPWorkflow: + @workflow.run + async def run(self, prompt: str) -> str: + agent = Agent( + client=TemporalChatClient( + "gpt-4o-mini", start_to_close_timeout=timedelta(seconds=60) + ), + instructions="Use the echo tool when asked to echo something.", + context_providers=[ + TemporalMCPToolProvider( + "echo", start_to_close_timeout=timedelta(seconds=30) + ) + ], + ) + response = await agent.run(prompt) + return response.text + + +# @@@SNIPEND diff --git a/agent_framework_plugin/middleware/__init__.py b/agent_framework_plugin/middleware/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_framework_plugin/middleware/run_worker.py b/agent_framework_plugin/middleware/run_worker.py new file mode 100644 index 00000000..f3557559 --- /dev/null +++ b/agent_framework_plugin/middleware/run_worker.py @@ -0,0 +1,43 @@ +"""Worker for the middleware sample.""" + +# @@@SNIPSTART python-agent-framework-middleware-worker +import asyncio +import os + +from agent_framework.openai import OpenAIChatClient +from openai import AsyncOpenAI +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.client import Client +from temporalio.worker import Worker + +from agent_framework_plugin.middleware.workflow import MiddlewareWorkflow + + +def openai_chat_client() -> OpenAIChatClient: + return OpenAIChatClient( + model="gpt-4o-mini", async_client=AsyncOpenAI(max_retries=0) + ) + + +async def main() -> None: + if not os.environ.get("OPENAI_API_KEY"): + raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") + + plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + client = await Client.connect( + os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), + plugins=[plugin], + ) + + worker = Worker( + client, + task_queue="agent-framework-middleware", + workflows=[MiddlewareWorkflow], + ) + print("Worker started. Ctrl+C to exit.") + await worker.run() + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/middleware/run_workflow.py b/agent_framework_plugin/middleware/run_workflow.py new file mode 100644 index 00000000..a9254436 --- /dev/null +++ b/agent_framework_plugin/middleware/run_workflow.py @@ -0,0 +1,25 @@ +"""Start the middleware workflow.""" + +import asyncio +import os + +from temporalio.client import Client + +from agent_framework_plugin.middleware.workflow import MiddlewareWorkflow + + +async def main() -> None: + client = await Client.connect(os.environ.get("TEMPORAL_ADDRESS", "localhost:7233")) + + result = await client.execute_workflow( + MiddlewareWorkflow.run, + "Delete /etc/passwd, then delete /tmp/scratch.txt, and tell me what happened.", + id="agent-framework-middleware", + task_queue="agent-framework-middleware", + ) + + print(f"Result: {result}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/agent_framework_plugin/middleware/workflow.py b/agent_framework_plugin/middleware/workflow.py new file mode 100644 index 00000000..da2508fb --- /dev/null +++ b/agent_framework_plugin/middleware/workflow.py @@ -0,0 +1,88 @@ +"""Agent Framework middleware running in the Workflow. + +Both middleware classes run inside the Workflow, next to the agent's loop, so +they must be deterministic: no clock, no I/O, no randomness. Middleware that +needs I/O calls ``await workflow.execute_activity(...)``. +""" + +from collections.abc import Awaitable, Callable, Mapping +from datetime import timedelta + +from agent_framework import ( + Agent, + ChatContext, + ChatMiddleware, + FunctionInvocationContext, + FunctionMiddleware, + tool, +) +from temporalio import workflow +from temporalio.agent_framework import TemporalChatClient + +ALLOWED_PREFIX = "/tmp/" +REMINDER = "Answer in one short sentence." + + +@tool +def delete_file(path: str) -> str: + """Delete the file at the given path.""" + return f"deleted {path}" + + +# @@@SNIPSTART python-agent-framework-middleware-middleware +class BlockOutsideTmp(FunctionMiddleware): + """Refuses delete_file calls outside /tmp without running the tool.""" + + async def process( + self, + context: FunctionInvocationContext, + call_next: Callable[[], Awaitable[None]], + ) -> None: + if context.function.name == "delete_file": + arguments = context.arguments + path = str( + arguments.get("path", "") + if isinstance(arguments, Mapping) + else getattr(arguments, "path", "") + ) + if not path.startswith(ALLOWED_PREFIX): + # Not calling call_next() skips the tool; the model gets this result. + context.result = f"Refused: {path} is outside {ALLOWED_PREFIX}" + return + await call_next() + + +class AppendReminder(ChatMiddleware): + """Adds a reminder to the instructions of every model call.""" + + async def process( + self, context: ChatContext, call_next: Callable[[], Awaitable[None]] + ) -> None: + options = dict(context.options or {}) + instructions = options.get("instructions") or "" + options["instructions"] = f"{instructions} {REMINDER}".strip() + context.options = options + await call_next() + + +# @@@SNIPEND + + +# @@@SNIPSTART python-agent-framework-middleware-workflow +@workflow.defn +class MiddlewareWorkflow: + @workflow.run + async def run(self, prompt: str) -> str: + agent = Agent( + client=TemporalChatClient( + "gpt-4o-mini", start_to_close_timeout=timedelta(seconds=60) + ), + instructions="You manage files. Use delete_file when asked to delete one.", + tools=[delete_file], + middleware=[BlockOutsideTmp(), AppendReminder()], + ) + response = await agent.run(prompt) + return response.text + + +# @@@SNIPEND diff --git a/agent_framework_plugin/multi_agent/__init__.py b/agent_framework_plugin/multi_agent/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_framework_plugin/multi_agent/run_worker.py b/agent_framework_plugin/multi_agent/run_worker.py new file mode 100644 index 00000000..454c2687 --- /dev/null +++ b/agent_framework_plugin/multi_agent/run_worker.py @@ -0,0 +1,44 @@ +"""Worker for the multi-agent sample.""" + +# @@@SNIPSTART python-agent-framework-multi-agent-worker +import asyncio +import os + +from agent_framework.openai import OpenAIChatClient +from openai import AsyncOpenAI +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.client import Client +from temporalio.worker import Worker + +from agent_framework_plugin.multi_agent.workflow import MultiAgentWorkflow + + +def openai_chat_client() -> OpenAIChatClient: + return OpenAIChatClient( + model="gpt-4o-mini", async_client=AsyncOpenAI(max_retries=0) + ) + + +async def main() -> None: + if not os.environ.get("OPENAI_API_KEY"): + raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") + + # All three agents use the same model name; one factory serves them all. + plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + client = await Client.connect( + os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), + plugins=[plugin], + ) + + worker = Worker( + client, + task_queue="agent-framework-multi-agent", + workflows=[MultiAgentWorkflow], + ) + print("Worker started. Ctrl+C to exit.") + await worker.run() + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/multi_agent/run_workflow.py b/agent_framework_plugin/multi_agent/run_workflow.py new file mode 100644 index 00000000..479c420c --- /dev/null +++ b/agent_framework_plugin/multi_agent/run_workflow.py @@ -0,0 +1,25 @@ +"""Start the multi-agent workflow.""" + +import asyncio +import os + +from temporalio.client import Client + +from agent_framework_plugin.multi_agent.workflow import MultiAgentWorkflow + + +async def main() -> None: + client = await Client.connect(os.environ.get("TEMPORAL_ADDRESS", "localhost:7233")) + + result = await client.execute_workflow( + MultiAgentWorkflow.run, + "durable execution", + id="agent-framework-multi-agent", + task_queue="agent-framework-multi-agent", + ) + + print(f"Result: {result}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/agent_framework_plugin/multi_agent/workflow.py b/agent_framework_plugin/multi_agent/workflow.py new file mode 100644 index 00000000..68a04cd1 --- /dev/null +++ b/agent_framework_plugin/multi_agent/workflow.py @@ -0,0 +1,51 @@ +"""A coordinator agent that delegates to two sub-agents through ``Agent.as_tool()``. + +Every agent runs in the Workflow and every agent's model calls run as +Activities, so the whole tree is durable with no per-agent wiring: to the +coordinator a sub-agent is a tool like any other. +""" + +# @@@SNIPSTART python-agent-framework-multi-agent-workflow +from datetime import timedelta + +from agent_framework import Agent +from temporalio import workflow +from temporalio.agent_framework import TemporalChatClient + + +def chat_client(summary: str) -> TemporalChatClient: + # One client per agent, so each Activity's summary names the agent in the UI. + return TemporalChatClient( + "gpt-4o-mini", start_to_close_timeout=timedelta(seconds=60), summary=summary + ) + + +@workflow.defn +class MultiAgentWorkflow: + @workflow.run + async def run(self, topic: str) -> str: + researcher = Agent( + client=chat_client("researcher"), + name="researcher", + description="Lists three facts about a topic.", + instructions="List three short facts about the topic you are given.", + ) + writer = Agent( + client=chat_client("writer"), + name="writer", + description="Writes one paragraph from a list of facts.", + instructions="Write one short paragraph from the facts you are given.", + ) + coordinator = Agent( + client=chat_client("coordinator"), + instructions=( + "Ask the researcher for facts about the topic, then ask the writer " + "to turn those facts into a paragraph. Reply with the paragraph." + ), + tools=[researcher.as_tool(), writer.as_tool()], + ) + response = await coordinator.run(f"Write a paragraph about {topic}.") + return response.text + + +# @@@SNIPEND diff --git a/agent_framework_plugin/open_telemetry/__init__.py b/agent_framework_plugin/open_telemetry/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_framework_plugin/open_telemetry/run_worker.py b/agent_framework_plugin/open_telemetry/run_worker.py new file mode 100644 index 00000000..6c07dad0 --- /dev/null +++ b/agent_framework_plugin/open_telemetry/run_worker.py @@ -0,0 +1,48 @@ +"""Worker for the OpenTelemetry sample.""" + +# @@@SNIPSTART python-agent-framework-open-telemetry-worker +import asyncio +import os + +from agent_framework.openai import OpenAIChatClient +from openai import AsyncOpenAI +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.client import Client +from temporalio.contrib.opentelemetry import OpenTelemetryPlugin +from temporalio.worker import Worker + +from agent_framework_plugin.open_telemetry.telemetry import install_tracer_provider +from agent_framework_plugin.open_telemetry.workflow import TracedWorkflow + + +def openai_chat_client() -> OpenAIChatClient: + return OpenAIChatClient( + model="gpt-4o-mini", async_client=AsyncOpenAI(max_retries=0) + ) + + +async def main() -> None: + if not os.environ.get("OPENAI_API_KEY"): + raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") + + # Set the provider before connecting; the plugins read it at startup. + install_tracer_provider("agent-framework-worker") + plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + # The Worker inherits the Client's plugins in this order: tracing first. + client = await Client.connect( + os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), + plugins=[OpenTelemetryPlugin(add_temporal_spans=True), plugin], + ) + + worker = Worker( + client, + task_queue="agent-framework-open-telemetry", + workflows=[TracedWorkflow], + ) + print("Worker started. Ctrl+C to exit.") + await worker.run() + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/open_telemetry/run_workflow.py b/agent_framework_plugin/open_telemetry/run_workflow.py new file mode 100644 index 00000000..dae519ea --- /dev/null +++ b/agent_framework_plugin/open_telemetry/run_workflow.py @@ -0,0 +1,40 @@ +"""Start the traced workflow under a root span and print the trace id.""" + +# @@@SNIPSTART python-agent-framework-open-telemetry-run-workflow +import asyncio +import os + +import opentelemetry.trace +from temporalio.client import Client +from temporalio.contrib.opentelemetry import OpenTelemetryPlugin + +from agent_framework_plugin.open_telemetry.telemetry import install_tracer_provider +from agent_framework_plugin.open_telemetry.workflow import TracedWorkflow + + +async def main() -> None: + install_tracer_provider("agent-framework-starter") + client = await Client.connect( + os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), + plugins=[OpenTelemetryPlugin(add_temporal_spans=True)], + ) + + # The root of the trace. The Workflow, its model Activity and Agent + # Framework's own spans all join it. + tracer = opentelemetry.trace.get_tracer(__name__) + with tracer.start_as_current_span("ask-agent") as span: + result = await client.execute_workflow( + TracedWorkflow.run, + "What is durable execution? Answer in one sentence.", + id="agent-framework-open-telemetry", + task_queue="agent-framework-open-telemetry", + ) + trace_id = format(span.get_span_context().trace_id, "032x") + + print(f"Result: {result}") + print(f"Trace ID: {trace_id}") + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/open_telemetry/telemetry.py b/agent_framework_plugin/open_telemetry/telemetry.py new file mode 100644 index 00000000..86b44280 --- /dev/null +++ b/agent_framework_plugin/open_telemetry/telemetry.py @@ -0,0 +1,26 @@ +"""Tracer provider setup shared by the Worker and the starter.""" + +import os + +import opentelemetry.trace +from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter +from opentelemetry.sdk.resources import SERVICE_NAME, Resource +from opentelemetry.sdk.trace.export import BatchSpanProcessor +from temporalio.contrib.opentelemetry import create_tracer_provider + +OTLP_ENDPOINT = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317") + + +def install_tracer_provider(service_name: str) -> None: + """Export this process's spans over OTLP, including replay-safe Workflow spans. + + ``create_tracer_provider`` returns the provider ``OpenTelemetryPlugin`` + requires: spans started from Workflow code keep their ids across replays + and are exported once. Agent Framework starts its agent and chat spans from + Workflow code through this global provider, so they are replay-safe too. + """ + provider = create_tracer_provider( + resource=Resource.create({SERVICE_NAME: service_name}) + ) + provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter(OTLP_ENDPOINT))) + opentelemetry.trace.set_tracer_provider(provider) diff --git a/agent_framework_plugin/open_telemetry/workflow.py b/agent_framework_plugin/open_telemetry/workflow.py new file mode 100644 index 00000000..4c1de24b --- /dev/null +++ b/agent_framework_plugin/open_telemetry/workflow.py @@ -0,0 +1,27 @@ +"""A Workflow whose agent run is traced end to end.""" + +# @@@SNIPSTART python-agent-framework-open-telemetry-workflow +from datetime import timedelta + +from agent_framework import Agent +from temporalio import workflow +from temporalio.agent_framework import TemporalChatClient + + +@workflow.defn +class TracedWorkflow: + @workflow.run + async def run(self, prompt: str) -> str: + agent = Agent( + client=TemporalChatClient( + "gpt-4o-mini", start_to_close_timeout=timedelta(seconds=60) + ), + # Agent Framework names its span after the agent: invoke_agent assistant. + name="assistant", + instructions="You are a concise assistant.", + ) + response = await agent.run(prompt) + return response.text + + +# @@@SNIPEND diff --git a/agent_framework_plugin/streaming/__init__.py b/agent_framework_plugin/streaming/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_framework_plugin/streaming/run_worker.py b/agent_framework_plugin/streaming/run_worker.py new file mode 100644 index 00000000..9bc682bd --- /dev/null +++ b/agent_framework_plugin/streaming/run_worker.py @@ -0,0 +1,43 @@ +"""Worker for the streaming sample.""" + +# @@@SNIPSTART python-agent-framework-streaming-worker +import asyncio +import os + +from agent_framework.openai import OpenAIChatClient +from openai import AsyncOpenAI +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.client import Client +from temporalio.worker import Worker + +from agent_framework_plugin.streaming.workflow import StreamingWorkflow + + +def openai_chat_client() -> OpenAIChatClient: + return OpenAIChatClient( + model="gpt-4o-mini", async_client=AsyncOpenAI(max_retries=0) + ) + + +async def main() -> None: + if not os.environ.get("OPENAI_API_KEY"): + raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") + + plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + client = await Client.connect( + os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), + plugins=[plugin], + ) + + worker = Worker( + client, + task_queue="agent-framework-streaming", + workflows=[StreamingWorkflow], + ) + print("Worker started. Ctrl+C to exit.") + await worker.run() + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/streaming/run_workflow.py b/agent_framework_plugin/streaming/run_workflow.py new file mode 100644 index 00000000..dbd70fb7 --- /dev/null +++ b/agent_framework_plugin/streaming/run_workflow.py @@ -0,0 +1,67 @@ +"""Start the streaming workflow and print the model's updates as they arrive.""" + +# @@@SNIPSTART python-agent-framework-streaming-client +import asyncio +import os +from datetime import timedelta + +from agent_framework import ChatResponseUpdate +from temporalio.client import Client +from temporalio.contrib.workflow_streams import WorkflowStreamClient + +from agent_framework_plugin.streaming.workflow import STREAMING_TOPIC, StreamingWorkflow + + +async def main() -> None: + client = await Client.connect(os.environ.get("TEMPORAL_ADDRESS", "localhost:7233")) + workflow_id = "agent-framework-streaming" + + handle = await client.start_workflow( + StreamingWorkflow.run, + "Count from 1 to 5, one number per sentence.", + id=workflow_id, + task_queue="agent-framework-streaming", + ) + + printed: list[str] = [] + + async def consume() -> None: + # Each item is a ChatResponseUpdate in its dict form. + stream = WorkflowStreamClient.create(client, workflow_id) + async for item in stream.subscribe( + [STREAMING_TOPIC], + from_offset=0, + result_type=dict, + poll_cooldown=timedelta(milliseconds=50), + ): + update = ChatResponseUpdate.from_dict(item.data) + if update.text: + printed.append(update.text) + print(update.text, end="", flush=True) + + consume_task = asyncio.create_task(consume()) + result = await handle.result() + + # The subscriber may still be reading the tail of the stream after the + # Workflow completes. The streamed text adds up to the result, so wait for it. + async def drained() -> None: + while result not in "".join(printed): + await asyncio.sleep(0.05) + + try: + await asyncio.wait_for(drained(), timeout=10) + except asyncio.TimeoutError: + print("\n(timed out waiting for the last streamed updates)") + consume_task.cancel() + try: + await consume_task + except asyncio.CancelledError: + pass + + print() + print(f"Final result: {result}") + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/streaming/workflow.py b/agent_framework_plugin/streaming/workflow.py new file mode 100644 index 00000000..7146b216 --- /dev/null +++ b/agent_framework_plugin/streaming/workflow.py @@ -0,0 +1,41 @@ +"""Stream model output to a subscriber outside the Workflow. + +``TemporalChatClient(streaming_topic="events")`` makes the model Activity +publish every update to the ``events`` topic of a ``WorkflowStream`` hosted on +the Workflow, batched every 100 milliseconds. The Workflow still gets the whole +response, so the result is the same as without streaming. +""" + +# @@@SNIPSTART python-agent-framework-streaming-workflow +from datetime import timedelta + +from agent_framework import Agent +from temporalio import workflow +from temporalio.agent_framework import TemporalChatClient +from temporalio.contrib.workflow_streams import WorkflowStream + +STREAMING_TOPIC = "events" + + +@workflow.defn +class StreamingWorkflow: + def __init__(self) -> None: + # Host the stream so the model Activity has somewhere to publish. + self._stream = WorkflowStream() + + @workflow.run + async def run(self, prompt: str) -> str: + agent = Agent( + client=TemporalChatClient( + "gpt-4o-mini", + start_to_close_timeout=timedelta(seconds=60), + streaming_topic=STREAMING_TOPIC, + ), + instructions="You are a concise assistant.", + ) + # stream=True selects the streaming Activity; the final response is the same. + response = await agent.run(prompt, stream=True).get_final_response() + return response.text + + +# @@@SNIPEND diff --git a/agent_framework_plugin/structured_output/__init__.py b/agent_framework_plugin/structured_output/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_framework_plugin/structured_output/run_worker.py b/agent_framework_plugin/structured_output/run_worker.py new file mode 100644 index 00000000..29451283 --- /dev/null +++ b/agent_framework_plugin/structured_output/run_worker.py @@ -0,0 +1,43 @@ +"""Worker for the structured output sample.""" + +# @@@SNIPSTART python-agent-framework-structured-output-worker +import asyncio +import os + +from agent_framework.openai import OpenAIChatClient +from openai import AsyncOpenAI +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.client import Client +from temporalio.worker import Worker + +from agent_framework_plugin.structured_output.workflow import StructuredOutputWorkflow + + +def openai_chat_client() -> OpenAIChatClient: + return OpenAIChatClient( + model="gpt-4o-mini", async_client=AsyncOpenAI(max_retries=0) + ) + + +async def main() -> None: + if not os.environ.get("OPENAI_API_KEY"): + raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") + + plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + client = await Client.connect( + os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), + plugins=[plugin], + ) + + worker = Worker( + client, + task_queue="agent-framework-structured-output", + workflows=[StructuredOutputWorkflow], + ) + print("Worker started. Ctrl+C to exit.") + await worker.run() + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/structured_output/run_workflow.py b/agent_framework_plugin/structured_output/run_workflow.py new file mode 100644 index 00000000..8403066b --- /dev/null +++ b/agent_framework_plugin/structured_output/run_workflow.py @@ -0,0 +1,33 @@ +"""Start the structured output workflow and print the typed result.""" + +# @@@SNIPSTART python-agent-framework-structured-output-run-workflow +import asyncio +import os + +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.client import Client + +from agent_framework_plugin.structured_output.workflow import StructuredOutputWorkflow + + +async def main() -> None: + # The plugin on this client installs the Pydantic data converter, which + # decodes the Workflow's PersonInfo result. + client = await Client.connect( + os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), + plugins=[AgentFrameworkPlugin()], + ) + + person = await client.execute_workflow( + StructuredOutputWorkflow.run, + "John Smith is a 30 year-old software engineer.", + id="agent-framework-structured-output", + task_queue="agent-framework-structured-output", + ) + + print(f"name={person.name} age={person.age} occupation={person.occupation}") + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/structured_output/workflow.py b/agent_framework_plugin/structured_output/workflow.py new file mode 100644 index 00000000..5ad6a9c4 --- /dev/null +++ b/agent_framework_plugin/structured_output/workflow.py @@ -0,0 +1,39 @@ +"""Typed output: the agent fills a Pydantic model and the Workflow returns it.""" + +# @@@SNIPSTART python-agent-framework-structured-output-workflow +from datetime import timedelta + +from agent_framework import Agent +from pydantic import BaseModel, Field +from temporalio import workflow +from temporalio.agent_framework import TemporalChatClient +from temporalio.exceptions import ApplicationError + + +class PersonInfo(BaseModel): + name: str = Field(description="Name of the person") + age: int = Field(description="Age of the person") + occupation: str = Field(description="Occupation of the person") + + +@workflow.defn +class StructuredOutputWorkflow: + @workflow.run + async def run(self, prompt: str) -> PersonInfo: + agent = Agent( + client=TemporalChatClient( + "gpt-4o-mini", start_to_close_timeout=timedelta(seconds=60) + ), + instructions="Extract the person's details from the text.", + ) + # The model's JSON schema crosses to the Activity; the Workflow parses the reply. + response = await agent.run(prompt, options={"response_format": PersonInfo}) + person = response.value + if not isinstance(person, PersonInfo): + raise ApplicationError( + "The model did not return a PersonInfo", non_retryable=True + ) + return person + + +# @@@SNIPEND diff --git a/agent_framework_plugin/tools/__init__.py b/agent_framework_plugin/tools/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agent_framework_plugin/tools/run_worker.py b/agent_framework_plugin/tools/run_worker.py new file mode 100644 index 00000000..afa33cef --- /dev/null +++ b/agent_framework_plugin/tools/run_worker.py @@ -0,0 +1,45 @@ +"""Worker for the tools sample.""" + +# @@@SNIPSTART python-agent-framework-tools-worker +import asyncio +import os + +from agent_framework.openai import OpenAIChatClient +from openai import AsyncOpenAI +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.client import Client +from temporalio.worker import Worker + +from agent_framework_plugin.tools.workflow import ToolsWorkflow, fetch_weather + + +def openai_chat_client() -> OpenAIChatClient: + return OpenAIChatClient( + model="gpt-4o-mini", async_client=AsyncOpenAI(max_retries=0) + ) + + +async def main() -> None: + if not os.environ.get("OPENAI_API_KEY"): + raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") + + plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + client = await Client.connect( + os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), + plugins=[plugin], + ) + + worker = Worker( + client, + task_queue="agent-framework-tools", + workflows=[ToolsWorkflow], + # The Activity behind the fetch_weather tool. + activities=[fetch_weather], + ) + print("Worker started. Ctrl+C to exit.") + await worker.run() + + +if __name__ == "__main__": + asyncio.run(main()) +# @@@SNIPEND diff --git a/agent_framework_plugin/tools/run_workflow.py b/agent_framework_plugin/tools/run_workflow.py new file mode 100644 index 00000000..d258cfce --- /dev/null +++ b/agent_framework_plugin/tools/run_workflow.py @@ -0,0 +1,29 @@ +"""Start the tools workflow.""" + +import asyncio +import os + +from temporalio.client import Client + +from agent_framework_plugin.tools.workflow import ToolsWorkflow + + +async def main() -> None: + client = await Client.connect(os.environ.get("TEMPORAL_ADDRESS", "localhost:7233")) + + result = await client.execute_workflow( + ToolsWorkflow.run, + ( + "Please do two things:\n" + "1. Count the letter R's in 'strawberry'.\n" + "2. Fetch the weather in San Francisco." + ), + id="agent-framework-tools", + task_queue="agent-framework-tools", + ) + + print(f"Result: {result}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/agent_framework_plugin/tools/workflow.py b/agent_framework_plugin/tools/workflow.py new file mode 100644 index 00000000..ac06a31b --- /dev/null +++ b/agent_framework_plugin/tools/workflow.py @@ -0,0 +1,59 @@ +"""Two tool patterns on one agent: a pure tool in the Workflow and an Activity. + +``letter_counter`` is plain logic, so it runs in the Workflow as an ordinary +``@tool``. ``fetch_weather`` stands in for an HTTP call, so it is an Activity +wrapped with ``activity_as_tool`` and gets its own timeout and retries. +""" + +from datetime import timedelta +from typing import Any + +from agent_framework import Agent, tool +from temporalio import activity, workflow +from temporalio.agent_framework import TemporalChatClient +from temporalio.agent_framework.workflow import activity_as_tool + + +# @@@SNIPSTART python-agent-framework-tools-tool +@tool +def letter_counter(word: str, letter: str) -> int: + """Count how many times a letter appears in a word, ignoring case.""" + return word.lower().count(letter.lower()) + + +# @@@SNIPEND + + +# @@@SNIPSTART python-agent-framework-tools-activity +@activity.defn +async def fetch_weather(city: str) -> dict[str, Any]: + """Look up the current weather for a city.""" + # Stands in for an HTTP call. Anything that does I/O belongs in an Activity. + return {"city": city, "temperature_f": 72, "conditions": "sunny"} + + +# @@@SNIPEND + + +# @@@SNIPSTART python-agent-framework-tools-workflow +@workflow.defn +class ToolsWorkflow: + @workflow.run + async def run(self, prompt: str) -> str: + agent = Agent( + client=TemporalChatClient( + "gpt-4o-mini", start_to_close_timeout=timedelta(seconds=60) + ), + instructions="Use the tools you are given and answer briefly.", + tools=[ + letter_counter, + activity_as_tool( + fetch_weather, start_to_close_timeout=timedelta(seconds=30) + ), + ], + ) + response = await agent.run(prompt) + return response.text + + +# @@@SNIPEND From ca8a71bc0b2735d53bc06269a97356a1229b05a5 Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 22:51:29 -0600 Subject: [PATCH 04/26] Test the Agent Framework samples with a scripted chat client --- agent_framework_plugin/mcp/tests/conftest.py | 39 ++++++ agent_framework_plugin/mcp/tests/mcp_test.py | 87 ++++++++++++ tests/agent_framework_plugin/__init__.py | 0 .../_fake_chat_client.py | 35 +++++ tests/agent_framework_plugin/conftest.py | 45 ++++++ .../continue_as_new_test.py | 91 ++++++++++++ .../hello_world_test.py | 40 ++++++ tests/agent_framework_plugin/helpers.py | 69 +++++++++ .../human_in_the_loop_test.py | 131 ++++++++++++++++++ .../agent_framework_plugin/middleware_test.py | 60 ++++++++ .../multi_agent_test.py | 76 ++++++++++ .../open_telemetry_test.py | 67 +++++++++ .../agent_framework_plugin/streaming_test.py | 70 ++++++++++ .../structured_output_test.py | 46 ++++++ tests/agent_framework_plugin/tools_test.py | 59 ++++++++ 15 files changed, 915 insertions(+) create mode 100644 agent_framework_plugin/mcp/tests/conftest.py create mode 100644 agent_framework_plugin/mcp/tests/mcp_test.py create mode 100644 tests/agent_framework_plugin/__init__.py create mode 100644 tests/agent_framework_plugin/_fake_chat_client.py create mode 100644 tests/agent_framework_plugin/conftest.py create mode 100644 tests/agent_framework_plugin/continue_as_new_test.py create mode 100644 tests/agent_framework_plugin/hello_world_test.py create mode 100644 tests/agent_framework_plugin/helpers.py create mode 100644 tests/agent_framework_plugin/human_in_the_loop_test.py create mode 100644 tests/agent_framework_plugin/middleware_test.py create mode 100644 tests/agent_framework_plugin/multi_agent_test.py create mode 100644 tests/agent_framework_plugin/open_telemetry_test.py create mode 100644 tests/agent_framework_plugin/streaming_test.py create mode 100644 tests/agent_framework_plugin/structured_output_test.py create mode 100644 tests/agent_framework_plugin/tools_test.py diff --git a/agent_framework_plugin/mcp/tests/conftest.py b/agent_framework_plugin/mcp/tests/conftest.py new file mode 100644 index 00000000..4a713fcc --- /dev/null +++ b/agent_framework_plugin/mcp/tests/conftest.py @@ -0,0 +1,39 @@ +"""Collection guard for the MCP sample tests. + +The sample needs Python 3.11 and the plugin. On other interpreters the project's +dependencies resolve to nothing, so the test module cannot be imported; skip +its collection instead of failing the session, and do not treat the empty run +as a failure. +""" + +import sys + +import pytest + +collect_ignore_glob: list[str] = [] + +_plugin_available = False +if sys.version_info >= (3, 11): + try: + import temporalio.agent_framework # noqa: F401 + + _plugin_available = True + except ImportError: + _plugin_available = False + +if not _plugin_available: + collect_ignore_glob = ["*_test.py"] + + +def pytest_report_header(config: pytest.Config) -> str | None: + if collect_ignore_glob: + return ( + "agent_framework_plugin/mcp: temporalio.agent_framework not importable " + "on this interpreter/environment; sample tests NOT collected" + ) + return None + + +def pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> None: + if collect_ignore_glob and exitstatus == pytest.ExitCode.NO_TESTS_COLLECTED: + session.exitstatus = pytest.ExitCode.OK diff --git a/agent_framework_plugin/mcp/tests/mcp_test.py b/agent_framework_plugin/mcp/tests/mcp_test.py new file mode 100644 index 00000000..267f489d --- /dev/null +++ b/agent_framework_plugin/mcp/tests/mcp_test.py @@ -0,0 +1,87 @@ +import uuid +from collections import Counter +from collections.abc import AsyncGenerator + +import pytest_asyncio +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.agent_framework.testing import ( + fake_chat_client, + function_call_response, + text_response, +) +from temporalio.api.enums.v1 import EventType +from temporalio.client import Client, WorkflowHandle +from temporalio.testing import WorkflowEnvironment +from temporalio.worker import Worker + +from run_worker import echo_mcp_client +from workflow import MCPWorkflow + +INVOKE_MODEL = "agent_framework.invoke_model" +LIST_TOOLS = "temporalio.mcp.echo.list-tools" +CALL_TOOL = "temporalio.mcp.echo.call-tool" + + +@pytest_asyncio.fixture(scope="session") +async def env() -> AsyncGenerator[WorkflowEnvironment, None]: + environment = await WorkflowEnvironment.start_local() + yield environment + await environment.shutdown() + + +async def count_scheduled_activities(handle: WorkflowHandle) -> Counter[str]: + counts: Counter[str] = Counter() + async for event in handle.fetch_history_events(): + if event.event_type == EventType.EVENT_TYPE_ACTIVITY_TASK_SCHEDULED: + counts[ + event.activity_task_scheduled_event_attributes.activity_type.name + ] += 1 + return counts + + +async def test_mcp_tool_runs_as_activities(env: WorkflowEnvironment) -> None: + # A scripted model: one call to the server's echo tool, then the answer. + # The MCP server is the real echo_mcp_server.py, started over stdio by + # the same factory the Worker uses. + factory = fake_chat_client( + [ + function_call_response( + "echo", {"message": "hello from MCP"}, call_id="call-1" + ), + text_response("The server echoed: hello from MCP"), + ] + ) + plugin = AgentFrameworkPlugin( + chat_clients={"gpt-4o-mini": factory}, mcp_clients={"echo": echo_mcp_client} + ) + config = env.client.config() + config["plugins"] = [*config["plugins"], plugin] + client = Client(**config) + task_queue = f"agent-framework-mcp-{uuid.uuid4()}" + + async with Worker( + client, + task_queue=task_queue, + workflows=[MCPWorkflow], + max_cached_workflows=0, + ): + handle = await client.start_workflow( + MCPWorkflow.run, + "Echo 'hello from MCP'.", + id=f"agent-framework-mcp-{uuid.uuid4()}", + task_queue=task_queue, + ) + result = await handle.result() + + assert result == "The server echoed: hello from MCP" + # The listing and the call each ran as an MCP Activity, around two model calls. + assert await count_scheduled_activities(handle) == { + INVOKE_MODEL: 2, + LIST_TOOLS: 1, + CALL_TOOL: 1, + } + # The model saw the server's tool with the server's own schema. + first_call, _ = factory().calls + (echo_tool,) = first_call["options"]["tools"] + assert echo_tool.name == "echo" + assert echo_tool.parameters()["required"] == ["message"] diff --git a/tests/agent_framework_plugin/__init__.py b/tests/agent_framework_plugin/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/agent_framework_plugin/_fake_chat_client.py b/tests/agent_framework_plugin/_fake_chat_client.py new file mode 100644 index 00000000..e615a4cc --- /dev/null +++ b/tests/agent_framework_plugin/_fake_chat_client.py @@ -0,0 +1,35 @@ +"""Scripted chat client for the sample tests. + +Built on ``temporalio.agent_framework.testing``. ``scripted_plugin`` registers +one ``FakeChatClient`` under the ``"gpt-4o-mini"`` name the samples use, so the +sample Workflows run unchanged against the script and no API key is needed. +""" + +from collections.abc import Callable, Sequence +from typing import Any + +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.agent_framework.testing import ( + FakeChatClient, + ScriptEntry, + fake_chat_client, + function_call_response, + text_response, +) + +MODEL = "gpt-4o-mini" + +__all__ = ["MODEL", "function_call_response", "scripted_plugin", "text_response"] + + +def scripted_plugin( + responses: Sequence[ScriptEntry], **kwargs: Any +) -> tuple[AgentFrameworkPlugin, Callable[[], FakeChatClient]]: + """Return a plugin whose ``gpt-4o-mini`` client replays ``responses``, and its factory. + + Each model call takes the next entry; the last one repeats. The factory + hands out the same client every time, so call it after the run to read + ``calls``, the requests the model received. + """ + factory = fake_chat_client(responses) + return AgentFrameworkPlugin(chat_clients={MODEL: factory}, **kwargs), factory diff --git a/tests/agent_framework_plugin/conftest.py b/tests/agent_framework_plugin/conftest.py new file mode 100644 index 00000000..94fc2582 --- /dev/null +++ b/tests/agent_framework_plugin/conftest.py @@ -0,0 +1,45 @@ +"""Collection guard for the Agent Framework plugin tests. + +The plugin ships as the standalone `temporalio-agent-framework` package, which +the `agent-framework` dependency group installs on Python >= 3.11 only, because +Agent Framework itself needs 3.11. These test modules import +`temporalio.agent_framework` at module load, which would raise `ImportError` +during collection on 3.10 or in an environment synced without the group, and +fail the whole session. + +A module-level `pytest.mark.skipif` cannot help: the mark is read after the +module is imported, so the import error fires first. `collect_ignore_glob` is +evaluated before any test module is imported, so it skips these files cleanly +when the plugin is unavailable and runs them on any 3.11+ environment synced +with the group, the canonical CI run included. The guard does a real import +rather than `find_spec`, because the package can exist on disk while its +runtime dependencies do not. When the guard is active, `pytest_report_header` +says so in the pytest header. +""" + +import sys + +collect_ignore_glob: list[str] = [] + +_plugin_available = False +if sys.version_info >= (3, 11): + try: + import temporalio.agent_framework # noqa: F401 + + _plugin_available = True + except ImportError: + _plugin_available = False + +if not _plugin_available: + collect_ignore_glob = ["*_test.py"] + + +def pytest_report_header(config) -> str | None: + """Make the guard visible in the pytest header instead of silent.""" + if collect_ignore_glob: + return ( + "agent_framework_plugin: temporalio.agent_framework not importable " + "on this interpreter/environment; sample tests NOT collected " + "(see tests/agent_framework_plugin/conftest.py)" + ) + return None diff --git a/tests/agent_framework_plugin/continue_as_new_test.py b/tests/agent_framework_plugin/continue_as_new_test.py new file mode 100644 index 00000000..0d6d568a --- /dev/null +++ b/tests/agent_framework_plugin/continue_as_new_test.py @@ -0,0 +1,91 @@ +import asyncio +import uuid + +from temporalio.client import Client, WorkflowExecutionStatus +from temporalio.testing import WorkflowEnvironment +from temporalio.worker import Worker + +from agent_framework_plugin.continue_as_new.workflow import ChatInput, ChatWorkflow +from tests.agent_framework_plugin._fake_chat_client import scripted_plugin +from tests.agent_framework_plugin.helpers import ( + count_scheduled_activities, + with_plugin, +) + + +async def test_continue_as_new_carries_the_session() -> None: + """Drive a chat across a continue-as-new boundary. + + One turn is enough history for the server to suggest continue-as-new at + the lowered threshold. The first run hands its session to a fresh run, + which takes a second turn on the carried transcript and ends normally. + """ + plugin, factory = scripted_plugin(["First reply.", "Second reply."]) + + # The server suggests continue-as-new once history passes a threshold + # (4096 events by default). Lower it so one turn trips the suggestion. + async with await WorkflowEnvironment.start_local( + dev_server_extra_args=[ + "--dynamic-config-value", + "limit.historyCount.suggestContinueAsNew=5", + ] + ) as env: + client = with_plugin(env.client, plugin) + task_queue = f"agent-framework-chat-{uuid.uuid4()}" + workflow_id = f"agent-framework-chat-{uuid.uuid4()}" + + async with Worker( + client, + task_queue=task_queue, + workflows=[ChatWorkflow], + max_cached_workflows=0, + ): + handle = await client.start_workflow( + ChatWorkflow.run, + ChatInput(), + id=workflow_id, + task_queue=task_queue, + ) + first_run_id = handle.result_run_id + + assert await handle.execute_update(ChatWorkflow.turn, "Hello") == ( + "First reply." + ) + + # Pin the first run: an unpinned handle follows the chain to the + # fresh run, which is still running. + first_run = client.get_workflow_handle(workflow_id, run_id=first_run_id) + for _ in range(100): + status = (await first_run.describe()).status + if status == WorkflowExecutionStatus.CONTINUED_AS_NEW: + break + await asyncio.sleep(0.1) + else: + raise AssertionError("the workflow did not continue as new") + + latest = client.get_workflow_handle(workflow_id) + assert await latest.execute_update(ChatWorkflow.turn, "How are you?") == ( + "Second reply." + ) + # Two messages carried across the handoff plus two from the new turn. + transcript = await latest.query(ChatWorkflow.transcript) + assert [m["text"] for m in transcript] == [ + "Hello", + "First reply.", + "How are you?", + "Second reply.", + ] + await latest.signal(ChatWorkflow.end_chat) + await latest.result() + + # The first run made exactly one model call before it handed off. + assert (await count_scheduled_activities(first_run)).total() == 1 + + # The fresh run rebuilt the session from the carried dict: its first model + # call already held the first turn. + _, second_call = factory().calls + assert [m.text for m in second_call["messages"]] == [ + "Hello", + "First reply.", + "How are you?", + ] diff --git a/tests/agent_framework_plugin/hello_world_test.py b/tests/agent_framework_plugin/hello_world_test.py new file mode 100644 index 00000000..2044aa64 --- /dev/null +++ b/tests/agent_framework_plugin/hello_world_test.py @@ -0,0 +1,40 @@ +import uuid + +from temporalio.client import Client +from temporalio.worker import Worker + +from agent_framework_plugin.hello_world.workflow import HelloWorldWorkflow +from tests.agent_framework_plugin._fake_chat_client import scripted_plugin +from tests.agent_framework_plugin.helpers import ( + INVOKE_MODEL, + count_scheduled_activities, + with_plugin, +) + + +async def test_hello_world(client: Client) -> None: + # A scripted model, so the test runs offline with no OPENAI_API_KEY. + plugin, factory = scripted_plugin(["Workflows outlive crashes."]) + client = with_plugin(client, plugin) + task_queue = f"agent-framework-hello-world-{uuid.uuid4()}" + + async with Worker( + client, + task_queue=task_queue, + workflows=[HelloWorldWorkflow], + max_cached_workflows=0, + ): + handle = await client.start_workflow( + HelloWorldWorkflow.run, + "Write a haiku.", + id=f"agent-framework-hello-world-{uuid.uuid4()}", + task_queue=task_queue, + ) + result = await handle.result() + + assert result == "Workflows outlive crashes." + # The model call ran as the plugin's Activity, not inside the Workflow. + assert await count_scheduled_activities(handle) == {INVOKE_MODEL: 1} + (call,) = factory().calls + assert call["options"]["instructions"] == "You are a concise assistant." + assert call["messages"][-1].text == "Write a haiku." diff --git a/tests/agent_framework_plugin/helpers.py b/tests/agent_framework_plugin/helpers.py new file mode 100644 index 00000000..9705e475 --- /dev/null +++ b/tests/agent_framework_plugin/helpers.py @@ -0,0 +1,69 @@ +"""Shared helpers for the Agent Framework plugin sample tests.""" + +import json +from collections import Counter +from collections.abc import Sequence +from typing import Any + +from agent_framework import Message +from temporalio.agent_framework import AgentFrameworkPlugin +from temporalio.api.enums.v1 import EventType +from temporalio.client import Client, WorkflowHandle + +INVOKE_MODEL = "agent_framework.invoke_model" +INVOKE_MODEL_STREAMING = "agent_framework.invoke_model_streaming" + + +def with_plugin(client: Client, plugin: AgentFrameworkPlugin) -> Client: + """Return a client with ``plugin`` added after the environment's own plugins.""" + config = client.config() + config["plugins"] = [*config["plugins"], plugin] + return Client(**config) + + +async def count_scheduled_activities(handle: WorkflowHandle) -> Counter[str]: + """Count ``ActivityTaskScheduled`` events in the history by activity type. + + The plugin's whole point is that model, tool and MCP calls run as + Activities. Asserting on the history proves that routing happened, where a + content-only assertion would still pass if a call silently ran in the + Workflow. + """ + counts: Counter[str] = Counter() + async for event in handle.fetch_history_events(): + if event.event_type == EventType.EVENT_TYPE_ACTIVITY_TASK_SCHEDULED: + counts[ + event.activity_task_scheduled_event_attributes.activity_type.name + ] += 1 + return counts + + +async def activity_summaries(handle: WorkflowHandle) -> list[str | None]: + """Return the ``summary`` of each ``ActivityTaskScheduled`` event, in order.""" + summaries: list[str | None] = [] + async for event in handle.fetch_history_events(): + if event.event_type != EventType.EVENT_TYPE_ACTIVITY_TASK_SCHEDULED: + continue + if event.HasField("user_metadata") and event.user_metadata.HasField("summary"): + summaries.append(json.loads(event.user_metadata.summary.data)) + else: + summaries.append(None) + return summaries + + +def function_results(messages: Sequence[Message]) -> list[str]: + """Return every function result in ``messages`` as text, in order.""" + results: list[str] = [] + for message in messages: + for content in message.contents: + if content.type == "function_result": + results.append(_result_text(content.result)) + return results + + +def _result_text(result: Any) -> str: + if isinstance(result, str): + return result + if isinstance(result, list): + return "".join(getattr(item, "text", "") or "" for item in result) + return json.dumps(result, default=str) diff --git a/tests/agent_framework_plugin/human_in_the_loop_test.py b/tests/agent_framework_plugin/human_in_the_loop_test.py new file mode 100644 index 00000000..875c586a --- /dev/null +++ b/tests/agent_framework_plugin/human_in_the_loop_test.py @@ -0,0 +1,131 @@ +import asyncio +import uuid + +import pytest +from temporalio.client import Client, WorkflowHandle, WorkflowUpdateFailedError +from temporalio.worker import Worker + +from agent_framework_plugin.human_in_the_loop.workflow import ( + ApprovalRequest, + HumanInTheLoopWorkflow, +) +from tests.agent_framework_plugin._fake_chat_client import ( + function_call_response, + scripted_plugin, + text_response, +) +from tests.agent_framework_plugin.helpers import ( + INVOKE_MODEL, + count_scheduled_activities, + function_results, + with_plugin, +) + + +def _script(final_text: str) -> list: + # The model asks to delete the file; after the decision it reports back. + return [ + function_call_response( + "delete_file", {"path": "/tmp/report.txt"}, call_id="call-1" + ), + text_response(final_text), + ] + + +async def _pending(handle: WorkflowHandle) -> list[ApprovalRequest]: + for _ in range(100): + pending = await handle.query(HumanInTheLoopWorkflow.pending_approvals) + if pending: + return pending + await asyncio.sleep(0.1) + raise AssertionError("the workflow never surfaced an approval request") + + +async def test_approve(client: Client) -> None: + plugin, factory = scripted_plugin(_script("Deleted /tmp/report.txt.")) + client = with_plugin(client, plugin) + task_queue = f"agent-framework-human-in-the-loop-{uuid.uuid4()}" + + async with Worker( + client, + task_queue=task_queue, + workflows=[HumanInTheLoopWorkflow], + max_cached_workflows=0, + ): + handle = await client.start_workflow( + HumanInTheLoopWorkflow.run, + "Please delete /tmp/report.txt", + id=f"agent-framework-human-in-the-loop-{uuid.uuid4()}", + task_queue=task_queue, + ) + (request,) = await _pending(handle) + assert request.tool_name == "delete_file" + assert request.arguments == {"path": "/tmp/report.txt"} + assert request.request_id + outcome = await handle.execute_update(HumanInTheLoopWorkflow.decide, "approve") + result = await handle.result() + + assert outcome == "approve" + assert result == "Deleted /tmp/report.txt." + # Two model calls: the request and the report. The approved tool ran in + # the Workflow, so it left no Activity, and its result reached the model. + assert await count_scheduled_activities(handle) == {INVOKE_MODEL: 2} + assert function_results(factory().calls[-1]["messages"]) == [ + "deleted /tmp/report.txt" + ] + + +async def test_reject(client: Client) -> None: + plugin, factory = scripted_plugin(_script("Understood, I did not delete it.")) + client = with_plugin(client, plugin) + task_queue = f"agent-framework-hitl-reject-{uuid.uuid4()}" + + async with Worker( + client, + task_queue=task_queue, + workflows=[HumanInTheLoopWorkflow], + max_cached_workflows=0, + ): + handle = await client.start_workflow( + HumanInTheLoopWorkflow.run, + "Please delete /tmp/report.txt", + id=f"agent-framework-hitl-reject-{uuid.uuid4()}", + task_queue=task_queue, + ) + await _pending(handle) + outcome = await handle.execute_update(HumanInTheLoopWorkflow.decide, "reject") + result = await handle.result() + + assert outcome == "reject" + assert result == "Understood, I did not delete it." + # The tool never ran: the model got Agent Framework's rejection result. + (tool_result,) = function_results(factory().calls[-1]["messages"]) + assert "deleted" not in tool_result + assert "reject" in tool_result.lower() + assert await count_scheduled_activities(handle) == {INVOKE_MODEL: 2} + + +async def test_validator_rejects_other_decisions(client: Client) -> None: + plugin, _ = scripted_plugin(_script("Deleted /tmp/report.txt.")) + client = with_plugin(client, plugin) + task_queue = f"agent-framework-hitl-validator-{uuid.uuid4()}" + + async with Worker( + client, + task_queue=task_queue, + workflows=[HumanInTheLoopWorkflow], + max_cached_workflows=0, + ): + handle = await client.start_workflow( + HumanInTheLoopWorkflow.run, + "Please delete /tmp/report.txt", + id=f"agent-framework-hitl-validator-{uuid.uuid4()}", + task_queue=task_queue, + ) + await _pending(handle) + # The validator keeps anything but approve or reject out of history. + with pytest.raises(WorkflowUpdateFailedError): + await handle.execute_update(HumanInTheLoopWorkflow.decide, "maybe") + # The Workflow is still waiting and takes the real decision. + await handle.execute_update(HumanInTheLoopWorkflow.decide, "approve") + assert await handle.result() == "Deleted /tmp/report.txt." diff --git a/tests/agent_framework_plugin/middleware_test.py b/tests/agent_framework_plugin/middleware_test.py new file mode 100644 index 00000000..46937832 --- /dev/null +++ b/tests/agent_framework_plugin/middleware_test.py @@ -0,0 +1,60 @@ +import uuid + +from temporalio.client import Client +from temporalio.worker import Worker + +from agent_framework_plugin.middleware.workflow import REMINDER, MiddlewareWorkflow +from tests.agent_framework_plugin._fake_chat_client import ( + function_call_response, + scripted_plugin, + text_response, +) +from tests.agent_framework_plugin.helpers import ( + INVOKE_MODEL, + count_scheduled_activities, + function_results, + with_plugin, +) + + +async def test_middleware(client: Client) -> None: + plugin, factory = scripted_plugin( + [ + function_call_response( + "delete_file", {"path": "/etc/passwd"}, call_id="c1" + ), + function_call_response( + "delete_file", {"path": "/tmp/scratch.txt"}, call_id="c2" + ), + text_response("The first delete was refused; the second file is gone."), + ] + ) + client = with_plugin(client, plugin) + task_queue = f"agent-framework-middleware-{uuid.uuid4()}" + + async with Worker( + client, + task_queue=task_queue, + workflows=[MiddlewareWorkflow], + max_cached_workflows=0, + ): + handle = await client.start_workflow( + MiddlewareWorkflow.run, + "Delete /etc/passwd, then delete /tmp/scratch.txt.", + id=f"agent-framework-middleware-{uuid.uuid4()}", + task_queue=task_queue, + ) + result = await handle.result() + + assert result == "The first delete was refused; the second file is gone." + # Three model turns; both tool calls were handled in the Workflow. + assert await count_scheduled_activities(handle) == {INVOKE_MODEL: 3} + calls = factory().calls + # The function middleware refused the first call without running the tool + # and let the second one through. + assert function_results(calls[-1]["messages"]) == [ + "Refused: /etc/passwd is outside /tmp/", + "deleted /tmp/scratch.txt", + ] + # The chat middleware added the reminder to every model call. + assert all(call["options"]["instructions"].endswith(REMINDER) for call in calls) diff --git a/tests/agent_framework_plugin/multi_agent_test.py b/tests/agent_framework_plugin/multi_agent_test.py new file mode 100644 index 00000000..aa9d7d3e --- /dev/null +++ b/tests/agent_framework_plugin/multi_agent_test.py @@ -0,0 +1,76 @@ +import uuid + +from temporalio.client import Client +from temporalio.worker import Worker + +from agent_framework_plugin.multi_agent.workflow import MultiAgentWorkflow +from tests.agent_framework_plugin._fake_chat_client import ( + function_call_response, + scripted_plugin, + text_response, +) +from tests.agent_framework_plugin.helpers import ( + INVOKE_MODEL, + INVOKE_MODEL_STREAMING, + activity_summaries, + count_scheduled_activities, + with_plugin, +) + +FACTS = "Fact one. Fact two. Fact three." +PARAGRAPH = "Durable execution keeps going where a process would have stopped." + + +async def test_multi_agent(client: Client) -> None: + # All three agents share the "gpt-4o-mini" client, so one script serves + # them in call order: coordinator, researcher, coordinator, writer, coordinator. + plugin, factory = scripted_plugin( + [ + function_call_response( + "researcher", {"task": "facts about durable execution"}, call_id="c1" + ), + text_response(FACTS), + function_call_response("writer", {"task": FACTS}, call_id="c2"), + text_response(PARAGRAPH), + text_response(f"Here it is: {PARAGRAPH}"), + ] + ) + client = with_plugin(client, plugin) + task_queue = f"agent-framework-multi-agent-{uuid.uuid4()}" + + async with Worker( + client, + task_queue=task_queue, + workflows=[MultiAgentWorkflow], + max_cached_workflows=0, + ): + handle = await client.start_workflow( + MultiAgentWorkflow.run, + "durable execution", + id=f"agent-framework-multi-agent-{uuid.uuid4()}", + task_queue=task_queue, + ) + result = await handle.result() + + assert result == f"Here it is: {PARAGRAPH}" + # Five model calls in one Workflow: three by the coordinator and one per + # sub-agent. Agent.as_tool() runs the sub-agent streaming, so those two + # are the streaming Activity. + assert await count_scheduled_activities(handle) == { + INVOKE_MODEL: 3, + INVOKE_MODEL_STREAMING: 2, + } + assert await activity_summaries(handle) == [ + "coordinator", + "researcher", + "coordinator", + "writer", + "coordinator", + ] + # The sub-agents got the coordinator's tasks with their own instructions. + calls = factory().calls + assert calls[1]["messages"][-1].text == "facts about durable execution" + assert calls[1]["options"]["instructions"] == ( + "List three short facts about the topic you are given." + ) + assert calls[3]["messages"][-1].text == FACTS diff --git a/tests/agent_framework_plugin/open_telemetry_test.py b/tests/agent_framework_plugin/open_telemetry_test.py new file mode 100644 index 00000000..6bbcf88d --- /dev/null +++ b/tests/agent_framework_plugin/open_telemetry_test.py @@ -0,0 +1,67 @@ +import uuid + +import opentelemetry.trace +import pytest +from opentelemetry.sdk.trace.export import SimpleSpanProcessor +from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( + InMemorySpanExporter, +) +from temporalio.client import Client +from temporalio.contrib.opentelemetry import OpenTelemetryPlugin, create_tracer_provider +from temporalio.worker import Worker + +from agent_framework_plugin.open_telemetry.workflow import TracedWorkflow +from tests.agent_framework_plugin._fake_chat_client import scripted_plugin +from tests.agent_framework_plugin.helpers import ( + INVOKE_MODEL, + count_scheduled_activities, +) + + +async def test_model_activity_span_joins_the_trace( + client: Client, monkeypatch: pytest.MonkeyPatch +) -> None: + exporter = InMemorySpanExporter() + provider = create_tracer_provider() + provider.add_span_processor(SimpleSpanProcessor(exporter)) + # Other test modules install their provider with set_tracer_provider, + # which works once per process. Make this one the global for the duration + # of the test only, so the modules stay independent of their order. + monkeypatch.setattr(opentelemetry.trace, "_TRACER_PROVIDER", provider) + + plugin, _ = scripted_plugin(["Traced reply."]) + config = client.config() + config["plugins"] = [ + *config["plugins"], + OpenTelemetryPlugin(add_temporal_spans=True), + plugin, + ] + client = Client(**config) + task_queue = f"agent-framework-open-telemetry-{uuid.uuid4()}" + + async with Worker( + client, + task_queue=task_queue, + workflows=[TracedWorkflow], + max_cached_workflows=0, + ): + tracer = opentelemetry.trace.get_tracer(__name__) + with tracer.start_as_current_span("ask-agent") as root: + handle = await client.start_workflow( + TracedWorkflow.run, + "What is durable execution?", + id=f"agent-framework-open-telemetry-{uuid.uuid4()}", + task_queue=task_queue, + ) + result = await handle.result() + + assert result == "Traced reply." + assert await count_scheduled_activities(handle) == {INVOKE_MODEL: 1} + + trace_id = root.get_span_context().trace_id + spans = {span.name: span for span in exporter.get_finished_spans()} + # The model Activity and the Workflow run are spans of the caller's trace. + for name in ("RunWorkflow:TracedWorkflow", f"RunActivity:{INVOKE_MODEL}"): + assert name in spans, sorted(spans) + assert spans[name].context.trace_id == trace_id + assert spans[f"RunActivity:{INVOKE_MODEL}"].parent is not None diff --git a/tests/agent_framework_plugin/streaming_test.py b/tests/agent_framework_plugin/streaming_test.py new file mode 100644 index 00000000..a3c2feae --- /dev/null +++ b/tests/agent_framework_plugin/streaming_test.py @@ -0,0 +1,70 @@ +import asyncio +import uuid +from datetime import timedelta + +from agent_framework import ChatResponseUpdate +from temporalio.client import Client +from temporalio.contrib.workflow_streams import WorkflowStreamClient +from temporalio.worker import Worker + +from agent_framework_plugin.streaming.workflow import STREAMING_TOPIC, StreamingWorkflow +from tests.agent_framework_plugin._fake_chat_client import scripted_plugin +from tests.agent_framework_plugin.helpers import ( + INVOKE_MODEL, + INVOKE_MODEL_STREAMING, + count_scheduled_activities, + with_plugin, +) + + +async def test_streaming(client: Client) -> None: + expected = "One. Two. Three. Four. Five. That is the whole count." + plugin, _ = scripted_plugin([expected]) + client = with_plugin(client, plugin) + task_queue = f"agent-framework-streaming-{uuid.uuid4()}" + workflow_id = f"agent-framework-streaming-{uuid.uuid4()}" + + async with Worker( + client, + task_queue=task_queue, + workflows=[StreamingWorkflow], + max_cached_workflows=0, + ): + handle = await client.start_workflow( + StreamingWorkflow.run, + "Count from 1 to 5.", + id=workflow_id, + task_queue=task_queue, + ) + + chunks: list[str] = [] + + async def consume() -> None: + stream = WorkflowStreamClient.create(client, workflow_id) + async for item in stream.subscribe( + [STREAMING_TOPIC], + from_offset=0, + result_type=dict, + poll_cooldown=timedelta(milliseconds=10), + ): + update = ChatResponseUpdate.from_dict(item.data) + if update.text: + chunks.append(update.text) + if "".join(chunks) == expected: + return + + consume_task = asyncio.create_task(consume()) + result = await handle.result() + # The subscriber stops once it has the whole answer; the timeout only + # bounds a regression. + await asyncio.wait_for(consume_task, timeout=10) + + # The durable result is the whole text... + assert result == expected + # ...and the same text reached the subscriber in more than one update. + assert len(chunks) > 1 + assert "".join(chunks) == expected + # stream=True ran the streaming Activity, not the plain one. + counts = await count_scheduled_activities(handle) + assert counts == {INVOKE_MODEL_STREAMING: 1} + assert counts[INVOKE_MODEL] == 0 diff --git a/tests/agent_framework_plugin/structured_output_test.py b/tests/agent_framework_plugin/structured_output_test.py new file mode 100644 index 00000000..bf410ad6 --- /dev/null +++ b/tests/agent_framework_plugin/structured_output_test.py @@ -0,0 +1,46 @@ +import uuid + +from temporalio.client import Client +from temporalio.worker import Worker + +from agent_framework_plugin.structured_output.workflow import ( + PersonInfo, + StructuredOutputWorkflow, +) +from tests.agent_framework_plugin._fake_chat_client import scripted_plugin +from tests.agent_framework_plugin.helpers import ( + INVOKE_MODEL, + count_scheduled_activities, + with_plugin, +) + + +async def test_structured_output(client: Client) -> None: + plugin, factory = scripted_plugin( + ['{"name": "John Smith", "age": 30, "occupation": "software engineer"}'] + ) + client = with_plugin(client, plugin) + task_queue = f"agent-framework-structured-output-{uuid.uuid4()}" + + async with Worker( + client, + task_queue=task_queue, + workflows=[StructuredOutputWorkflow], + max_cached_workflows=0, + ): + handle = await client.start_workflow( + StructuredOutputWorkflow.run, + "John Smith is a 30 year-old software engineer.", + id=f"agent-framework-structured-output-{uuid.uuid4()}", + task_queue=task_queue, + ) + result = await handle.result() + + # The Pydantic model crossed the Workflow boundary through the plugin's converter. + assert result == PersonInfo( + name="John Smith", age=30, occupation="software engineer" + ) + assert await count_scheduled_activities(handle) == {INVOKE_MODEL: 1} + # The model Activity received the schema, not the class. + (call,) = factory().calls + assert call["options"]["response_format"]["json_schema"]["name"] == "PersonInfo" diff --git a/tests/agent_framework_plugin/tools_test.py b/tests/agent_framework_plugin/tools_test.py new file mode 100644 index 00000000..28bfd5a7 --- /dev/null +++ b/tests/agent_framework_plugin/tools_test.py @@ -0,0 +1,59 @@ +import uuid + +from temporalio.client import Client +from temporalio.worker import Worker + +from agent_framework_plugin.tools.workflow import ToolsWorkflow, fetch_weather +from tests.agent_framework_plugin._fake_chat_client import ( + function_call_response, + scripted_plugin, + text_response, +) +from tests.agent_framework_plugin.helpers import ( + INVOKE_MODEL, + count_scheduled_activities, + function_results, + with_plugin, +) + + +async def test_tools(client: Client) -> None: + plugin, factory = scripted_plugin( + [ + function_call_response( + "letter_counter", {"word": "strawberry", "letter": "r"}, call_id="c1" + ), + function_call_response( + "fetch_weather", {"city": "San Francisco"}, call_id="c2" + ), + text_response("Three R's, and it is sunny in San Francisco."), + ] + ) + client = with_plugin(client, plugin) + task_queue = f"agent-framework-tools-{uuid.uuid4()}" + + async with Worker( + client, + task_queue=task_queue, + workflows=[ToolsWorkflow], + activities=[fetch_weather], + max_cached_workflows=0, + ): + handle = await client.start_workflow( + ToolsWorkflow.run, + "Count the R's in strawberry and fetch the weather in San Francisco.", + id=f"agent-framework-tools-{uuid.uuid4()}", + task_queue=task_queue, + ) + result = await handle.result() + + assert result == "Three R's, and it is sunny in San Francisco." + # Three model turns and one Activity for the wrapped tool. The pure tool + # ran in the Workflow and left no Activity behind. + assert await count_scheduled_activities(handle) == { + INVOKE_MODEL: 3, + "fetch_weather": 1, + } + counted, weather = function_results(factory().calls[-1]["messages"]) + assert counted == "3" + assert "San Francisco" in weather and "sunny" in weather From b7410ed9065fa9c2a466e5b4e7f5d50980d15f82 Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 22:51:29 -0600 Subject: [PATCH 05/26] Document and register the Agent Framework samples --- .github/CODEOWNERS | 4 +- .github/workflows/ci.yml | 1 + README.md | 1 + agent_framework_plugin/README.md | 107 ++++++++++++++++++ .../continue_as_new/README.md | 31 +++++ agent_framework_plugin/hello_world/README.md | 31 +++++ .../human_in_the_loop/README.md | 29 +++++ agent_framework_plugin/mcp/README.md | 51 +++++++++ agent_framework_plugin/middleware/README.md | 31 +++++ agent_framework_plugin/multi_agent/README.md | 31 +++++ .../open_telemetry/README.md | 61 ++++++++++ agent_framework_plugin/streaming/README.md | 29 +++++ .../structured_output/README.md | 29 +++++ agent_framework_plugin/tools/README.md | 37 ++++++ 14 files changed, 472 insertions(+), 1 deletion(-) create mode 100644 agent_framework_plugin/README.md create mode 100644 agent_framework_plugin/continue_as_new/README.md create mode 100644 agent_framework_plugin/hello_world/README.md create mode 100644 agent_framework_plugin/human_in_the_loop/README.md create mode 100644 agent_framework_plugin/mcp/README.md create mode 100644 agent_framework_plugin/middleware/README.md create mode 100644 agent_framework_plugin/multi_agent/README.md create mode 100644 agent_framework_plugin/open_telemetry/README.md create mode 100644 agent_framework_plugin/streaming/README.md create mode 100644 agent_framework_plugin/structured_output/README.md create mode 100644 agent_framework_plugin/tools/README.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index aef04af2..c010ed17 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -18,6 +18,7 @@ # The AI SDK team owns the AI integration samples and their tests. We add # @temporalio/sdk too, so the SDK team can continue to manage repo-wide concerns. +/agent_framework_plugin/ @temporalio/sdk @temporalio/ai-sdk /claude_agent_sdk_plugin/ @temporalio/sdk @temporalio/ai-sdk /deepagents_plugin/ @temporalio/sdk @temporalio/ai-sdk /google_adk_agents/ @temporalio/sdk @temporalio/ai-sdk @@ -28,7 +29,8 @@ /litellm_activity/ @temporalio/sdk @temporalio/ai-sdk /openai_agents/ @temporalio/sdk @temporalio/ai-sdk /strands_plugin/ @temporalio/sdk @temporalio/ai-sdk -/tests/claude_agent_sdk_plugin/ @temporalio/sdk @temporalio/ai-sdk +/tests/agent_framework_plugin/ @temporalio/sdk @temporalio/ai-sdk +/claude_agent_sdk_plugin/ @temporalio/sdk @temporalio/ai-sdk /tests/deepagents_plugin/ @temporalio/sdk @temporalio/ai-sdk /tests/google_adk_agents/ @temporalio/sdk @temporalio/ai-sdk /tests/google_genai/ @temporalio/sdk @temporalio/ai-sdk diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a7d7c67..a83ce395 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,7 @@ jobs: projects_json="$({ for project in \ + agent_framework_plugin/mcp \ bedrock_agentcore/strands_agent \ lambda_worker \ openai_agents/mcp_v2 diff --git a/README.md b/README.md index 1dff1ea7..3f7fe1ed 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ Some examples require extra dependencies. See each sample's directory for specif * [hello update](hello/hello_update.py) - Send a request to and a response from a client to a workflow execution. * [activity_worker](activity_worker) - Use Python activities from a workflow in another language. +* [agent_framework_plugin](agent_framework_plugin) - Run Microsoft Agent Framework agents as durable Temporal Workflows (model calls, tools, MCP, approvals, streaming, Continue-As-New). * [batch_sliding_window](batch_sliding_window) - Batch processing with a sliding window of child workflows. * [bedrock](bedrock) - Orchestrate a chatbot with Amazon Bedrock. * [bedrock_agentcore/strands_agent](bedrock_agentcore/strands_agent) - Run a AWS Strands Agent with Temporal Plugin on AgentCore Worker. diff --git a/agent_framework_plugin/README.md b/agent_framework_plugin/README.md new file mode 100644 index 00000000..cd939047 --- /dev/null +++ b/agent_framework_plugin/README.md @@ -0,0 +1,107 @@ +# Microsoft Agent Framework samples + +These samples show the [Temporal plugin for Microsoft Agent Framework](https://github.com/temporalio/ai-integrations/tree/main/python/agent_framework), which runs an Agent Framework `Agent` inside a Temporal Workflow. You write the agent the way Agent Framework documents it and swap the chat client for `TemporalChatClient`. Each model call then runs as the `agent_framework.invoke_model` Activity (`agent_framework.invoke_model_streaming` when you stream), with its own timeout and retries. The agent's loop, tools, middleware and approvals run in the Workflow, where Temporal records every step and replays it after a crash or a Worker restart. The docs page is [Microsoft Agent Framework](https://docs.temporal.io/develop/python/integrations/agent-framework). + +> **Pre-release.** The `temporalio-agent-framework` package and its interfaces may change before General Availability. + +`AgentFrameworkPlugin` goes on the Client or on the Worker, not both. These samples put it on `Client.connect(...)`; the Worker built from that Client inherits it. + +## Samples + +| Sample | Description | +|--------|-------------| +| [hello_world](hello_world) | One `Agent` over `TemporalChatClient`, one prompt, one model Activity. Start here. | +| [tools](tools) | A pure `@tool` that runs in the Workflow next to an Activity wrapped with `activity_as_tool`. | +| [mcp](mcp) | Tools from an MCP server through `TemporalMCPToolProvider`; the listing and each call are Activities. Its own uv project, see below. | +| [human_in_the_loop](human_in_the_loop) | A tool with `approval_mode="always_require"`. The Workflow publishes the request through a Query, waits for a `decide` Update and answers on the same session. | +| [middleware](middleware) | Function and chat middleware running in the Workflow: one refuses a tool call, one edits the instructions. | +| [multi_agent](multi_agent) | A coordinator with two sub-agents as tools through `Agent.as_tool()`; every agent's model calls are Activities. | +| [structured_output](structured_output) | `response_format` with a Pydantic model; the Workflow returns the model through the plugin's data converter. | +| [streaming](streaming) | `streaming_topic` publishes each update to a Workflow Stream; the starter prints them as they arrive. | +| [continue_as_new](continue_as_new) | A chat driven by Updates that hands `session.to_dict()` to a fresh run when Temporal suggests Continue-As-New. | +| [open_telemetry](open_telemetry) | `OpenTelemetryPlugin` and `AgentFrameworkPlugin` on one Client: one trace from the starter through the Workflow to the model Activity. | + +## Prerequisites + +1. Python 3.11 or later. Agent Framework does not support 3.10, so on 3.10 the `agent-framework` dependency group resolves to nothing and the tests are not collected. + +2. Install the dependencies from the repository root: + + ```bash + uv sync --group agent-framework + ``` + + This installs `temporalio-agent-framework`, `agent-framework-openai` and Temporal Python SDK 1.36.0 or later. + +3. An OpenAI API key. Each Worker reads it when it builds the `OpenAIChatClient`; the Workflow only carries the model name `gpt-4o-mini`: + + ```bash + export OPENAI_API_KEY=... + ``` + +4. A local Temporal Service from the [Temporal CLI](https://docs.temporal.io/cli): + + ```bash + temporal server start-dev + ``` + +## Running a sample + +Each sample has two scripts. Start the Worker first, then the starter in a second terminal, both from the repository root: + +```bash +# Terminal 1: start the Worker +uv run agent_framework_plugin//run_worker.py + +# Terminal 2: start the Workflow +uv run agent_framework_plugin//run_workflow.py +``` + +For example, for the tools sample: + +```bash +# Terminal 1 +uv run agent_framework_plugin/tools/run_worker.py + +# Terminal 2 +uv run agent_framework_plugin/tools/run_workflow.py +``` + +The `mcp` sample is its own uv project: the plugin's MCP support needs MCP Python SDK 2, and other samples in this repository still pin SDK 1, so they cannot share one lock file. Run it from its directory: + +```bash +cd agent_framework_plugin/mcp +uv sync + +# Terminal 1 +uv run run_worker.py + +# Terminal 2 +uv run run_workflow.py +``` + +## Key features + +- One Activity per model round trip, `agent_framework.invoke_model`, with a 60-second start-to-close timeout in these samples and the plugin's default 10-second heartbeat timeout. Temporal owns retries, so the Workers build the OpenAI client with `AsyncOpenAI(max_retries=0)`. +- Tools run where you put them: a plain `@tool` runs in the Workflow, `activity_as_tool` runs an Activity with its own `start_to_close_timeout`. +- MCP servers are registered on the Worker. `TemporalMCPToolProvider` lists and calls their tools through the `temporalio.mcp..list-tools` and `temporalio.mcp..call-tool` Activities. +- Approvals are Agent Framework's own `approval_mode`; the wait is a Workflow Update with a validator. +- Middleware and context providers run in the Workflow, so they must be deterministic; I/O in middleware goes through `workflow.execute_activity`. +- Streaming publishes `ChatResponseUpdate` dicts to a Workflow Stream topic, batched every 100 milliseconds. +- Long chats carry `session.to_dict()` across Continue-As-New and restore it with `AgentSession.from_dict`. +- `OpenTelemetryPlugin` composes on the same Client; `create_tracer_provider()` keeps the spans Agent Framework starts from Workflow code replay-safe. + +## Tests + +The tests run every sample against the plugin's scripted `FakeChatClient` and a local dev server, with no API key: + +```bash +uv run --group agent-framework pytest tests/agent_framework_plugin +cd agent_framework_plugin/mcp && uv run pytest +``` + +## Related + +- [Temporal plugin for Microsoft Agent Framework](https://github.com/temporalio/ai-integrations/tree/main/python/agent_framework) +- [Microsoft Agent Framework](https://github.com/microsoft/agent-framework) +- [Microsoft Agent Framework docs page](https://docs.temporal.io/develop/python/integrations/agent-framework) diff --git a/agent_framework_plugin/continue_as_new/README.md b/agent_framework_plugin/continue_as_new/README.md new file mode 100644 index 00000000..16228b0c --- /dev/null +++ b/agent_framework_plugin/continue_as_new/README.md @@ -0,0 +1,31 @@ +# Continue-as-new + +A chat that runs as long as you like. Each user turn is an Update, so the caller gets the reply from the same call. History grows with every turn, and `workflow.info().is_continue_as_new_suggested()` turns `True` once the Temporal Service decides it is large enough. The Workflow waits on that or on the `end_chat` Signal, lets in-flight Updates finish with `workflow.all_handlers_finished`, and hands `session.to_dict()` to a fresh run, which restores it with `AgentSession.from_dict`. + +What crosses the boundary: the serialized session, which holds the transcript, any pending approval state and the provider's conversation id. What does not: the `Agent` object, provider connections and spans; the new run builds the agent again. + +## What this sample demonstrates + +- The `turn` Update, the `end_chat` Signal and the `transcript` Query +- An `asyncio.Lock` so turns take the session one at a time +- `workflow.continue_as_new(ChatInput(session=session.to_dict()))` + +## Running the sample + +```bash +# Terminal 1 +uv run agent_framework_plugin/continue_as_new/run_worker.py + +# Terminal 2 +uv run agent_framework_plugin/continue_as_new/run_workflow.py +``` + +The starter sends two turns, prints each reply, prints the number of messages in the session and ends the chat. Two turns do not reach the default threshold, so the Workflow `agent-framework-chat` completes in one run; the test lowers the threshold to see the handoff. In a chat application a UI would drive the Updates and the Workflow would run on, continuing as new whenever history gets large. + +## Files + +| File | Description | +|------|-------------| +| `workflow.py` | `ChatInput` and `ChatWorkflow` with the Update, Signal and Query | +| `run_worker.py` | Registers the plugin, starts the Worker on `agent-framework-chat` | +| `run_workflow.py` | Starts the chat, sends two turns, ends it | diff --git a/agent_framework_plugin/hello_world/README.md b/agent_framework_plugin/hello_world/README.md new file mode 100644 index 00000000..94420cc7 --- /dev/null +++ b/agent_framework_plugin/hello_world/README.md @@ -0,0 +1,31 @@ +# Hello world + +The smallest Agent Framework + Temporal sample: one `Agent` over `TemporalChatClient`, invoked once. The model call runs as the `agent_framework.invoke_model` Activity with a 60-second start-to-close timeout, so Temporal retries it and it survives a Worker restart. + +## What this sample demonstrates + +- `AgentFrameworkPlugin(chat_clients=...)` on the Client, with the `OpenAIChatClient` built on the Worker from `OPENAI_API_KEY` +- `Agent(client=TemporalChatClient("gpt-4o-mini", start_to_close_timeout=...))` in a `@workflow.defn` +- `await agent.run(prompt)` and `response.text`, unchanged from Agent Framework + +## Running the sample + +Prerequisites: `uv sync --group agent-framework`, `OPENAI_API_KEY` in the Worker's environment, and a Temporal dev server (`temporal server start-dev`). Run both commands from the repository root. + +```bash +# Terminal 1 +uv run agent_framework_plugin/hello_world/run_worker.py + +# Terminal 2 +uv run agent_framework_plugin/hello_world/run_workflow.py +``` + +The starter prints `Result:` and the haiku. In the Temporal UI the Workflow `agent-framework-hello-world` shows one `agent_framework.invoke_model` Activity with the summary `gpt-4o-mini`. + +## Files + +| File | Description | +|------|-------------| +| `workflow.py` | `HelloWorldWorkflow` with one `Agent` | +| `run_worker.py` | Builds the plugin with the OpenAI chat client factory and starts the Worker | +| `run_workflow.py` | Executes the Workflow and prints the result | diff --git a/agent_framework_plugin/human_in_the_loop/README.md b/agent_framework_plugin/human_in_the_loop/README.md new file mode 100644 index 00000000..07d54949 --- /dev/null +++ b/agent_framework_plugin/human_in_the_loop/README.md @@ -0,0 +1,29 @@ +# Human in the loop + +A tool that runs only after a person approves. `delete_file` carries `approval_mode="always_require"`, Agent Framework's own approval setting. The run returns with the request in `response.user_input_requests` instead of calling the tool. The Workflow publishes the request through the `pending_approvals` Query, waits for the `decide` Update, and sends the decision back with `request.to_function_approval_response(approved)` on the same session. Agent Framework keys the approval by the function call's id, which the plugin records in history, so the request survives a Worker restart. + +## What this sample demonstrates + +- `@tool(approval_mode="always_require")` and the `while response.user_input_requests` loop +- A Query for what is pending and an Update for the decision. The validator accepts only `approve` or `reject`, so anything else never enters history. It checks the input and not the Workflow's state: validators run before the Workflow coroutine applies the other events of the same Workflow Task, so a decision that lands in the same task as the model's reply would see no pending request yet. +- The approved tool runs in the Workflow; a rejected one never runs, and the model gets Agent Framework's rejection result + +## Running the sample + +```bash +# Terminal 1 +uv run agent_framework_plugin/human_in_the_loop/run_worker.py + +# Terminal 2 +uv run agent_framework_plugin/human_in_the_loop/run_workflow.py +``` + +The starter polls the Query, prints `Approval requested: delete_file({'path': '/tmp/report.txt'})`, sends `approve`, prints `Decision: approve` and then the agent's report. Send `reject` instead to see the rejection path. The Workflow `agent-framework-human-in-the-loop` shows two `agent_framework.invoke_model` Activities: the request and the report. + +## Files + +| File | Description | +|------|-------------| +| `workflow.py` | `delete_file`, `ApprovalRequest` and `HumanInTheLoopWorkflow` | +| `run_worker.py` | Registers the plugin, starts the Worker | +| `run_workflow.py` | Starts the Workflow, polls the Query, sends the Update, prints the result | diff --git a/agent_framework_plugin/mcp/README.md b/agent_framework_plugin/mcp/README.md new file mode 100644 index 00000000..b90699c7 --- /dev/null +++ b/agent_framework_plugin/mcp/README.md @@ -0,0 +1,51 @@ +# MCP + +Give the agent the tools of an MCP server. `AgentFrameworkPlugin(mcp_clients={"echo": ...})` registers the server on the Worker. `TemporalMCPToolProvider("echo")` in `Agent(context_providers=[...])` lists the server's tools before each run and turns each call into an Activity: + +| Activity | When it runs | +|----------|--------------| +| `temporalio.mcp.echo.list-tools` | Before each `agent.run`, because `cache_tools` defaults to `False` | +| `temporalio.mcp.echo.call-tool` | Each time the model calls a tool of the server | + +The included `echo_mcp_server.py` is a one-tool server built on the MCP Python SDK's `MCPServer`. The Worker starts it over stdio with `sys.executable` on the first MCP Activity and keeps the connection for 5 minutes of idleness. Swap in any MCP server by changing the factory in `run_worker.py`. + +This sample is its own uv project. The plugin's MCP support needs MCP Python SDK 2, and other samples in this repository still pin SDK 1, so the two cannot share one lock file. + +## What this sample demonstrates + +- `AgentFrameworkPlugin(mcp_clients=...)` with a factory that returns an `mcp.Client` +- `TemporalMCPToolProvider` as a context provider, with a 30-second start-to-close timeout for its list and call Activities +- The model sees the server's tool schema; the server's connection never leaves the Worker + +## Running the sample + +Prerequisites: Python 3.11 or later, `OPENAI_API_KEY` in the Worker's environment, and a Temporal dev server (`temporal server start-dev`). Change to this directory first: + +```bash +cd agent_framework_plugin/mcp +uv sync +``` + +```bash +# Terminal 1 +uv run run_worker.py + +# Terminal 2 +uv run run_workflow.py +``` + +The Worker spawns the server itself. The starter prints the agent's answer, and the Workflow `agent-framework-mcp` shows one `temporalio.mcp.echo.list-tools`, two `agent_framework.invoke_model` and one `temporalio.mcp.echo.call-tool` Activity. + +## Tests + +`uv run pytest` runs the Workflow against the plugin's scripted chat client and the real echo server, with no API key. `uv run poe lint` runs ruff and mypy. + +## Files + +| File | Description | +|------|-------------| +| `echo_mcp_server.py` | `MCPServer` with one `echo` tool, served over stdio | +| `workflow.py` | `MCPWorkflow` with `TemporalMCPToolProvider("echo")` | +| `run_worker.py` | Registers the chat client and the MCP server factories, starts the Worker | +| `run_workflow.py` | Executes the Workflow and prints the result | +| `tests/mcp_test.py` | Asserts on the model and MCP Activities in the history | diff --git a/agent_framework_plugin/middleware/README.md b/agent_framework_plugin/middleware/README.md new file mode 100644 index 00000000..5f2a2ae3 --- /dev/null +++ b/agent_framework_plugin/middleware/README.md @@ -0,0 +1,31 @@ +# Middleware + +Agent Framework middleware, running in the Workflow. `BlockOutsideTmp` is a `FunctionMiddleware` that refuses `delete_file` calls outside `/tmp` by setting `context.result` and not calling `call_next()`, so the tool never runs and the model reads the refusal. `AppendReminder` is a `ChatMiddleware` that adds one sentence to the instructions of every model call. + +Both run inside the Workflow, next to the agent's loop, so a Workflow Definition that uses them must stay deterministic: no clock, no I/O, no randomness in middleware. Middleware that needs I/O calls `await workflow.execute_activity(...)`. + +## What this sample demonstrates + +- `Agent(middleware=[BlockOutsideTmp(), AppendReminder()])` +- Refusing a tool call from function middleware without an Activity +- Editing `context.options["instructions"]` from chat middleware before the request leaves for the model Activity + +## Running the sample + +```bash +# Terminal 1 +uv run agent_framework_plugin/middleware/run_worker.py + +# Terminal 2 +uv run agent_framework_plugin/middleware/run_workflow.py +``` + +The prompt asks for two deletes. The agent reports that `/etc/passwd` was refused and `/tmp/scratch.txt` was deleted, in one short sentence because of the reminder. The Workflow `agent-framework-middleware` shows three `agent_framework.invoke_model` Activities and nothing for the tool calls. + +## Files + +| File | Description | +|------|-------------| +| `workflow.py` | `delete_file`, the two middleware classes and `MiddlewareWorkflow` | +| `run_worker.py` | Registers the plugin, starts the Worker | +| `run_workflow.py` | Executes the Workflow and prints the result | diff --git a/agent_framework_plugin/multi_agent/README.md b/agent_framework_plugin/multi_agent/README.md new file mode 100644 index 00000000..7c98bf0e --- /dev/null +++ b/agent_framework_plugin/multi_agent/README.md @@ -0,0 +1,31 @@ +# Multi-agent + +A coordinator agent with two sub-agents as tools. `researcher.as_tool()` and `writer.as_tool()` are Agent Framework's own way to delegate; the coordinator decides when to call them. All three agents run in the Workflow, and every model call any of them makes is an Activity, so the whole tree is durable with no per-agent wiring. + +Each agent has its own `TemporalChatClient` with a `summary`, so the Activities read `coordinator`, `researcher`, `coordinator`, `writer`, `coordinator` in the Temporal UI. `Agent.as_tool()` runs the sub-agent streaming, so the two sub-agent calls are `agent_framework.invoke_model_streaming` Activities and the coordinator's three are `agent_framework.invoke_model`. + +## What this sample demonstrates + +- `Agent(tools=[researcher.as_tool(), writer.as_tool()])` +- One `TemporalChatClient` per agent, with `summary=` naming the agent +- Five model Activities in one Workflow run + +## Running the sample + +```bash +# Terminal 1 +uv run agent_framework_plugin/multi_agent/run_worker.py + +# Terminal 2 +uv run agent_framework_plugin/multi_agent/run_workflow.py +``` + +The starter prints the writer's paragraph as relayed by the coordinator. The Workflow `agent-framework-multi-agent` shows the five Activities above. + +## Files + +| File | Description | +|------|-------------| +| `workflow.py` | `chat_client` and `MultiAgentWorkflow` with the three agents | +| `run_worker.py` | Registers the plugin, starts the Worker | +| `run_workflow.py` | Executes the Workflow and prints the result | diff --git a/agent_framework_plugin/open_telemetry/README.md b/agent_framework_plugin/open_telemetry/README.md new file mode 100644 index 00000000..e82c28b0 --- /dev/null +++ b/agent_framework_plugin/open_telemetry/README.md @@ -0,0 +1,61 @@ +# OpenTelemetry + +One trace for a Workflow and the agent it runs: the starter's root span, the Workflow, Agent Framework's own `invoke_agent` span, the model Activity, and the OpenAI client's `chat` span inside that Activity. + +## What this sample demonstrates + +- `Client.connect(plugins=[OpenTelemetryPlugin(add_temporal_spans=True), AgentFrameworkPlugin(...)])` on the Worker's Client: tracing first, so the Worker inherits both in that order. The starter's Client carries `OpenTelemetryPlugin` alone and propagates the trace into the Workflow. +- `create_tracer_provider()` from `temporalio.contrib.opentelemetry` as the global provider in both processes, from [telemetry.py](telemetry.py). Agent Framework starts its `invoke_agent` span from Workflow code, so the provider has to be replay-safe: Workflow spans keep their ids across replays and are exported once. With a plain SDK `TracerProvider` the plugin warns at Worker start, and `OpenTelemetryPlugin` refuses to start the Worker. +- The OpenAI client's own `chat gpt-4o-mini` span runs inside the Activity with real timings. + +## Running the sample + +Start a Temporal dev server in one terminal: + +```bash +temporal server start-dev +``` + +Start [Jaeger](https://www.jaegertracing.io/) to receive and show the traces. It takes OTLP on port 4317, the default in `telemetry.py`; set `OTEL_EXPORTER_OTLP_ENDPOINT` to send the spans somewhere else: + +```bash +docker run --rm -p 16686:16686 -p 4317:4317 jaegertracing/all-in-one:latest +``` + +Then the Worker and the starter, from the repository root: + +```bash +# Terminal 1 +uv run agent_framework_plugin/open_telemetry/run_worker.py + +# Terminal 2 +uv run agent_framework_plugin/open_telemetry/run_workflow.py +``` + +The starter prints the result and the trace id: + +``` +Result: Durable execution runs code to completion despite failures, by recording progress and resuming from it. +Trace ID: aa8b95b37c1b39f76cc9102fa3d1d5b9 +``` + +Open `http://localhost:16686/trace/`. The trace spans two services: + +``` +ask-agent agent-framework-starter +└─ StartWorkflow:TracedWorkflow agent-framework-starter + └─ RunWorkflow:TracedWorkflow agent-framework-worker + └─ invoke_agent assistant agent-framework-worker + └─ StartActivity:agent_framework.invoke_model agent-framework-worker + └─ RunActivity:agent_framework.invoke_model agent-framework-worker + └─ chat gpt-4o-mini agent-framework-worker +``` + +## Files + +| File | Description | +|------|-------------| +| `telemetry.py` | `install_tracer_provider`: a replay-safe provider with an OTLP gRPC exporter | +| `workflow.py` | `TracedWorkflow` with one named `Agent` | +| `run_worker.py` | Both plugins on the Client, starts the Worker | +| `run_workflow.py` | Starts the Workflow under a root span and prints the trace id | diff --git a/agent_framework_plugin/streaming/README.md b/agent_framework_plugin/streaming/README.md new file mode 100644 index 00000000..4ca348d2 --- /dev/null +++ b/agent_framework_plugin/streaming/README.md @@ -0,0 +1,29 @@ +# Streaming + +Model output streamed to a subscriber outside the Workflow. `TemporalChatClient(streaming_topic="events")` makes a streamed call run as the `agent_framework.invoke_model_streaming` Activity, which publishes every `ChatResponseUpdate` to the `events` topic of a `WorkflowStream` hosted on the Workflow, batched every 100 milliseconds (`streaming_batch_interval`). The Workflow still gets the whole response, so the durable result is the same as without streaming. + +## What this sample demonstrates + +- A `WorkflowStream()` hosted on the Workflow +- `agent.run(prompt, stream=True)` with `streaming_topic` set; without `stream=True` the call runs as the plain model Activity and publishes nothing +- `WorkflowStreamClient.subscribe(["events"])` in the starter, decoding each item with `ChatResponseUpdate.from_dict` + +## Running the sample + +```bash +# Terminal 1 +uv run agent_framework_plugin/streaming/run_worker.py + +# Terminal 2 +uv run agent_framework_plugin/streaming/run_workflow.py +``` + +The starter prints the text as it arrives, then `Final result:` with the whole text. The Workflow `agent-framework-streaming` shows one `agent_framework.invoke_model_streaming` Activity and one Signal per published batch. + +## Files + +| File | Description | +|------|-------------| +| `workflow.py` | `StreamingWorkflow` hosting a `WorkflowStream` and a `TemporalChatClient` with `streaming_topic` | +| `run_worker.py` | Registers the plugin, starts the Worker | +| `run_workflow.py` | Starts the Workflow and subscribes to the `events` topic | diff --git a/agent_framework_plugin/structured_output/README.md b/agent_framework_plugin/structured_output/README.md new file mode 100644 index 00000000..4c646291 --- /dev/null +++ b/agent_framework_plugin/structured_output/README.md @@ -0,0 +1,29 @@ +# Structured output + +The agent fills a Pydantic model. `agent.run(prompt, options={"response_format": PersonInfo})` sends the model's JSON schema to the model Activity, and the Workflow reads the parsed `PersonInfo` from `response.value`. The Workflow returns the model itself: the plugin sets Temporal's Pydantic data converter, so the typed value crosses the Workflow boundary without extra setup. + +## What this sample demonstrates + +- `options={"response_format": PersonInfo}` and `response.value` +- A Workflow whose return type is a Pydantic model +- `AgentFrameworkPlugin()` on the starter's Client too, so the starter decodes the result with the same converter + +## Running the sample + +```bash +# Terminal 1 +uv run agent_framework_plugin/structured_output/run_worker.py + +# Terminal 2 +uv run agent_framework_plugin/structured_output/run_workflow.py +``` + +The starter prints `name=John Smith age=30 occupation=software engineer`. The Workflow `agent-framework-structured-output` shows one `agent_framework.invoke_model` Activity. + +## Files + +| File | Description | +|------|-------------| +| `workflow.py` | `PersonInfo` and `StructuredOutputWorkflow` | +| `run_worker.py` | Registers the plugin, starts the Worker | +| `run_workflow.py` | Executes the Workflow and prints the typed result | diff --git a/agent_framework_plugin/tools/README.md b/agent_framework_plugin/tools/README.md new file mode 100644 index 00000000..fac5a9c0 --- /dev/null +++ b/agent_framework_plugin/tools/README.md @@ -0,0 +1,37 @@ +# Tools + +Two tool patterns on one agent: + +| Pattern | When to use it | +|---------|----------------| +| `@tool` from `agent_framework` | Pure logic with no I/O. Runs in the Workflow and leaves no Activity. | +| `@activity.defn` wrapped with `activity_as_tool` | Anything with I/O or a clock. Runs as an Activity with its own timeout and retries. | + +The prompt exercises both. The history shows one `agent_framework.invoke_model` Activity per model turn plus one `fetch_weather` Activity; `letter_counter` runs in the Workflow and does not appear. + +## What this sample demonstrates + +- `activity_as_tool(fetch_weather, start_to_close_timeout=timedelta(seconds=30))` from `temporalio.agent_framework.workflow`; Agent Framework reads the tool's schema from the Activity's signature and docstring +- The plugin cannot tell a pure tool from an I/O tool, so you choose per tool +- Registering the Activity on the Worker next to the Workflow +- A synchronous `def` tool: the plugin runs it on the Workflow's event loop, so keep it quick + +## Running the sample + +```bash +# Terminal 1 +uv run agent_framework_plugin/tools/run_worker.py + +# Terminal 2 +uv run agent_framework_plugin/tools/run_workflow.py +``` + +The starter prints the agent's answer with the letter count and the weather. The Workflow `agent-framework-tools` shows three model Activities and one `fetch_weather` Activity. + +## Files + +| File | Description | +|------|-------------| +| `workflow.py` | `letter_counter`, `fetch_weather` and `ToolsWorkflow` | +| `run_worker.py` | Registers the plugin and the `fetch_weather` Activity, starts the Worker | +| `run_workflow.py` | Executes the Workflow and prints the result | From 53ece11123485a0a8d69aedf2d4c14aa63d68166 Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 23:05:33 -0600 Subject: [PATCH 06/26] Record the human-in-the-loop decision in the Update handler temporalio 1.36 processes a workflow activation's jobs as one batch (workflow logic flag 2), so a signal or update handler no longer runs before the other jobs delivered in the same activation. When the decide Update arrived in the same activation as the lost-connection failure of the read Activity, the wait in ask_human was cancelled before it consumed the decision and the finally block reset it, so the turn redone after a Worker restart asked again. Recording the decision in the handler itself, keyed by the pending call, keeps it for the redone turn on 1.35 and 1.36. Fixes tests/claude_agent_sdk_plugin/human_in_the_loop_test.py::test_decision_survives_a_worker_restart on temporalio 1.36.0 (failed 3 of 3 runs before, passes 3 of 3 after; 3 of 3 on 1.35.0 before and after). The docs snippets python-claude-agent-sdk-human-in-the-loop-approval and -decision change with it. --- .../human_in_the_loop/workflow.py | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/claude_agent_sdk_plugin/human_in_the_loop/workflow.py b/claude_agent_sdk_plugin/human_in_the_loop/workflow.py index 8d7cc41c..d7e5f7b5 100644 --- a/claude_agent_sdk_plugin/human_in_the_loop/workflow.py +++ b/claude_agent_sdk_plugin/human_in_the_loop/workflow.py @@ -29,11 +29,15 @@ class ToolRequest: CONTINUE = "Continue from where you left off." +def call_key(tool_name: str, tool_input: dict[str, Any]) -> str: + """The key a decision is remembered under: the same call asks the same question.""" + return json.dumps([tool_name, tool_input], sort_keys=True) + + @workflow.defn class HumanInTheLoopWorkflow: def __init__(self) -> None: self.pending: ToolRequest | None = None - self.approved: bool | None = None self.decisions: dict[str, bool] = {} @workflow.run @@ -98,15 +102,13 @@ async def ask_human( self, tool_name: str, tool_input: dict[str, Any], context: ToolPermissionContext ) -> PermissionResult: # A turn redone after a Worker restart asks again, so reuse the decision. - call = json.dumps([tool_name, tool_input], sort_keys=True) + call = call_key(tool_name, tool_input) if call not in self.decisions: self.pending = ToolRequest(context.tool_use_id or "", tool_name, tool_input) try: - await workflow.wait_condition(lambda: self.approved is not None) - self.decisions[call] = self.approved is True + await workflow.wait_condition(lambda: call in self.decisions) finally: self.pending = None - self.approved = None if self.decisions[call]: return PermissionResultAllow() return PermissionResultDeny(message="A person denied this tool call") @@ -120,11 +122,21 @@ def pending_tool_calls(self) -> ToolRequest | None: # @@@SNIPSTART python-claude-agent-sdk-human-in-the-loop-decision @workflow.update def decide(self, approved: bool) -> None: - self.approved = approved + # Remember the decision here, not after the wait in ask_human. That wait + # is cancelled when Claude Code's connection is lost, and the turn + # redone after the restart must find the decision already recorded. + assert self.pending is not None + self.decisions[call_key(self.pending.tool_name, self.pending.tool_input)] = ( + approved + ) # @@@SNIPEND @decide.validator def validate_decide(self, approved: bool) -> None: - if self.pending is None or self.approved is not None: + if ( + self.pending is None + or call_key(self.pending.tool_name, self.pending.tool_input) + in self.decisions + ): raise ValueError("No tool call is waiting for a decision") From ef55d93cf742dfadd3135c8c95803b658bf54bc2 Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 23:26:59 -0600 Subject: [PATCH 07/26] Restore the Claude tests row in CODEOWNERS --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c010ed17..aa30595d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -30,7 +30,7 @@ /openai_agents/ @temporalio/sdk @temporalio/ai-sdk /strands_plugin/ @temporalio/sdk @temporalio/ai-sdk /tests/agent_framework_plugin/ @temporalio/sdk @temporalio/ai-sdk -/claude_agent_sdk_plugin/ @temporalio/sdk @temporalio/ai-sdk +/tests/claude_agent_sdk_plugin/ @temporalio/sdk @temporalio/ai-sdk /tests/deepagents_plugin/ @temporalio/sdk @temporalio/ai-sdk /tests/google_adk_agents/ @temporalio/sdk @temporalio/ai-sdk /tests/google_genai/ @temporalio/sdk @temporalio/ai-sdk From a7fbfcd488e4a022693d8eabf036652ec07024ac Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 23:27:00 -0600 Subject: [PATCH 08/26] Key the human-in-the-loop decision to its request --- .../human_in_the_loop/README.md | 9 +- .../human_in_the_loop/run_workflow.py | 11 ++- .../human_in_the_loop/workflow.py | 89 ++++++++++++------- .../human_in_the_loop_test.py | 78 ++++++++++++---- 4 files changed, 130 insertions(+), 57 deletions(-) diff --git a/agent_framework_plugin/human_in_the_loop/README.md b/agent_framework_plugin/human_in_the_loop/README.md index 07d54949..c541938f 100644 --- a/agent_framework_plugin/human_in_the_loop/README.md +++ b/agent_framework_plugin/human_in_the_loop/README.md @@ -1,11 +1,12 @@ # Human in the loop -A tool that runs only after a person approves. `delete_file` carries `approval_mode="always_require"`, Agent Framework's own approval setting. The run returns with the request in `response.user_input_requests` instead of calling the tool. The Workflow publishes the request through the `pending_approvals` Query, waits for the `decide` Update, and sends the decision back with `request.to_function_approval_response(approved)` on the same session. Agent Framework keys the approval by the function call's id, which the plugin records in history, so the request survives a Worker restart. +A tool that runs only after a person approves. `delete_file` carries `approval_mode="always_require"`, Agent Framework's own approval setting. The run returns with the request in `response.user_input_requests` instead of calling the tool. The Workflow publishes the request through the `pending_approvals` Query, waits for a `decide` Update carrying `Decision(request_id, approved)` for each request, and sends the decisions back with `request.to_function_approval_response(approved)` on the same session. Agent Framework keys the approval by the function call's id, which the plugin records in history, so the request survives a Worker restart. ## What this sample demonstrates - `@tool(approval_mode="always_require")` and the `while response.user_input_requests` loop -- A Query for what is pending and an Update for the decision. The validator accepts only `approve` or `reject`, so anything else never enters history. It checks the input and not the Workflow's state: validators run before the Workflow coroutine applies the other events of the same Workflow Task, so a decision that lands in the same task as the model's reply would see no pending request yet. +- A Query for what is pending and an Update keyed by request id. The validator checks only the input (a non-empty `request_id`), so bad input never enters history. The handler fails the Update with an `ApplicationError` for an id that is already decided or not pending, so a double submit or a typo cannot answer a later request. +- Why the handler waits for the model's reply to be read first: a decision can land in the same Workflow Task as the reply that raised the request, and validators and handlers run before the loop in `run` has read that reply. - The approved tool runs in the Workflow; a rejected one never runs, and the model gets Agent Framework's rejection result ## Running the sample @@ -18,12 +19,12 @@ uv run agent_framework_plugin/human_in_the_loop/run_worker.py uv run agent_framework_plugin/human_in_the_loop/run_workflow.py ``` -The starter polls the Query, prints `Approval requested: delete_file({'path': '/tmp/report.txt'})`, sends `approve`, prints `Decision: approve` and then the agent's report. Send `reject` instead to see the rejection path. The Workflow `agent-framework-human-in-the-loop` shows two `agent_framework.invoke_model` Activities: the request and the report. +The starter polls the Query, prints `Approval requested: delete_file({'path': '/tmp/report.txt'})`, sends `Decision(request_id=..., approved=True)` with the id the Query returned, prints `Decision: approved` and then the agent's report. Set `approved=False` in `run_workflow.py` to see the rejection path. The Workflow `agent-framework-human-in-the-loop` shows two `agent_framework.invoke_model` Activities: the request and the report. ## Files | File | Description | |------|-------------| -| `workflow.py` | `delete_file`, `ApprovalRequest` and `HumanInTheLoopWorkflow` | +| `workflow.py` | `delete_file`, `ApprovalRequest`, `Decision` and `HumanInTheLoopWorkflow` | | `run_worker.py` | Registers the plugin, starts the Worker | | `run_workflow.py` | Starts the Workflow, polls the Query, sends the Update, prints the result | diff --git a/agent_framework_plugin/human_in_the_loop/run_workflow.py b/agent_framework_plugin/human_in_the_loop/run_workflow.py index 339a3510..1d742fd4 100644 --- a/agent_framework_plugin/human_in_the_loop/run_workflow.py +++ b/agent_framework_plugin/human_in_the_loop/run_workflow.py @@ -8,6 +8,7 @@ from agent_framework_plugin.human_in_the_loop.workflow import ( ApprovalRequest, + Decision, HumanInTheLoopWorkflow, ) @@ -34,10 +35,12 @@ async def main() -> None: for request in pending: print(f"Approval requested: {request.tool_name}({request.arguments})") - - # Send "reject" to see the agent handle a rejection. - outcome = await handle.execute_update(HumanInTheLoopWorkflow.decide, "approve") - print(f"Decision: {outcome}") + # Set approved=False to see the rejection path. + outcome = await handle.execute_update( + HumanInTheLoopWorkflow.decide, + Decision(request_id=request.request_id, approved=True), + ) + print(f"Decision: {outcome}") result = await handle.result() print(f"Result: {result}") diff --git a/agent_framework_plugin/human_in_the_loop/workflow.py b/agent_framework_plugin/human_in_the_loop/workflow.py index 14513df0..0cfb0275 100644 --- a/agent_framework_plugin/human_in_the_loop/workflow.py +++ b/agent_framework_plugin/human_in_the_loop/workflow.py @@ -3,8 +3,9 @@ ``delete_file`` carries ``approval_mode="always_require"``, Agent Framework's own approval setting. The run returns with the request in ``response.user_input_requests`` instead of calling the tool. The Workflow -publishes the request through a Query, waits for a ``decide`` Update, and sends -the decision back on the same session. Only an approved call runs the tool. +publishes the request through a Query, waits for a ``decide`` Update for each +request id, and sends the decisions back on the same session. Only an approved +call runs the tool. """ # @@@SNIPSTART python-agent-framework-human-in-the-loop-workflow @@ -12,9 +13,10 @@ from datetime import timedelta from typing import Any -from agent_framework import Agent, Content, tool +from agent_framework import Agent, AgentResponse, AgentSession, Content, tool from temporalio import workflow from temporalio.agent_framework import TemporalChatClient +from temporalio.exceptions import ApplicationError @tool(approval_mode="always_require") @@ -30,6 +32,12 @@ class ApprovalRequest: arguments: dict[str, Any] = field(default_factory=dict) +@dataclass +class Decision: + request_id: str + approved: bool + + def approval_request(request: Content) -> ApprovalRequest: call = request.function_call return ApprovalRequest( @@ -42,27 +50,39 @@ def approval_request(request: Content) -> ApprovalRequest: @workflow.defn class HumanInTheLoopWorkflow: def __init__(self) -> None: - self._pending: list[ApprovalRequest] = [] - self._decision: str | None = None + self._pending: dict[str, ApprovalRequest] = {} + self._decisions: dict[str, bool] = {} + self._awaiting_reply = False @workflow.query def pending_approvals(self) -> list[ApprovalRequest]: - return list(self._pending) + return list(self._pending.values()) @workflow.update - async def decide(self, decision: str) -> str: - # Applies to every pending request. The validator below runs before the - # Workflow sees the model's reply, so it checks the input, not the state. - self._decision = decision - return decision + async def decide(self, decision: Decision) -> str: + # A decision can arrive in the same Workflow Task as the model's reply, + # before the loop in run has read the requests in that reply. Judge the + # id once the reply has been read. + await workflow.wait_condition(lambda: not self._awaiting_reply) + if decision.request_id in self._decisions: + raise ApplicationError( + f"approval request {decision.request_id!r} is already decided", + non_retryable=True, + ) + if decision.request_id not in self._pending: + raise ApplicationError( + f"no pending approval request {decision.request_id!r}", + non_retryable=True, + ) + self._decisions[decision.request_id] = decision.approved + return "approved" if decision.approved else "rejected" @decide.validator - def validate_decide(self, decision: str) -> None: - # Anything else never enters the Workflow's history. - if decision not in ("approve", "reject"): - raise ValueError( - f"decision must be 'approve' or 'reject', not {decision!r}" - ) + def validate_decide(self, decision: Decision) -> None: + # Validators run before the Workflow sees the model's reply, so this one + # checks the input and not the state. Bad input never enters history. + if not decision.request_id: + raise ValueError("request_id is required") @workflow.run async def run(self, prompt: str) -> str: @@ -74,24 +94,33 @@ async def run(self, prompt: str) -> str: tools=[delete_file], ) # Approvals are keyed by the function call's id and resolved against - # the session, so both runs use the same session. + # the session, so every run uses the same session. session = agent.create_session() - response = await agent.run(prompt, session=session) + response = await self._reply(agent, prompt, session) while response.user_input_requests: requests = response.user_input_requests - self._pending = [approval_request(request) for request in requests] - await workflow.wait_condition(lambda: self._decision is not None) - approved = self._decision == "approve" - self._decision = None - self._pending = [] - response = await agent.run( - [ - request.to_function_approval_response(approved) - for request in requests - ], - session=session, + self._pending = { + item.request_id: item for item in map(approval_request, requests) + } + await workflow.wait_condition( + lambda: all(id in self._decisions for id in self._pending) ) + replies = [ + request.to_function_approval_response(self._decisions[request.id or ""]) + for request in requests + ] + self._pending = {} + response = await self._reply(agent, replies, session) return response.text + async def _reply( + self, agent: Agent, messages: Any, session: AgentSession + ) -> AgentResponse: + self._awaiting_reply = True + try: + return await agent.run(messages, session=session) + finally: + self._awaiting_reply = False + # @@@SNIPEND diff --git a/tests/agent_framework_plugin/human_in_the_loop_test.py b/tests/agent_framework_plugin/human_in_the_loop_test.py index 875c586a..fe8137d9 100644 --- a/tests/agent_framework_plugin/human_in_the_loop_test.py +++ b/tests/agent_framework_plugin/human_in_the_loop_test.py @@ -7,6 +7,7 @@ from agent_framework_plugin.human_in_the_loop.workflow import ( ApprovalRequest, + Decision, HumanInTheLoopWorkflow, ) from tests.agent_framework_plugin._fake_chat_client import ( @@ -32,10 +33,13 @@ def _script(final_text: str) -> list: ] -async def _pending(handle: WorkflowHandle) -> list[ApprovalRequest]: +async def _pending( + handle: WorkflowHandle, *, after: str | None = None +) -> list[ApprovalRequest]: + """Return the pending requests once there are any, other than ``after``.""" for _ in range(100): pending = await handle.query(HumanInTheLoopWorkflow.pending_approvals) - if pending: + if pending and all(request.request_id != after for request in pending): return pending await asyncio.sleep(0.1) raise AssertionError("the workflow never surfaced an approval request") @@ -62,10 +66,13 @@ async def test_approve(client: Client) -> None: assert request.tool_name == "delete_file" assert request.arguments == {"path": "/tmp/report.txt"} assert request.request_id - outcome = await handle.execute_update(HumanInTheLoopWorkflow.decide, "approve") + outcome = await handle.execute_update( + HumanInTheLoopWorkflow.decide, + Decision(request_id=request.request_id, approved=True), + ) result = await handle.result() - assert outcome == "approve" + assert outcome == "approved" assert result == "Deleted /tmp/report.txt." # Two model calls: the request and the report. The approved tool ran in # the Workflow, so it left no Activity, and its result reached the model. @@ -92,11 +99,14 @@ async def test_reject(client: Client) -> None: id=f"agent-framework-hitl-reject-{uuid.uuid4()}", task_queue=task_queue, ) - await _pending(handle) - outcome = await handle.execute_update(HumanInTheLoopWorkflow.decide, "reject") + (request,) = await _pending(handle) + outcome = await handle.execute_update( + HumanInTheLoopWorkflow.decide, + Decision(request_id=request.request_id, approved=False), + ) result = await handle.result() - assert outcome == "reject" + assert outcome == "rejected" assert result == "Understood, I did not delete it." # The tool never ran: the model got Agent Framework's rejection result. (tool_result,) = function_results(factory().calls[-1]["messages"]) @@ -105,10 +115,22 @@ async def test_reject(client: Client) -> None: assert await count_scheduled_activities(handle) == {INVOKE_MODEL: 2} -async def test_validator_rejects_other_decisions(client: Client) -> None: - plugin, _ = scripted_plugin(_script("Deleted /tmp/report.txt.")) +async def test_bad_decisions_are_rejected(client: Client) -> None: + # Two approval rounds, so a decision for the first request is stale while + # the second one is pending. + plugin, _ = scripted_plugin( + [ + function_call_response( + "delete_file", {"path": "/tmp/a.txt"}, call_id="call-1" + ), + function_call_response( + "delete_file", {"path": "/tmp/b.txt"}, call_id="call-2" + ), + text_response("Deleted both files."), + ] + ) client = with_plugin(client, plugin) - task_queue = f"agent-framework-hitl-validator-{uuid.uuid4()}" + task_queue = f"agent-framework-hitl-bad-{uuid.uuid4()}" async with Worker( client, @@ -118,14 +140,32 @@ async def test_validator_rejects_other_decisions(client: Client) -> None: ): handle = await client.start_workflow( HumanInTheLoopWorkflow.run, - "Please delete /tmp/report.txt", - id=f"agent-framework-hitl-validator-{uuid.uuid4()}", + "Please delete /tmp/a.txt and /tmp/b.txt", + id=f"agent-framework-hitl-bad-{uuid.uuid4()}", task_queue=task_queue, ) - await _pending(handle) - # The validator keeps anything but approve or reject out of history. - with pytest.raises(WorkflowUpdateFailedError): - await handle.execute_update(HumanInTheLoopWorkflow.decide, "maybe") - # The Workflow is still waiting and takes the real decision. - await handle.execute_update(HumanInTheLoopWorkflow.decide, "approve") - assert await handle.result() == "Deleted /tmp/report.txt." + (first,) = await _pending(handle) + assert first.arguments == {"path": "/tmp/a.txt"} + await handle.execute_update( + HumanInTheLoopWorkflow.decide, + Decision(request_id=first.request_id, approved=True), + ) + (second,) = await _pending(handle, after=first.request_id) + assert second.arguments == {"path": "/tmp/b.txt"} + + # The validator keeps an empty id out of history; the handler fails the + # Update for a stale id and for an unknown one. The Workflow keeps waiting. + for bad in ("", first.request_id, "not-a-request"): + with pytest.raises(WorkflowUpdateFailedError): + await handle.execute_update( + HumanInTheLoopWorkflow.decide, + Decision(request_id=bad, approved=True), + ) + assert await handle.query(HumanInTheLoopWorkflow.pending_approvals) == [second] + + await handle.execute_update( + HumanInTheLoopWorkflow.decide, + Decision(request_id=second.request_id, approved=True), + ) + assert await handle.result() == "Deleted both files." + assert await count_scheduled_activities(handle) == {INVOKE_MODEL: 3} From 27f51578a0c630903328d920cc287615cca16f28 Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 23:27:00 -0600 Subject: [PATCH 09/26] Keep the middleware snippet self-contained --- agent_framework_plugin/middleware/workflow.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/agent_framework_plugin/middleware/workflow.py b/agent_framework_plugin/middleware/workflow.py index da2508fb..a6041956 100644 --- a/agent_framework_plugin/middleware/workflow.py +++ b/agent_framework_plugin/middleware/workflow.py @@ -19,9 +19,6 @@ from temporalio import workflow from temporalio.agent_framework import TemporalChatClient -ALLOWED_PREFIX = "/tmp/" -REMINDER = "Answer in one short sentence." - @tool def delete_file(path: str) -> str: @@ -30,6 +27,10 @@ def delete_file(path: str) -> str: # @@@SNIPSTART python-agent-framework-middleware-middleware +ALLOWED_PREFIX = "/tmp/" +REMINDER = "Answer in one short sentence." + + class BlockOutsideTmp(FunctionMiddleware): """Refuses delete_file calls outside /tmp without running the tool.""" From fe521e2037eb03ea0d4460baf63fadc342b7dbdf Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 23:27:00 -0600 Subject: [PATCH 10/26] Fail the Claude decision Update instead of the Workflow Task An AssertionError in an Update handler fails the Workflow Task, which the Worker retries while the Update stays stuck; an ApplicationError fails only this Update. call_key is now a static method inside the python-claude-agent-sdk-human-in-the-loop-approval snippet, so the quoted code shows it. No marker name changes. --- .../human_in_the_loop/workflow.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/claude_agent_sdk_plugin/human_in_the_loop/workflow.py b/claude_agent_sdk_plugin/human_in_the_loop/workflow.py index d7e5f7b5..2eddd82a 100644 --- a/claude_agent_sdk_plugin/human_in_the_loop/workflow.py +++ b/claude_agent_sdk_plugin/human_in_the_loop/workflow.py @@ -16,6 +16,7 @@ ) from temporalio import workflow from temporalio.claude_agent_sdk import TemporalTransport +from temporalio.exceptions import ApplicationError @dataclass @@ -29,11 +30,6 @@ class ToolRequest: CONTINUE = "Continue from where you left off." -def call_key(tool_name: str, tool_input: dict[str, Any]) -> str: - """The key a decision is remembered under: the same call asks the same question.""" - return json.dumps([tool_name, tool_input], sort_keys=True) - - @workflow.defn class HumanInTheLoopWorkflow: def __init__(self) -> None: @@ -98,11 +94,16 @@ async def ask(self, prompt: str) -> str: pass # @@@SNIPSTART python-claude-agent-sdk-human-in-the-loop-approval + @staticmethod + def call_key(tool_name: str, tool_input: dict[str, Any]) -> str: + """The key a decision is remembered under: the same call asks the same question.""" + return json.dumps([tool_name, tool_input], sort_keys=True) + async def ask_human( self, tool_name: str, tool_input: dict[str, Any], context: ToolPermissionContext ) -> PermissionResult: # A turn redone after a Worker restart asks again, so reuse the decision. - call = call_key(tool_name, tool_input) + call = self.call_key(tool_name, tool_input) if call not in self.decisions: self.pending = ToolRequest(context.tool_use_id or "", tool_name, tool_input) try: @@ -122,13 +123,14 @@ def pending_tool_calls(self) -> ToolRequest | None: # @@@SNIPSTART python-claude-agent-sdk-human-in-the-loop-decision @workflow.update def decide(self, approved: bool) -> None: + if self.pending is None: + # Fails this Update only; an assertion would fail the Workflow Task. + raise ApplicationError("No tool call is waiting for a decision") # Remember the decision here, not after the wait in ask_human. That wait # is cancelled when Claude Code's connection is lost, and the turn # redone after the restart must find the decision already recorded. - assert self.pending is not None - self.decisions[call_key(self.pending.tool_name, self.pending.tool_input)] = ( - approved - ) + call = self.call_key(self.pending.tool_name, self.pending.tool_input) + self.decisions[call] = approved # @@@SNIPEND @@ -136,7 +138,7 @@ def decide(self, approved: bool) -> None: def validate_decide(self, approved: bool) -> None: if ( self.pending is None - or call_key(self.pending.tool_name, self.pending.tool_input) + or self.call_key(self.pending.tool_name, self.pending.tool_input) in self.decisions ): raise ValueError("No tool call is waiting for a decision") From 4124a0434a7714f7bd59766ae5956d02d28a987b Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 23:27:00 -0600 Subject: [PATCH 11/26] Read the transcript through the provider's source id --- agent_framework_plugin/continue_as_new/workflow.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/agent_framework_plugin/continue_as_new/workflow.py b/agent_framework_plugin/continue_as_new/workflow.py index 2a636027..070abc8e 100644 --- a/agent_framework_plugin/continue_as_new/workflow.py +++ b/agent_framework_plugin/continue_as_new/workflow.py @@ -13,7 +13,7 @@ from datetime import timedelta from typing import Any -from agent_framework import Agent, AgentSession +from agent_framework import Agent, AgentSession, InMemoryHistoryProvider from temporalio import workflow from temporalio.agent_framework import TemporalChatClient @@ -49,7 +49,8 @@ def transcript(self) -> list[dict[str, str]]: if self._session is None: return [] # Agent Framework's in-memory history provider keeps the messages here. - messages = self._session.state.get("in_memory", {}).get("messages", []) + state = self._session.state.get(InMemoryHistoryProvider.DEFAULT_SOURCE_ID, {}) + messages = state.get("messages", []) return [ { "role": str(getattr(message.role, "value", message.role)), From 035d61d9fa6c0339e3504d48f5038a7ee71874d6 Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 23:27:00 -0600 Subject: [PATCH 12/26] Point the sample README at poe test --- agent_framework_plugin/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/agent_framework_plugin/README.md b/agent_framework_plugin/README.md index cd939047..459d6847 100644 --- a/agent_framework_plugin/README.md +++ b/agent_framework_plugin/README.md @@ -96,8 +96,9 @@ uv run run_workflow.py The tests run every sample against the plugin's scripted `FakeChatClient` and a local dev server, with no API key: ```bash -uv run --group agent-framework pytest tests/agent_framework_plugin -cd agent_framework_plugin/mcp && uv run pytest +uv run poe test tests/agent_framework_plugin +uv run poe test tests/agent_framework_plugin --workflow-environment time-skipping +cd agent_framework_plugin/mcp && uv run poe test ``` ## Related From 440e36a054661a9da1de741ebdcc910af3ae359f Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 23:27:00 -0600 Subject: [PATCH 13/26] Pin the plugin to its round-1 revision Both lock files move temporalio-agent-framework from b5ed079c to 1c2006ae on dabh/agent-framework-python; no signature the samples use changed. --- agent_framework_plugin/mcp/uv.lock | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agent_framework_plugin/mcp/uv.lock b/agent_framework_plugin/mcp/uv.lock index c3d1edeb..05851ae8 100644 --- a/agent_framework_plugin/mcp/uv.lock +++ b/agent_framework_plugin/mcp/uv.lock @@ -1604,7 +1604,7 @@ wheels = [ [[package]] name = "temporalio-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python#b5ed079cb18e313199ef6437de491e536144623f" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python#1c2006aec68659eb2210cbb5a04738003df85d57" } dependencies = [ { name = "agent-framework-core" }, { name = "pydantic" }, diff --git a/uv.lock b/uv.lock index 14f82451..e01071d9 100644 --- a/uv.lock +++ b/uv.lock @@ -4854,7 +4854,7 @@ pydantic = [ [[package]] name = "temporalio-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python#b5ed079cb18e313199ef6437de491e536144623f" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python#1c2006aec68659eb2210cbb5a04738003df85d57" } dependencies = [ { name = "agent-framework-core" }, { name = "pydantic" }, From 16dbbfa226ab0542b1c7dd34907053f20a27bd33 Mon Sep 17 00:00:00 2001 From: DABH Date: Fri, 13 Nov 2026 00:03:57 -0600 Subject: [PATCH 14/26] Pin the plugin to its current revision Both locks move temporalio-agent-framework from 1c2006ae to 509bfab4 on dabh/agent-framework-python; the only change in each lock is that commit hash. --- agent_framework_plugin/mcp/uv.lock | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agent_framework_plugin/mcp/uv.lock b/agent_framework_plugin/mcp/uv.lock index 05851ae8..a4de261e 100644 --- a/agent_framework_plugin/mcp/uv.lock +++ b/agent_framework_plugin/mcp/uv.lock @@ -1604,7 +1604,7 @@ wheels = [ [[package]] name = "temporalio-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python#1c2006aec68659eb2210cbb5a04738003df85d57" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python#509bfab4ffc16410ce28aa4c092c8c15853b0874" } dependencies = [ { name = "agent-framework-core" }, { name = "pydantic" }, diff --git a/uv.lock b/uv.lock index e01071d9..d345bd7d 100644 --- a/uv.lock +++ b/uv.lock @@ -4854,7 +4854,7 @@ pydantic = [ [[package]] name = "temporalio-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python#1c2006aec68659eb2210cbb5a04738003df85d57" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python#509bfab4ffc16410ce28aa4c092c8c15853b0874" } dependencies = [ { name = "agent-framework-core" }, { name = "pydantic" }, From 4002670c6cdd2239d1a466759d2e1cdb2e39d865 Mon Sep 17 00:00:00 2001 From: DABH Date: Fri, 13 Nov 2026 00:03:57 -0600 Subject: [PATCH 15/26] State the Python floor as the plugin's requires-python --- agent_framework_plugin/README.md | 2 +- agent_framework_plugin/mcp/pyproject.toml | 5 +++-- tests/agent_framework_plugin/conftest.py | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/agent_framework_plugin/README.md b/agent_framework_plugin/README.md index 459d6847..3adf4416 100644 --- a/agent_framework_plugin/README.md +++ b/agent_framework_plugin/README.md @@ -23,7 +23,7 @@ These samples show the [Temporal plugin for Microsoft Agent Framework](https://g ## Prerequisites -1. Python 3.11 or later. Agent Framework does not support 3.10, so on 3.10 the `agent-framework` dependency group resolves to nothing and the tests are not collected. +1. Python 3.11 or later. The plugin declares `requires-python = ">=3.11"`, as Temporal Python SDK 1.36 does, so on 3.10 the `agent-framework` dependency group resolves to nothing and the tests are not collected. 2. Install the dependencies from the repository root: diff --git a/agent_framework_plugin/mcp/pyproject.toml b/agent_framework_plugin/mcp/pyproject.toml index ace72b4b..e5d0d07b 100644 --- a/agent_framework_plugin/mcp/pyproject.toml +++ b/agent_framework_plugin/mcp/pyproject.toml @@ -3,8 +3,9 @@ name = "temporalio-samples-agent-framework-mcp" version = "0.1.0" description = "Temporal.io Python SDK sample: Microsoft Agent Framework with an MCP server" requires-python = ">=3.10" -# Agent Framework needs Python 3.11. On 3.10 these resolve to nothing and the -# tests are not collected, so the shared standalone CI matrix stays green. +# The plugin declares requires-python >=3.11, as temporalio 1.36 does. On 3.10 +# these resolve to nothing and the tests are not collected, so the shared +# standalone CI matrix stays green. dependencies = [ "temporalio-agent-framework[mcp] ; python_version >= '3.11'", "agent-framework-openai>=1.17,<2 ; python_version >= '3.11'", diff --git a/tests/agent_framework_plugin/conftest.py b/tests/agent_framework_plugin/conftest.py index 94fc2582..99b88960 100644 --- a/tests/agent_framework_plugin/conftest.py +++ b/tests/agent_framework_plugin/conftest.py @@ -2,7 +2,8 @@ The plugin ships as the standalone `temporalio-agent-framework` package, which the `agent-framework` dependency group installs on Python >= 3.11 only, because -Agent Framework itself needs 3.11. These test modules import +the plugin declares `requires-python = ">=3.11"`, as Temporal Python SDK 1.36 +does. These test modules import `temporalio.agent_framework` at module load, which would raise `ImportError` during collection on 3.10 or in an environment synced without the group, and fail the whole session. From be77fc183d94d6594d74b48846fb710ce102ba9d Mon Sep 17 00:00:00 2001 From: DABH Date: Fri, 13 Nov 2026 00:03:57 -0600 Subject: [PATCH 16/26] Use poe test in the mcp README and note the no-collector case --- agent_framework_plugin/mcp/README.md | 2 +- agent_framework_plugin/open_telemetry/README.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/agent_framework_plugin/mcp/README.md b/agent_framework_plugin/mcp/README.md index b90699c7..7e844d82 100644 --- a/agent_framework_plugin/mcp/README.md +++ b/agent_framework_plugin/mcp/README.md @@ -38,7 +38,7 @@ The Worker spawns the server itself. The starter prints the agent's answer, and ## Tests -`uv run pytest` runs the Workflow against the plugin's scripted chat client and the real echo server, with no API key. `uv run poe lint` runs ruff and mypy. +`uv run poe test` runs the Workflow against the plugin's scripted chat client and the real echo server, with no API key. `uv run poe lint` runs ruff and mypy. ## Files diff --git a/agent_framework_plugin/open_telemetry/README.md b/agent_framework_plugin/open_telemetry/README.md index e82c28b0..30d0fa0e 100644 --- a/agent_framework_plugin/open_telemetry/README.md +++ b/agent_framework_plugin/open_telemetry/README.md @@ -22,6 +22,8 @@ Start [Jaeger](https://www.jaegertracing.io/) to receive and show the traces. It docker run --rm -p 16686:16686 -p 4317:4317 jaegertracing/all-in-one:latest ``` +Without a collector the Workflow still completes; each process logs an export warning and an export error, and the starter exits a few seconds late. + Then the Worker and the starter, from the repository root: ```bash From 83d46bfba073a87b83b286a482f390f59a2bf146 Mon Sep 17 00:00:00 2001 From: DABH Date: Fri, 13 Nov 2026 00:03:57 -0600 Subject: [PATCH 17/26] Name the request id in the approval wait --- agent_framework_plugin/human_in_the_loop/workflow.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/agent_framework_plugin/human_in_the_loop/workflow.py b/agent_framework_plugin/human_in_the_loop/workflow.py index 0cfb0275..65de5f47 100644 --- a/agent_framework_plugin/human_in_the_loop/workflow.py +++ b/agent_framework_plugin/human_in_the_loop/workflow.py @@ -103,7 +103,9 @@ async def run(self, prompt: str) -> str: item.request_id: item for item in map(approval_request, requests) } await workflow.wait_condition( - lambda: all(id in self._decisions for id in self._pending) + lambda: all( + request_id in self._decisions for request_id in self._pending + ) ) replies = [ request.to_function_approval_response(self._decisions[request.id or ""]) From 7c737ec83531c1e6e7a57911884dc09dc58098c2 Mon Sep 17 00:00:00 2001 From: DABH Date: Fri, 13 Nov 2026 00:49:52 -0600 Subject: [PATCH 18/26] Rename the samples to microsoft_agent_framework_plugin The plugin is now temporalio-microsoft-agent-framework, since an unqualified agent framework could be anyone's, and the samples follow it in the directory, the dependency group, the task queues, the snippet markers and the uv source pin, which now points at the renamed plugin revision. The framework's own agent_framework imports and the agent-framework-openai distribution are Microsoft's names and stay as they are. --- .github/CODEOWNERS | 4 +- .github/workflows/ci.yml | 2 +- README.md | 2 +- .../README.md | 36 +++++++-------- .../__init__.py | 0 .../continue_as_new/README.md | 8 ++-- .../continue_as_new/__init__.py | 0 .../continue_as_new/run_worker.py | 12 ++--- .../continue_as_new/run_workflow.py | 11 +++-- .../continue_as_new/workflow.py | 4 +- .../hello_world/README.md | 12 ++--- .../hello_world/__init__.py | 0 .../hello_world/run_worker.py | 12 ++--- .../hello_world/run_workflow.py | 8 ++-- .../hello_world/workflow.py | 4 +- .../human_in_the_loop/README.md | 6 +-- .../human_in_the_loop/__init__.py | 0 .../human_in_the_loop/run_worker.py | 14 +++--- .../human_in_the_loop/run_workflow.py | 8 ++-- .../human_in_the_loop/workflow.py | 4 +- .../mcp/README.md | 8 ++-- .../mcp/__init__.py | 0 .../mcp/echo_mcp_server.py | 2 +- .../mcp/pyproject.toml | 8 ++-- .../mcp/run_worker.py | 8 ++-- .../mcp/run_workflow.py | 4 +- .../mcp/tests/conftest.py | 4 +- .../mcp/tests/mcp_test.py | 16 +++---- .../mcp/uv.lock | 40 ++++++++--------- .../mcp/workflow.py | 7 ++- .../middleware/README.md | 6 +-- .../middleware/__init__.py | 0 .../middleware/run_worker.py | 12 ++--- .../middleware/run_workflow.py | 6 +-- .../middleware/workflow.py | 6 +-- .../multi_agent/README.md | 8 ++-- .../multi_agent/__init__.py | 0 .../multi_agent/run_worker.py | 12 ++--- .../multi_agent/run_workflow.py | 6 +-- .../multi_agent/workflow.py | 4 +- .../open_telemetry/README.md | 20 ++++----- .../open_telemetry/__init__.py | 0 .../open_telemetry/run_worker.py | 18 +++++--- .../open_telemetry/run_workflow.py | 14 +++--- .../open_telemetry/telemetry.py | 0 .../open_telemetry/workflow.py | 4 +- .../streaming/README.md | 8 ++-- .../streaming/__init__.py | 0 .../streaming/run_worker.py | 12 ++--- .../streaming/run_workflow.py | 11 +++-- .../streaming/workflow.py | 4 +- .../structured_output/README.md | 8 ++-- .../structured_output/__init__.py | 0 .../structured_output/run_worker.py | 14 +++--- .../structured_output/run_workflow.py | 14 +++--- .../structured_output/workflow.py | 4 +- .../tools/README.md | 10 ++--- .../tools/__init__.py | 0 .../tools/run_worker.py | 12 ++--- .../tools/run_workflow.py | 6 +-- .../tools/workflow.py | 10 ++--- pyproject.toml | 24 +++++----- .../__init__.py | 0 .../_fake_chat_client.py | 12 ++--- .../conftest.py | 12 ++--- .../continue_as_new_test.py | 13 +++--- .../hello_world_test.py | 10 ++--- .../helpers.py | 8 ++-- .../human_in_the_loop_test.py | 18 ++++---- .../middleware_test.py | 13 +++--- .../multi_agent_test.py | 10 ++--- .../open_telemetry_test.py | 10 ++--- .../streaming_test.py | 13 +++--- .../structured_output_test.py | 10 ++--- .../tools_test.py | 10 ++--- uv.lock | 44 +++++++++---------- 76 files changed, 364 insertions(+), 316 deletions(-) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/README.md (63%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/__init__.py (100%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/continue_as_new/README.md (76%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/continue_as_new/__init__.py (100%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/continue_as_new/run_worker.py (69%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/continue_as_new/run_workflow.py (75%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/continue_as_new/workflow.py (95%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/hello_world/README.md (50%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/hello_world/__init__.py (100%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/hello_world/run_worker.py (72%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/hello_world/run_workflow.py (62%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/hello_world/workflow.py (80%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/human_in_the_loop/README.md (88%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/human_in_the_loop/__init__.py (100%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/human_in_the_loop/run_worker.py (67%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/human_in_the_loop/run_workflow.py (82%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/human_in_the_loop/workflow.py (96%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/mcp/README.md (76%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/mcp/__init__.py (100%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/mcp/echo_mcp_server.py (84%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/mcp/pyproject.toml (78%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/mcp/run_worker.py (86%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/mcp/run_workflow.py (83%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/mcp/tests/conftest.py (85%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/mcp/tests/mcp_test.py (88%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/mcp/uv.lock (99%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/mcp/workflow.py (85%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/middleware/README.md (81%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/middleware/__init__.py (100%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/middleware/run_worker.py (68%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/middleware/run_workflow.py (70%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/middleware/workflow.py (92%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/multi_agent/README.md (72%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/multi_agent/__init__.py (100%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/multi_agent/run_worker.py (70%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/multi_agent/run_workflow.py (67%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/multi_agent/workflow.py (92%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/open_telemetry/README.md (66%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/open_telemetry/__init__.py (100%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/open_telemetry/run_worker.py (66%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/open_telemetry/run_workflow.py (69%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/open_telemetry/telemetry.py (100%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/open_telemetry/workflow.py (82%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/streaming/README.md (58%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/streaming/__init__.py (100%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/streaming/run_worker.py (69%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/streaming/run_workflow.py (86%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/streaming/workflow.py (90%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/structured_output/README.md (70%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/structured_output/__init__.py (100%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/structured_output/run_worker.py (67%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/structured_output/run_workflow.py (60%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/structured_output/workflow.py (89%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/tools/README.md (64%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/tools/__init__.py (100%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/tools/run_worker.py (70%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/tools/run_workflow.py (75%) rename {agent_framework_plugin => microsoft_agent_framework_plugin}/tools/workflow.py (82%) rename tests/{agent_framework_plugin => microsoft_agent_framework_plugin}/__init__.py (100%) rename tests/{agent_framework_plugin => microsoft_agent_framework_plugin}/_fake_chat_client.py (69%) rename tests/{agent_framework_plugin => microsoft_agent_framework_plugin}/conftest.py (73%) rename tests/{agent_framework_plugin => microsoft_agent_framework_plugin}/continue_as_new_test.py (89%) rename tests/{agent_framework_plugin => microsoft_agent_framework_plugin}/hello_world_test.py (74%) rename tests/{agent_framework_plugin => microsoft_agent_framework_plugin}/helpers.py (89%) rename tests/{agent_framework_plugin => microsoft_agent_framework_plugin}/human_in_the_loop_test.py (89%) rename tests/{agent_framework_plugin => microsoft_agent_framework_plugin}/middleware_test.py (82%) rename tests/{agent_framework_plugin => microsoft_agent_framework_plugin}/multi_agent_test.py (86%) rename tests/{agent_framework_plugin => microsoft_agent_framework_plugin}/open_telemetry_test.py (85%) rename tests/{agent_framework_plugin => microsoft_agent_framework_plugin}/streaming_test.py (84%) rename tests/{agent_framework_plugin => microsoft_agent_framework_plugin}/structured_output_test.py (77%) rename tests/{agent_framework_plugin => microsoft_agent_framework_plugin}/tools_test.py (82%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index aa30595d..2ffb06dc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -18,7 +18,6 @@ # The AI SDK team owns the AI integration samples and their tests. We add # @temporalio/sdk too, so the SDK team can continue to manage repo-wide concerns. -/agent_framework_plugin/ @temporalio/sdk @temporalio/ai-sdk /claude_agent_sdk_plugin/ @temporalio/sdk @temporalio/ai-sdk /deepagents_plugin/ @temporalio/sdk @temporalio/ai-sdk /google_adk_agents/ @temporalio/sdk @temporalio/ai-sdk @@ -27,9 +26,9 @@ /langgraph_plugin/ @temporalio/sdk @temporalio/ai-sdk /langsmith_tracing/ @temporalio/sdk @temporalio/ai-sdk /litellm_activity/ @temporalio/sdk @temporalio/ai-sdk +/microsoft_agent_framework_plugin/ @temporalio/sdk @temporalio/ai-sdk /openai_agents/ @temporalio/sdk @temporalio/ai-sdk /strands_plugin/ @temporalio/sdk @temporalio/ai-sdk -/tests/agent_framework_plugin/ @temporalio/sdk @temporalio/ai-sdk /tests/claude_agent_sdk_plugin/ @temporalio/sdk @temporalio/ai-sdk /tests/deepagents_plugin/ @temporalio/sdk @temporalio/ai-sdk /tests/google_adk_agents/ @temporalio/sdk @temporalio/ai-sdk @@ -38,5 +37,6 @@ /tests/langgraph_plugin/ @temporalio/sdk @temporalio/ai-sdk /tests/langsmith_tracing/ @temporalio/sdk @temporalio/ai-sdk /tests/litellm_activity/ @temporalio/sdk @temporalio/ai-sdk +/tests/microsoft_agent_framework_plugin/ @temporalio/sdk @temporalio/ai-sdk /tests/openai_agents/ @temporalio/sdk @temporalio/ai-sdk /tests/strands_plugin/ @temporalio/sdk @temporalio/ai-sdk diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a83ce395..ba399d9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,9 +33,9 @@ jobs: projects_json="$({ for project in \ - agent_framework_plugin/mcp \ bedrock_agentcore/strands_agent \ lambda_worker \ + microsoft_agent_framework_plugin/mcp \ openai_agents/mcp_v2 do if grep -q "^${project}/" <<< "$changed_files"; then diff --git a/README.md b/README.md index 3f7fe1ed..5e4e36cc 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,6 @@ Some examples require extra dependencies. See each sample's directory for specif * [hello update](hello/hello_update.py) - Send a request to and a response from a client to a workflow execution. * [activity_worker](activity_worker) - Use Python activities from a workflow in another language. -* [agent_framework_plugin](agent_framework_plugin) - Run Microsoft Agent Framework agents as durable Temporal Workflows (model calls, tools, MCP, approvals, streaming, Continue-As-New). * [batch_sliding_window](batch_sliding_window) - Batch processing with a sliding window of child workflows. * [bedrock](bedrock) - Orchestrate a chatbot with Amazon Bedrock. * [bedrock_agentcore/strands_agent](bedrock_agentcore/strands_agent) - Run a AWS Strands Agent with Temporal Plugin on AgentCore Worker. @@ -91,6 +90,7 @@ Some examples require extra dependencies. See each sample's directory for specif * [message_passing/introduction](message_passing/introduction/) - Introduction to queries, signals, and updates. * [message_passing/safe_message_handlers](message_passing/safe_message_handlers/) - Safely handling updates and signals. * [message_passing/update_with_start/lazy_initialization](message_passing/update_with_start/lazy_initialization/) - Use update-with-start to update a Shopping Cart, starting it if it does not exist. +* [microsoft_agent_framework_plugin](microsoft_agent_framework_plugin) - Run Microsoft Agent Framework agents as durable Temporal Workflows (model calls, tools, MCP, approvals, streaming, Continue-As-New). * [nexus_cancel](nexus_cancel) - Fan out concurrent Nexus operations, take the first result, and cancel the rest. * [nexus_messaging](nexus_messaging): Demonstrates how to send signal, update and query messages through Nexus. This contains two samples, one sending messages to an existing workflow and a second that creates a workflow through Nexus diff --git a/agent_framework_plugin/README.md b/microsoft_agent_framework_plugin/README.md similarity index 63% rename from agent_framework_plugin/README.md rename to microsoft_agent_framework_plugin/README.md index 3adf4416..5629642d 100644 --- a/agent_framework_plugin/README.md +++ b/microsoft_agent_framework_plugin/README.md @@ -1,10 +1,10 @@ # Microsoft Agent Framework samples -These samples show the [Temporal plugin for Microsoft Agent Framework](https://github.com/temporalio/ai-integrations/tree/main/python/agent_framework), which runs an Agent Framework `Agent` inside a Temporal Workflow. You write the agent the way Agent Framework documents it and swap the chat client for `TemporalChatClient`. Each model call then runs as the `agent_framework.invoke_model` Activity (`agent_framework.invoke_model_streaming` when you stream), with its own timeout and retries. The agent's loop, tools, middleware and approvals run in the Workflow, where Temporal records every step and replays it after a crash or a Worker restart. The docs page is [Microsoft Agent Framework](https://docs.temporal.io/develop/python/integrations/agent-framework). +These samples show the [Temporal plugin for Microsoft Agent Framework](https://github.com/temporalio/ai-integrations/tree/main/python/microsoft_agent_framework), which runs an Agent Framework `Agent` inside a Temporal Workflow. You write the agent the way Agent Framework documents it and swap the chat client for `TemporalChatClient`. Each model call then runs as the `microsoft_agent_framework.invoke_model` Activity (`microsoft_agent_framework.invoke_model_streaming` when you stream), with its own timeout and retries. The agent's loop, tools, middleware and approvals run in the Workflow, where Temporal records every step and replays it after a crash or a Worker restart. The docs page is [Microsoft Agent Framework](https://docs.temporal.io/develop/python/integrations/microsoft-agent-framework). -> **Pre-release.** The `temporalio-agent-framework` package and its interfaces may change before General Availability. +> **Pre-release.** The `temporalio-microsoft-agent-framework` package and its interfaces may change before General Availability. -`AgentFrameworkPlugin` goes on the Client or on the Worker, not both. These samples put it on `Client.connect(...)`; the Worker built from that Client inherits it. +`MicrosoftAgentFrameworkPlugin` goes on the Client or on the Worker, not both. These samples put it on `Client.connect(...)`; the Worker built from that Client inherits it. ## Samples @@ -19,19 +19,19 @@ These samples show the [Temporal plugin for Microsoft Agent Framework](https://g | [structured_output](structured_output) | `response_format` with a Pydantic model; the Workflow returns the model through the plugin's data converter. | | [streaming](streaming) | `streaming_topic` publishes each update to a Workflow Stream; the starter prints them as they arrive. | | [continue_as_new](continue_as_new) | A chat driven by Updates that hands `session.to_dict()` to a fresh run when Temporal suggests Continue-As-New. | -| [open_telemetry](open_telemetry) | `OpenTelemetryPlugin` and `AgentFrameworkPlugin` on one Client: one trace from the starter through the Workflow to the model Activity. | +| [open_telemetry](open_telemetry) | `OpenTelemetryPlugin` and `MicrosoftAgentFrameworkPlugin` on one Client: one trace from the starter through the Workflow to the model Activity. | ## Prerequisites -1. Python 3.11 or later. The plugin declares `requires-python = ">=3.11"`, as Temporal Python SDK 1.36 does, so on 3.10 the `agent-framework` dependency group resolves to nothing and the tests are not collected. +1. Python 3.11 or later. The plugin declares `requires-python = ">=3.11"`, as Temporal Python SDK 1.36 does, so on 3.10 the `microsoft-agent-framework` dependency group resolves to nothing and the tests are not collected. 2. Install the dependencies from the repository root: ```bash - uv sync --group agent-framework + uv sync --group microsoft-agent-framework ``` - This installs `temporalio-agent-framework`, `agent-framework-openai` and Temporal Python SDK 1.36.0 or later. + This installs `temporalio-microsoft-agent-framework`, `agent-framework-openai` and Temporal Python SDK 1.36.0 or later. 3. An OpenAI API key. Each Worker reads it when it builds the `OpenAIChatClient`; the Workflow only carries the model name `gpt-4o-mini`: @@ -51,26 +51,26 @@ Each sample has two scripts. Start the Worker first, then the starter in a secon ```bash # Terminal 1: start the Worker -uv run agent_framework_plugin//run_worker.py +uv run microsoft_agent_framework_plugin//run_worker.py # Terminal 2: start the Workflow -uv run agent_framework_plugin//run_workflow.py +uv run microsoft_agent_framework_plugin//run_workflow.py ``` For example, for the tools sample: ```bash # Terminal 1 -uv run agent_framework_plugin/tools/run_worker.py +uv run microsoft_agent_framework_plugin/tools/run_worker.py # Terminal 2 -uv run agent_framework_plugin/tools/run_workflow.py +uv run microsoft_agent_framework_plugin/tools/run_workflow.py ``` The `mcp` sample is its own uv project: the plugin's MCP support needs MCP Python SDK 2, and other samples in this repository still pin SDK 1, so they cannot share one lock file. Run it from its directory: ```bash -cd agent_framework_plugin/mcp +cd microsoft_agent_framework_plugin/mcp uv sync # Terminal 1 @@ -82,7 +82,7 @@ uv run run_workflow.py ## Key features -- One Activity per model round trip, `agent_framework.invoke_model`, with a 60-second start-to-close timeout in these samples and the plugin's default 10-second heartbeat timeout. Temporal owns retries, so the Workers build the OpenAI client with `AsyncOpenAI(max_retries=0)`. +- One Activity per model round trip, `microsoft_agent_framework.invoke_model`, with a 60-second start-to-close timeout in these samples and the plugin's default 10-second heartbeat timeout. Temporal owns retries, so the Workers build the OpenAI client with `AsyncOpenAI(max_retries=0)`. - Tools run where you put them: a plain `@tool` runs in the Workflow, `activity_as_tool` runs an Activity with its own `start_to_close_timeout`. - MCP servers are registered on the Worker. `TemporalMCPToolProvider` lists and calls their tools through the `temporalio.mcp..list-tools` and `temporalio.mcp..call-tool` Activities. - Approvals are Agent Framework's own `approval_mode`; the wait is a Workflow Update with a validator. @@ -96,13 +96,13 @@ uv run run_workflow.py The tests run every sample against the plugin's scripted `FakeChatClient` and a local dev server, with no API key: ```bash -uv run poe test tests/agent_framework_plugin -uv run poe test tests/agent_framework_plugin --workflow-environment time-skipping -cd agent_framework_plugin/mcp && uv run poe test +uv run poe test tests/microsoft_agent_framework_plugin +uv run poe test tests/microsoft_agent_framework_plugin --workflow-environment time-skipping +cd microsoft_agent_framework_plugin/mcp && uv run poe test ``` ## Related -- [Temporal plugin for Microsoft Agent Framework](https://github.com/temporalio/ai-integrations/tree/main/python/agent_framework) +- [Temporal plugin for Microsoft Agent Framework](https://github.com/temporalio/ai-integrations/tree/main/python/microsoft_agent_framework) - [Microsoft Agent Framework](https://github.com/microsoft/agent-framework) -- [Microsoft Agent Framework docs page](https://docs.temporal.io/develop/python/integrations/agent-framework) +- [Microsoft Agent Framework docs page](https://docs.temporal.io/develop/python/integrations/microsoft-agent-framework) diff --git a/agent_framework_plugin/__init__.py b/microsoft_agent_framework_plugin/__init__.py similarity index 100% rename from agent_framework_plugin/__init__.py rename to microsoft_agent_framework_plugin/__init__.py diff --git a/agent_framework_plugin/continue_as_new/README.md b/microsoft_agent_framework_plugin/continue_as_new/README.md similarity index 76% rename from agent_framework_plugin/continue_as_new/README.md rename to microsoft_agent_framework_plugin/continue_as_new/README.md index 16228b0c..d0640311 100644 --- a/agent_framework_plugin/continue_as_new/README.md +++ b/microsoft_agent_framework_plugin/continue_as_new/README.md @@ -14,18 +14,18 @@ What crosses the boundary: the serialized session, which holds the transcript, a ```bash # Terminal 1 -uv run agent_framework_plugin/continue_as_new/run_worker.py +uv run microsoft_agent_framework_plugin/continue_as_new/run_worker.py # Terminal 2 -uv run agent_framework_plugin/continue_as_new/run_workflow.py +uv run microsoft_agent_framework_plugin/continue_as_new/run_workflow.py ``` -The starter sends two turns, prints each reply, prints the number of messages in the session and ends the chat. Two turns do not reach the default threshold, so the Workflow `agent-framework-chat` completes in one run; the test lowers the threshold to see the handoff. In a chat application a UI would drive the Updates and the Workflow would run on, continuing as new whenever history gets large. +The starter sends two turns, prints each reply, prints the number of messages in the session and ends the chat. Two turns do not reach the default threshold, so the Workflow `microsoft-agent-framework-chat` completes in one run; the test lowers the threshold to see the handoff. In a chat application a UI would drive the Updates and the Workflow would run on, continuing as new whenever history gets large. ## Files | File | Description | |------|-------------| | `workflow.py` | `ChatInput` and `ChatWorkflow` with the Update, Signal and Query | -| `run_worker.py` | Registers the plugin, starts the Worker on `agent-framework-chat` | +| `run_worker.py` | Registers the plugin, starts the Worker on `microsoft-agent-framework-chat` | | `run_workflow.py` | Starts the chat, sends two turns, ends it | diff --git a/agent_framework_plugin/continue_as_new/__init__.py b/microsoft_agent_framework_plugin/continue_as_new/__init__.py similarity index 100% rename from agent_framework_plugin/continue_as_new/__init__.py rename to microsoft_agent_framework_plugin/continue_as_new/__init__.py diff --git a/agent_framework_plugin/continue_as_new/run_worker.py b/microsoft_agent_framework_plugin/continue_as_new/run_worker.py similarity index 69% rename from agent_framework_plugin/continue_as_new/run_worker.py rename to microsoft_agent_framework_plugin/continue_as_new/run_worker.py index f9e98708..c8f0e4b5 100644 --- a/agent_framework_plugin/continue_as_new/run_worker.py +++ b/microsoft_agent_framework_plugin/continue_as_new/run_worker.py @@ -1,16 +1,16 @@ """Worker for the chat continue-as-new sample.""" -# @@@SNIPSTART python-agent-framework-continue-as-new-worker +# @@@SNIPSTART python-microsoft-agent-framework-continue-as-new-worker import asyncio import os from agent_framework.openai import OpenAIChatClient from openai import AsyncOpenAI -from temporalio.agent_framework import AgentFrameworkPlugin from temporalio.client import Client +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin from temporalio.worker import Worker -from agent_framework_plugin.continue_as_new.workflow import ChatWorkflow +from microsoft_agent_framework_plugin.continue_as_new.workflow import ChatWorkflow def openai_chat_client() -> OpenAIChatClient: @@ -23,7 +23,9 @@ async def main() -> None: if not os.environ.get("OPENAI_API_KEY"): raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") - plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + plugin = MicrosoftAgentFrameworkPlugin( + chat_clients={"gpt-4o-mini": openai_chat_client} + ) client = await Client.connect( os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), plugins=[plugin], @@ -31,7 +33,7 @@ async def main() -> None: worker = Worker( client, - task_queue="agent-framework-chat", + task_queue="microsoft-agent-framework-chat", workflows=[ChatWorkflow], ) print("Worker started. Ctrl+C to exit.") diff --git a/agent_framework_plugin/continue_as_new/run_workflow.py b/microsoft_agent_framework_plugin/continue_as_new/run_workflow.py similarity index 75% rename from agent_framework_plugin/continue_as_new/run_workflow.py rename to microsoft_agent_framework_plugin/continue_as_new/run_workflow.py index 7952c024..cd29e03f 100644 --- a/agent_framework_plugin/continue_as_new/run_workflow.py +++ b/microsoft_agent_framework_plugin/continue_as_new/run_workflow.py @@ -1,12 +1,15 @@ """Start the chat workflow, send two turns, then end it.""" -# @@@SNIPSTART python-agent-framework-continue-as-new-run-workflow +# @@@SNIPSTART python-microsoft-agent-framework-continue-as-new-run-workflow import asyncio import os from temporalio.client import Client -from agent_framework_plugin.continue_as_new.workflow import ChatInput, ChatWorkflow +from microsoft_agent_framework_plugin.continue_as_new.workflow import ( + ChatInput, + ChatWorkflow, +) async def main() -> None: @@ -15,8 +18,8 @@ async def main() -> None: handle = await client.start_workflow( ChatWorkflow.run, ChatInput(), - id="agent-framework-chat", - task_queue="agent-framework-chat", + id="microsoft-agent-framework-chat", + task_queue="microsoft-agent-framework-chat", ) for prompt in [ diff --git a/agent_framework_plugin/continue_as_new/workflow.py b/microsoft_agent_framework_plugin/continue_as_new/workflow.py similarity index 95% rename from agent_framework_plugin/continue_as_new/workflow.py rename to microsoft_agent_framework_plugin/continue_as_new/workflow.py index 070abc8e..d486a421 100644 --- a/agent_framework_plugin/continue_as_new/workflow.py +++ b/microsoft_agent_framework_plugin/continue_as_new/workflow.py @@ -7,7 +7,7 @@ boundary; the ``Agent`` object does not cross and is built again in the new run. """ -# @@@SNIPSTART python-agent-framework-continue-as-new-workflow +# @@@SNIPSTART python-microsoft-agent-framework-continue-as-new-workflow import asyncio from dataclasses import dataclass from datetime import timedelta @@ -15,7 +15,7 @@ from agent_framework import Agent, AgentSession, InMemoryHistoryProvider from temporalio import workflow -from temporalio.agent_framework import TemporalChatClient +from temporalio.microsoft_agent_framework import TemporalChatClient @dataclass diff --git a/agent_framework_plugin/hello_world/README.md b/microsoft_agent_framework_plugin/hello_world/README.md similarity index 50% rename from agent_framework_plugin/hello_world/README.md rename to microsoft_agent_framework_plugin/hello_world/README.md index 94420cc7..0f6fd090 100644 --- a/agent_framework_plugin/hello_world/README.md +++ b/microsoft_agent_framework_plugin/hello_world/README.md @@ -1,26 +1,26 @@ # Hello world -The smallest Agent Framework + Temporal sample: one `Agent` over `TemporalChatClient`, invoked once. The model call runs as the `agent_framework.invoke_model` Activity with a 60-second start-to-close timeout, so Temporal retries it and it survives a Worker restart. +The smallest Agent Framework + Temporal sample: one `Agent` over `TemporalChatClient`, invoked once. The model call runs as the `microsoft_agent_framework.invoke_model` Activity with a 60-second start-to-close timeout, so Temporal retries it and it survives a Worker restart. ## What this sample demonstrates -- `AgentFrameworkPlugin(chat_clients=...)` on the Client, with the `OpenAIChatClient` built on the Worker from `OPENAI_API_KEY` +- `MicrosoftAgentFrameworkPlugin(chat_clients=...)` on the Client, with the `OpenAIChatClient` built on the Worker from `OPENAI_API_KEY` - `Agent(client=TemporalChatClient("gpt-4o-mini", start_to_close_timeout=...))` in a `@workflow.defn` - `await agent.run(prompt)` and `response.text`, unchanged from Agent Framework ## Running the sample -Prerequisites: `uv sync --group agent-framework`, `OPENAI_API_KEY` in the Worker's environment, and a Temporal dev server (`temporal server start-dev`). Run both commands from the repository root. +Prerequisites: `uv sync --group microsoft-agent-framework`, `OPENAI_API_KEY` in the Worker's environment, and a Temporal dev server (`temporal server start-dev`). Run both commands from the repository root. ```bash # Terminal 1 -uv run agent_framework_plugin/hello_world/run_worker.py +uv run microsoft_agent_framework_plugin/hello_world/run_worker.py # Terminal 2 -uv run agent_framework_plugin/hello_world/run_workflow.py +uv run microsoft_agent_framework_plugin/hello_world/run_workflow.py ``` -The starter prints `Result:` and the haiku. In the Temporal UI the Workflow `agent-framework-hello-world` shows one `agent_framework.invoke_model` Activity with the summary `gpt-4o-mini`. +The starter prints `Result:` and the haiku. In the Temporal UI the Workflow `microsoft-agent-framework-hello-world` shows one `microsoft_agent_framework.invoke_model` Activity with the summary `gpt-4o-mini`. ## Files diff --git a/agent_framework_plugin/hello_world/__init__.py b/microsoft_agent_framework_plugin/hello_world/__init__.py similarity index 100% rename from agent_framework_plugin/hello_world/__init__.py rename to microsoft_agent_framework_plugin/hello_world/__init__.py diff --git a/agent_framework_plugin/hello_world/run_worker.py b/microsoft_agent_framework_plugin/hello_world/run_worker.py similarity index 72% rename from agent_framework_plugin/hello_world/run_worker.py rename to microsoft_agent_framework_plugin/hello_world/run_worker.py index 7f42c5f1..e7369538 100644 --- a/agent_framework_plugin/hello_world/run_worker.py +++ b/microsoft_agent_framework_plugin/hello_world/run_worker.py @@ -1,16 +1,16 @@ """Worker for the hello world sample.""" -# @@@SNIPSTART python-agent-framework-hello-world-worker +# @@@SNIPSTART python-microsoft-agent-framework-hello-world-worker import asyncio import os from agent_framework.openai import OpenAIChatClient from openai import AsyncOpenAI -from temporalio.agent_framework import AgentFrameworkPlugin from temporalio.client import Client +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin from temporalio.worker import Worker -from agent_framework_plugin.hello_world.workflow import HelloWorldWorkflow +from microsoft_agent_framework_plugin.hello_world.workflow import HelloWorldWorkflow def openai_chat_client() -> OpenAIChatClient: @@ -25,7 +25,9 @@ async def main() -> None: if not os.environ.get("OPENAI_API_KEY"): raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") - plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + plugin = MicrosoftAgentFrameworkPlugin( + chat_clients={"gpt-4o-mini": openai_chat_client} + ) client = await Client.connect( os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), plugins=[plugin], @@ -33,7 +35,7 @@ async def main() -> None: worker = Worker( client, - task_queue="agent-framework-hello-world", + task_queue="microsoft-agent-framework-hello-world", workflows=[HelloWorldWorkflow], ) print("Worker started. Ctrl+C to exit.") diff --git a/agent_framework_plugin/hello_world/run_workflow.py b/microsoft_agent_framework_plugin/hello_world/run_workflow.py similarity index 62% rename from agent_framework_plugin/hello_world/run_workflow.py rename to microsoft_agent_framework_plugin/hello_world/run_workflow.py index 1b83ea0b..6308b85b 100644 --- a/agent_framework_plugin/hello_world/run_workflow.py +++ b/microsoft_agent_framework_plugin/hello_world/run_workflow.py @@ -1,12 +1,12 @@ """Start the hello world workflow.""" -# @@@SNIPSTART python-agent-framework-hello-world-run-workflow +# @@@SNIPSTART python-microsoft-agent-framework-hello-world-run-workflow import asyncio import os from temporalio.client import Client -from agent_framework_plugin.hello_world.workflow import HelloWorldWorkflow +from microsoft_agent_framework_plugin.hello_world.workflow import HelloWorldWorkflow async def main() -> None: @@ -15,8 +15,8 @@ async def main() -> None: result = await client.execute_workflow( HelloWorldWorkflow.run, "Write a haiku about durable execution.", - id="agent-framework-hello-world", - task_queue="agent-framework-hello-world", + id="microsoft-agent-framework-hello-world", + task_queue="microsoft-agent-framework-hello-world", ) print(f"Result: {result}") diff --git a/agent_framework_plugin/hello_world/workflow.py b/microsoft_agent_framework_plugin/hello_world/workflow.py similarity index 80% rename from agent_framework_plugin/hello_world/workflow.py rename to microsoft_agent_framework_plugin/hello_world/workflow.py index beedd967..58646448 100644 --- a/agent_framework_plugin/hello_world/workflow.py +++ b/microsoft_agent_framework_plugin/hello_world/workflow.py @@ -1,11 +1,11 @@ """One Agent Framework agent, one prompt, one model Activity.""" -# @@@SNIPSTART python-agent-framework-hello-world-workflow +# @@@SNIPSTART python-microsoft-agent-framework-hello-world-workflow from datetime import timedelta from agent_framework import Agent from temporalio import workflow -from temporalio.agent_framework import TemporalChatClient +from temporalio.microsoft_agent_framework import TemporalChatClient @workflow.defn diff --git a/agent_framework_plugin/human_in_the_loop/README.md b/microsoft_agent_framework_plugin/human_in_the_loop/README.md similarity index 88% rename from agent_framework_plugin/human_in_the_loop/README.md rename to microsoft_agent_framework_plugin/human_in_the_loop/README.md index c541938f..008e0588 100644 --- a/agent_framework_plugin/human_in_the_loop/README.md +++ b/microsoft_agent_framework_plugin/human_in_the_loop/README.md @@ -13,13 +13,13 @@ A tool that runs only after a person approves. `delete_file` carries `approval_m ```bash # Terminal 1 -uv run agent_framework_plugin/human_in_the_loop/run_worker.py +uv run microsoft_agent_framework_plugin/human_in_the_loop/run_worker.py # Terminal 2 -uv run agent_framework_plugin/human_in_the_loop/run_workflow.py +uv run microsoft_agent_framework_plugin/human_in_the_loop/run_workflow.py ``` -The starter polls the Query, prints `Approval requested: delete_file({'path': '/tmp/report.txt'})`, sends `Decision(request_id=..., approved=True)` with the id the Query returned, prints `Decision: approved` and then the agent's report. Set `approved=False` in `run_workflow.py` to see the rejection path. The Workflow `agent-framework-human-in-the-loop` shows two `agent_framework.invoke_model` Activities: the request and the report. +The starter polls the Query, prints `Approval requested: delete_file({'path': '/tmp/report.txt'})`, sends `Decision(request_id=..., approved=True)` with the id the Query returned, prints `Decision: approved` and then the agent's report. Set `approved=False` in `run_workflow.py` to see the rejection path. The Workflow `microsoft-agent-framework-human-in-the-loop` shows two `microsoft_agent_framework.invoke_model` Activities: the request and the report. ## Files diff --git a/agent_framework_plugin/human_in_the_loop/__init__.py b/microsoft_agent_framework_plugin/human_in_the_loop/__init__.py similarity index 100% rename from agent_framework_plugin/human_in_the_loop/__init__.py rename to microsoft_agent_framework_plugin/human_in_the_loop/__init__.py diff --git a/agent_framework_plugin/human_in_the_loop/run_worker.py b/microsoft_agent_framework_plugin/human_in_the_loop/run_worker.py similarity index 67% rename from agent_framework_plugin/human_in_the_loop/run_worker.py rename to microsoft_agent_framework_plugin/human_in_the_loop/run_worker.py index 1b0f6897..09f3d032 100644 --- a/agent_framework_plugin/human_in_the_loop/run_worker.py +++ b/microsoft_agent_framework_plugin/human_in_the_loop/run_worker.py @@ -1,16 +1,18 @@ """Worker for the human in the loop sample.""" -# @@@SNIPSTART python-agent-framework-human-in-the-loop-worker +# @@@SNIPSTART python-microsoft-agent-framework-human-in-the-loop-worker import asyncio import os from agent_framework.openai import OpenAIChatClient from openai import AsyncOpenAI -from temporalio.agent_framework import AgentFrameworkPlugin from temporalio.client import Client +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin from temporalio.worker import Worker -from agent_framework_plugin.human_in_the_loop.workflow import HumanInTheLoopWorkflow +from microsoft_agent_framework_plugin.human_in_the_loop.workflow import ( + HumanInTheLoopWorkflow, +) def openai_chat_client() -> OpenAIChatClient: @@ -23,7 +25,9 @@ async def main() -> None: if not os.environ.get("OPENAI_API_KEY"): raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") - plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + plugin = MicrosoftAgentFrameworkPlugin( + chat_clients={"gpt-4o-mini": openai_chat_client} + ) client = await Client.connect( os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), plugins=[plugin], @@ -31,7 +35,7 @@ async def main() -> None: worker = Worker( client, - task_queue="agent-framework-human-in-the-loop", + task_queue="microsoft-agent-framework-human-in-the-loop", workflows=[HumanInTheLoopWorkflow], ) print("Worker started. Ctrl+C to exit.") diff --git a/agent_framework_plugin/human_in_the_loop/run_workflow.py b/microsoft_agent_framework_plugin/human_in_the_loop/run_workflow.py similarity index 82% rename from agent_framework_plugin/human_in_the_loop/run_workflow.py rename to microsoft_agent_framework_plugin/human_in_the_loop/run_workflow.py index 1d742fd4..3a9f064b 100644 --- a/agent_framework_plugin/human_in_the_loop/run_workflow.py +++ b/microsoft_agent_framework_plugin/human_in_the_loop/run_workflow.py @@ -1,12 +1,12 @@ """Start the human in the loop workflow, wait for the request, approve it.""" -# @@@SNIPSTART python-agent-framework-human-in-the-loop-run-workflow +# @@@SNIPSTART python-microsoft-agent-framework-human-in-the-loop-run-workflow import asyncio import os from temporalio.client import Client -from agent_framework_plugin.human_in_the_loop.workflow import ( +from microsoft_agent_framework_plugin.human_in_the_loop.workflow import ( ApprovalRequest, Decision, HumanInTheLoopWorkflow, @@ -19,8 +19,8 @@ async def main() -> None: handle = await client.start_workflow( HumanInTheLoopWorkflow.run, "Please delete /tmp/report.txt", - id="agent-framework-human-in-the-loop", - task_queue="agent-framework-human-in-the-loop", + id="microsoft-agent-framework-human-in-the-loop", + task_queue="microsoft-agent-framework-human-in-the-loop", ) # Poll the Query until the agent asks for approval. diff --git a/agent_framework_plugin/human_in_the_loop/workflow.py b/microsoft_agent_framework_plugin/human_in_the_loop/workflow.py similarity index 96% rename from agent_framework_plugin/human_in_the_loop/workflow.py rename to microsoft_agent_framework_plugin/human_in_the_loop/workflow.py index 65de5f47..fdeea076 100644 --- a/agent_framework_plugin/human_in_the_loop/workflow.py +++ b/microsoft_agent_framework_plugin/human_in_the_loop/workflow.py @@ -8,15 +8,15 @@ call runs the tool. """ -# @@@SNIPSTART python-agent-framework-human-in-the-loop-workflow +# @@@SNIPSTART python-microsoft-agent-framework-human-in-the-loop-workflow from dataclasses import dataclass, field from datetime import timedelta from typing import Any from agent_framework import Agent, AgentResponse, AgentSession, Content, tool from temporalio import workflow -from temporalio.agent_framework import TemporalChatClient from temporalio.exceptions import ApplicationError +from temporalio.microsoft_agent_framework import TemporalChatClient @tool(approval_mode="always_require") diff --git a/agent_framework_plugin/mcp/README.md b/microsoft_agent_framework_plugin/mcp/README.md similarity index 76% rename from agent_framework_plugin/mcp/README.md rename to microsoft_agent_framework_plugin/mcp/README.md index 7e844d82..c00570dc 100644 --- a/agent_framework_plugin/mcp/README.md +++ b/microsoft_agent_framework_plugin/mcp/README.md @@ -1,6 +1,6 @@ # MCP -Give the agent the tools of an MCP server. `AgentFrameworkPlugin(mcp_clients={"echo": ...})` registers the server on the Worker. `TemporalMCPToolProvider("echo")` in `Agent(context_providers=[...])` lists the server's tools before each run and turns each call into an Activity: +Give the agent the tools of an MCP server. `MicrosoftAgentFrameworkPlugin(mcp_clients={"echo": ...})` registers the server on the Worker. `TemporalMCPToolProvider("echo")` in `Agent(context_providers=[...])` lists the server's tools before each run and turns each call into an Activity: | Activity | When it runs | |----------|--------------| @@ -13,7 +13,7 @@ This sample is its own uv project. The plugin's MCP support needs MCP Python SDK ## What this sample demonstrates -- `AgentFrameworkPlugin(mcp_clients=...)` with a factory that returns an `mcp.Client` +- `MicrosoftAgentFrameworkPlugin(mcp_clients=...)` with a factory that returns an `mcp.Client` - `TemporalMCPToolProvider` as a context provider, with a 30-second start-to-close timeout for its list and call Activities - The model sees the server's tool schema; the server's connection never leaves the Worker @@ -22,7 +22,7 @@ This sample is its own uv project. The plugin's MCP support needs MCP Python SDK Prerequisites: Python 3.11 or later, `OPENAI_API_KEY` in the Worker's environment, and a Temporal dev server (`temporal server start-dev`). Change to this directory first: ```bash -cd agent_framework_plugin/mcp +cd microsoft_agent_framework_plugin/mcp uv sync ``` @@ -34,7 +34,7 @@ uv run run_worker.py uv run run_workflow.py ``` -The Worker spawns the server itself. The starter prints the agent's answer, and the Workflow `agent-framework-mcp` shows one `temporalio.mcp.echo.list-tools`, two `agent_framework.invoke_model` and one `temporalio.mcp.echo.call-tool` Activity. +The Worker spawns the server itself. The starter prints the agent's answer, and the Workflow `microsoft-agent-framework-mcp` shows one `temporalio.mcp.echo.list-tools`, two `microsoft_agent_framework.invoke_model` and one `temporalio.mcp.echo.call-tool` Activity. ## Tests diff --git a/agent_framework_plugin/mcp/__init__.py b/microsoft_agent_framework_plugin/mcp/__init__.py similarity index 100% rename from agent_framework_plugin/mcp/__init__.py rename to microsoft_agent_framework_plugin/mcp/__init__.py diff --git a/agent_framework_plugin/mcp/echo_mcp_server.py b/microsoft_agent_framework_plugin/mcp/echo_mcp_server.py similarity index 84% rename from agent_framework_plugin/mcp/echo_mcp_server.py rename to microsoft_agent_framework_plugin/mcp/echo_mcp_server.py index afdcbbe6..091da3be 100644 --- a/agent_framework_plugin/mcp/echo_mcp_server.py +++ b/microsoft_agent_framework_plugin/mcp/echo_mcp_server.py @@ -1,6 +1,6 @@ """A one-tool MCP server over stdio. The Worker starts it as a subprocess.""" -# @@@SNIPSTART python-agent-framework-mcp-server +# @@@SNIPSTART python-microsoft-agent-framework-mcp-server from mcp.server.mcpserver import MCPServer server = MCPServer("echo") diff --git a/agent_framework_plugin/mcp/pyproject.toml b/microsoft_agent_framework_plugin/mcp/pyproject.toml similarity index 78% rename from agent_framework_plugin/mcp/pyproject.toml rename to microsoft_agent_framework_plugin/mcp/pyproject.toml index e5d0d07b..c7f26093 100644 --- a/agent_framework_plugin/mcp/pyproject.toml +++ b/microsoft_agent_framework_plugin/mcp/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "temporalio-samples-agent-framework-mcp" +name = "temporalio-samples-microsoft-agent-framework-mcp" version = "0.1.0" description = "Temporal.io Python SDK sample: Microsoft Agent Framework with an MCP server" requires-python = ">=3.10" @@ -7,7 +7,7 @@ requires-python = ">=3.10" # these resolve to nothing and the tests are not collected, so the shared # standalone CI matrix stays green. dependencies = [ - "temporalio-agent-framework[mcp] ; python_version >= '3.11'", + "temporalio-microsoft-agent-framework[mcp] ; python_version >= '3.11'", "agent-framework-openai>=1.17,<2 ; python_version >= '3.11'", ] @@ -21,8 +21,8 @@ dev = [ ] [tool.uv.sources] -# Temporary while the plugin is unreleased: remove once temporalio-agent-framework is on PyPI. -temporalio-agent-framework = { git = "https://github.com/temporalio/ai-integrations", subdirectory = "python/agent_framework", branch = "dabh/agent-framework-python" } +# Temporary while the plugin is unreleased: remove once temporalio-microsoft-agent-framework is on PyPI. +temporalio-microsoft-agent-framework = { git = "https://github.com/temporalio/ai-integrations", subdirectory = "python/microsoft_agent_framework", branch = "dabh/agent-framework-python" } [tool.poe.tasks] format = [ diff --git a/agent_framework_plugin/mcp/run_worker.py b/microsoft_agent_framework_plugin/mcp/run_worker.py similarity index 86% rename from agent_framework_plugin/mcp/run_worker.py rename to microsoft_agent_framework_plugin/mcp/run_worker.py index 78fa9bd0..2cfc6f4f 100644 --- a/agent_framework_plugin/mcp/run_worker.py +++ b/microsoft_agent_framework_plugin/mcp/run_worker.py @@ -1,6 +1,6 @@ """Worker for the MCP sample. It starts ``echo_mcp_server.py`` on demand.""" -# @@@SNIPSTART python-agent-framework-mcp-worker +# @@@SNIPSTART python-microsoft-agent-framework-mcp-worker import asyncio import os import sys @@ -10,8 +10,8 @@ from mcp import Client as MCPClient from mcp import StdioServerParameters, stdio_client from openai import AsyncOpenAI -from temporalio.agent_framework import AgentFrameworkPlugin from temporalio.client import Client +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin from temporalio.worker import Worker from workflow import MCPWorkflow @@ -39,7 +39,7 @@ async def main() -> None: if not os.environ.get("OPENAI_API_KEY"): raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") - plugin = AgentFrameworkPlugin( + plugin = MicrosoftAgentFrameworkPlugin( chat_clients={"gpt-4o-mini": openai_chat_client}, mcp_clients={"echo": echo_mcp_client}, ) @@ -50,7 +50,7 @@ async def main() -> None: worker = Worker( client, - task_queue="agent-framework-mcp", + task_queue="microsoft-agent-framework-mcp", workflows=[MCPWorkflow], ) print("Worker started. Ctrl+C to exit.") diff --git a/agent_framework_plugin/mcp/run_workflow.py b/microsoft_agent_framework_plugin/mcp/run_workflow.py similarity index 83% rename from agent_framework_plugin/mcp/run_workflow.py rename to microsoft_agent_framework_plugin/mcp/run_workflow.py index 5bde08f7..eb9b9c15 100644 --- a/agent_framework_plugin/mcp/run_workflow.py +++ b/microsoft_agent_framework_plugin/mcp/run_workflow.py @@ -14,8 +14,8 @@ async def main() -> None: result = await client.execute_workflow( MCPWorkflow.run, "Use the echo tool to echo the message 'hello from MCP'.", - id="agent-framework-mcp", - task_queue="agent-framework-mcp", + id="microsoft-agent-framework-mcp", + task_queue="microsoft-agent-framework-mcp", ) print(f"Result: {result}") diff --git a/agent_framework_plugin/mcp/tests/conftest.py b/microsoft_agent_framework_plugin/mcp/tests/conftest.py similarity index 85% rename from agent_framework_plugin/mcp/tests/conftest.py rename to microsoft_agent_framework_plugin/mcp/tests/conftest.py index 4a713fcc..4e4f7f0f 100644 --- a/agent_framework_plugin/mcp/tests/conftest.py +++ b/microsoft_agent_framework_plugin/mcp/tests/conftest.py @@ -15,7 +15,7 @@ _plugin_available = False if sys.version_info >= (3, 11): try: - import temporalio.agent_framework # noqa: F401 + import temporalio.microsoft_agent_framework # noqa: F401 _plugin_available = True except ImportError: @@ -28,7 +28,7 @@ def pytest_report_header(config: pytest.Config) -> str | None: if collect_ignore_glob: return ( - "agent_framework_plugin/mcp: temporalio.agent_framework not importable " + "microsoft_agent_framework_plugin/mcp: temporalio.microsoft_agent_framework not importable " "on this interpreter/environment; sample tests NOT collected" ) return None diff --git a/agent_framework_plugin/mcp/tests/mcp_test.py b/microsoft_agent_framework_plugin/mcp/tests/mcp_test.py similarity index 88% rename from agent_framework_plugin/mcp/tests/mcp_test.py rename to microsoft_agent_framework_plugin/mcp/tests/mcp_test.py index 267f489d..155517f1 100644 --- a/agent_framework_plugin/mcp/tests/mcp_test.py +++ b/microsoft_agent_framework_plugin/mcp/tests/mcp_test.py @@ -3,21 +3,21 @@ from collections.abc import AsyncGenerator import pytest_asyncio -from temporalio.agent_framework import AgentFrameworkPlugin -from temporalio.agent_framework.testing import ( +from temporalio.api.enums.v1 import EventType +from temporalio.client import Client, WorkflowHandle +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin +from temporalio.microsoft_agent_framework.testing import ( fake_chat_client, function_call_response, text_response, ) -from temporalio.api.enums.v1 import EventType -from temporalio.client import Client, WorkflowHandle from temporalio.testing import WorkflowEnvironment from temporalio.worker import Worker from run_worker import echo_mcp_client from workflow import MCPWorkflow -INVOKE_MODEL = "agent_framework.invoke_model" +INVOKE_MODEL = "microsoft_agent_framework.invoke_model" LIST_TOOLS = "temporalio.mcp.echo.list-tools" CALL_TOOL = "temporalio.mcp.echo.call-tool" @@ -51,13 +51,13 @@ async def test_mcp_tool_runs_as_activities(env: WorkflowEnvironment) -> None: text_response("The server echoed: hello from MCP"), ] ) - plugin = AgentFrameworkPlugin( + plugin = MicrosoftAgentFrameworkPlugin( chat_clients={"gpt-4o-mini": factory}, mcp_clients={"echo": echo_mcp_client} ) config = env.client.config() config["plugins"] = [*config["plugins"], plugin] client = Client(**config) - task_queue = f"agent-framework-mcp-{uuid.uuid4()}" + task_queue = f"microsoft-agent-framework-mcp-{uuid.uuid4()}" async with Worker( client, @@ -68,7 +68,7 @@ async def test_mcp_tool_runs_as_activities(env: WorkflowEnvironment) -> None: handle = await client.start_workflow( MCPWorkflow.run, "Echo 'hello from MCP'.", - id=f"agent-framework-mcp-{uuid.uuid4()}", + id=f"microsoft-agent-framework-mcp-{uuid.uuid4()}", task_queue=task_queue, ) result = await handle.result() diff --git a/agent_framework_plugin/mcp/uv.lock b/microsoft_agent_framework_plugin/mcp/uv.lock similarity index 99% rename from agent_framework_plugin/mcp/uv.lock rename to microsoft_agent_framework_plugin/mcp/uv.lock index a4de261e..6fdda106 100644 --- a/agent_framework_plugin/mcp/uv.lock +++ b/microsoft_agent_framework_plugin/mcp/uv.lock @@ -1601,23 +1601,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/d8/95ec26dcb35ef2dd35eae52e9e5223f148b21c05344d46ca29d746ebefe9/temporalio-1.36.0-cp311-abi3-win_amd64.whl", hash = "sha256:74940ad05bebf84e1c53a9f10a69addd10b819985286cd5e05c8b48340ded815", size = 15472448, upload-time = "2026-11-11T01:08:36.551Z" }, ] -[[package]] -name = "temporalio-agent-framework" -version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python#509bfab4ffc16410ce28aa4c092c8c15853b0874" } -dependencies = [ - { name = "agent-framework-core" }, - { name = "pydantic" }, - { name = "temporalio" }, -] - -[package.optional-dependencies] -mcp = [ - { name = "mcp" }, - { name = "mcp-types" }, - { name = "temporalio-mcp" }, -] - [[package]] name = "temporalio-mcp" version = "0.3.0" @@ -1634,12 +1617,29 @@ wheels = [ ] [[package]] -name = "temporalio-samples-agent-framework-mcp" +name = "temporalio-microsoft-agent-framework" +version = "0.0.0" +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#d20bc0c83d7e0009842ce1a3368d789b08767dd6" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "pydantic" }, + { name = "temporalio" }, +] + +[package.optional-dependencies] +mcp = [ + { name = "mcp" }, + { name = "mcp-types" }, + { name = "temporalio-mcp" }, +] + +[[package]] +name = "temporalio-samples-microsoft-agent-framework-mcp" version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "agent-framework-openai", marker = "python_full_version >= '3.11'" }, - { name = "temporalio-agent-framework", extra = ["mcp"], marker = "python_full_version >= '3.11'" }, + { name = "temporalio-microsoft-agent-framework", extra = ["mcp"], marker = "python_full_version >= '3.11'" }, ] [package.dev-dependencies] @@ -1654,7 +1654,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "agent-framework-openai", marker = "python_full_version >= '3.11'", specifier = ">=1.17,<2" }, - { name = "temporalio-agent-framework", extras = ["mcp"], marker = "python_full_version >= '3.11'", git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python" }, + { name = "temporalio-microsoft-agent-framework", extras = ["mcp"], marker = "python_full_version >= '3.11'", git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python" }, ] [package.metadata.requires-dev] diff --git a/agent_framework_plugin/mcp/workflow.py b/microsoft_agent_framework_plugin/mcp/workflow.py similarity index 85% rename from agent_framework_plugin/mcp/workflow.py rename to microsoft_agent_framework_plugin/mcp/workflow.py index a9d1991a..426a99e0 100644 --- a/agent_framework_plugin/mcp/workflow.py +++ b/microsoft_agent_framework_plugin/mcp/workflow.py @@ -6,12 +6,15 @@ connection to the server lives on the Worker. """ -# @@@SNIPSTART python-agent-framework-mcp-workflow +# @@@SNIPSTART python-microsoft-agent-framework-mcp-workflow from datetime import timedelta from agent_framework import Agent from temporalio import workflow -from temporalio.agent_framework import TemporalChatClient, TemporalMCPToolProvider +from temporalio.microsoft_agent_framework import ( + TemporalChatClient, + TemporalMCPToolProvider, +) @workflow.defn diff --git a/agent_framework_plugin/middleware/README.md b/microsoft_agent_framework_plugin/middleware/README.md similarity index 81% rename from agent_framework_plugin/middleware/README.md rename to microsoft_agent_framework_plugin/middleware/README.md index 5f2a2ae3..9fa92014 100644 --- a/agent_framework_plugin/middleware/README.md +++ b/microsoft_agent_framework_plugin/middleware/README.md @@ -14,13 +14,13 @@ Both run inside the Workflow, next to the agent's loop, so a Workflow Definition ```bash # Terminal 1 -uv run agent_framework_plugin/middleware/run_worker.py +uv run microsoft_agent_framework_plugin/middleware/run_worker.py # Terminal 2 -uv run agent_framework_plugin/middleware/run_workflow.py +uv run microsoft_agent_framework_plugin/middleware/run_workflow.py ``` -The prompt asks for two deletes. The agent reports that `/etc/passwd` was refused and `/tmp/scratch.txt` was deleted, in one short sentence because of the reminder. The Workflow `agent-framework-middleware` shows three `agent_framework.invoke_model` Activities and nothing for the tool calls. +The prompt asks for two deletes. The agent reports that `/etc/passwd` was refused and `/tmp/scratch.txt` was deleted, in one short sentence because of the reminder. The Workflow `microsoft-agent-framework-middleware` shows three `microsoft_agent_framework.invoke_model` Activities and nothing for the tool calls. ## Files diff --git a/agent_framework_plugin/middleware/__init__.py b/microsoft_agent_framework_plugin/middleware/__init__.py similarity index 100% rename from agent_framework_plugin/middleware/__init__.py rename to microsoft_agent_framework_plugin/middleware/__init__.py diff --git a/agent_framework_plugin/middleware/run_worker.py b/microsoft_agent_framework_plugin/middleware/run_worker.py similarity index 68% rename from agent_framework_plugin/middleware/run_worker.py rename to microsoft_agent_framework_plugin/middleware/run_worker.py index f3557559..e3084510 100644 --- a/agent_framework_plugin/middleware/run_worker.py +++ b/microsoft_agent_framework_plugin/middleware/run_worker.py @@ -1,16 +1,16 @@ """Worker for the middleware sample.""" -# @@@SNIPSTART python-agent-framework-middleware-worker +# @@@SNIPSTART python-microsoft-agent-framework-middleware-worker import asyncio import os from agent_framework.openai import OpenAIChatClient from openai import AsyncOpenAI -from temporalio.agent_framework import AgentFrameworkPlugin from temporalio.client import Client +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin from temporalio.worker import Worker -from agent_framework_plugin.middleware.workflow import MiddlewareWorkflow +from microsoft_agent_framework_plugin.middleware.workflow import MiddlewareWorkflow def openai_chat_client() -> OpenAIChatClient: @@ -23,7 +23,9 @@ async def main() -> None: if not os.environ.get("OPENAI_API_KEY"): raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") - plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + plugin = MicrosoftAgentFrameworkPlugin( + chat_clients={"gpt-4o-mini": openai_chat_client} + ) client = await Client.connect( os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), plugins=[plugin], @@ -31,7 +33,7 @@ async def main() -> None: worker = Worker( client, - task_queue="agent-framework-middleware", + task_queue="microsoft-agent-framework-middleware", workflows=[MiddlewareWorkflow], ) print("Worker started. Ctrl+C to exit.") diff --git a/agent_framework_plugin/middleware/run_workflow.py b/microsoft_agent_framework_plugin/middleware/run_workflow.py similarity index 70% rename from agent_framework_plugin/middleware/run_workflow.py rename to microsoft_agent_framework_plugin/middleware/run_workflow.py index a9254436..03bb308a 100644 --- a/agent_framework_plugin/middleware/run_workflow.py +++ b/microsoft_agent_framework_plugin/middleware/run_workflow.py @@ -5,7 +5,7 @@ from temporalio.client import Client -from agent_framework_plugin.middleware.workflow import MiddlewareWorkflow +from microsoft_agent_framework_plugin.middleware.workflow import MiddlewareWorkflow async def main() -> None: @@ -14,8 +14,8 @@ async def main() -> None: result = await client.execute_workflow( MiddlewareWorkflow.run, "Delete /etc/passwd, then delete /tmp/scratch.txt, and tell me what happened.", - id="agent-framework-middleware", - task_queue="agent-framework-middleware", + id="microsoft-agent-framework-middleware", + task_queue="microsoft-agent-framework-middleware", ) print(f"Result: {result}") diff --git a/agent_framework_plugin/middleware/workflow.py b/microsoft_agent_framework_plugin/middleware/workflow.py similarity index 92% rename from agent_framework_plugin/middleware/workflow.py rename to microsoft_agent_framework_plugin/middleware/workflow.py index a6041956..d7b193ed 100644 --- a/agent_framework_plugin/middleware/workflow.py +++ b/microsoft_agent_framework_plugin/middleware/workflow.py @@ -17,7 +17,7 @@ tool, ) from temporalio import workflow -from temporalio.agent_framework import TemporalChatClient +from temporalio.microsoft_agent_framework import TemporalChatClient @tool @@ -26,7 +26,7 @@ def delete_file(path: str) -> str: return f"deleted {path}" -# @@@SNIPSTART python-agent-framework-middleware-middleware +# @@@SNIPSTART python-microsoft-agent-framework-middleware-middleware ALLOWED_PREFIX = "/tmp/" REMINDER = "Answer in one short sentence." @@ -69,7 +69,7 @@ async def process( # @@@SNIPEND -# @@@SNIPSTART python-agent-framework-middleware-workflow +# @@@SNIPSTART python-microsoft-agent-framework-middleware-workflow @workflow.defn class MiddlewareWorkflow: @workflow.run diff --git a/agent_framework_plugin/multi_agent/README.md b/microsoft_agent_framework_plugin/multi_agent/README.md similarity index 72% rename from agent_framework_plugin/multi_agent/README.md rename to microsoft_agent_framework_plugin/multi_agent/README.md index 7c98bf0e..b0b121b7 100644 --- a/agent_framework_plugin/multi_agent/README.md +++ b/microsoft_agent_framework_plugin/multi_agent/README.md @@ -2,7 +2,7 @@ A coordinator agent with two sub-agents as tools. `researcher.as_tool()` and `writer.as_tool()` are Agent Framework's own way to delegate; the coordinator decides when to call them. All three agents run in the Workflow, and every model call any of them makes is an Activity, so the whole tree is durable with no per-agent wiring. -Each agent has its own `TemporalChatClient` with a `summary`, so the Activities read `coordinator`, `researcher`, `coordinator`, `writer`, `coordinator` in the Temporal UI. `Agent.as_tool()` runs the sub-agent streaming, so the two sub-agent calls are `agent_framework.invoke_model_streaming` Activities and the coordinator's three are `agent_framework.invoke_model`. +Each agent has its own `TemporalChatClient` with a `summary`, so the Activities read `coordinator`, `researcher`, `coordinator`, `writer`, `coordinator` in the Temporal UI. `Agent.as_tool()` runs the sub-agent streaming, so the two sub-agent calls are `microsoft_agent_framework.invoke_model_streaming` Activities and the coordinator's three are `microsoft_agent_framework.invoke_model`. ## What this sample demonstrates @@ -14,13 +14,13 @@ Each agent has its own `TemporalChatClient` with a `summary`, so the Activities ```bash # Terminal 1 -uv run agent_framework_plugin/multi_agent/run_worker.py +uv run microsoft_agent_framework_plugin/multi_agent/run_worker.py # Terminal 2 -uv run agent_framework_plugin/multi_agent/run_workflow.py +uv run microsoft_agent_framework_plugin/multi_agent/run_workflow.py ``` -The starter prints the writer's paragraph as relayed by the coordinator. The Workflow `agent-framework-multi-agent` shows the five Activities above. +The starter prints the writer's paragraph as relayed by the coordinator. The Workflow `microsoft-agent-framework-multi-agent` shows the five Activities above. ## Files diff --git a/agent_framework_plugin/multi_agent/__init__.py b/microsoft_agent_framework_plugin/multi_agent/__init__.py similarity index 100% rename from agent_framework_plugin/multi_agent/__init__.py rename to microsoft_agent_framework_plugin/multi_agent/__init__.py diff --git a/agent_framework_plugin/multi_agent/run_worker.py b/microsoft_agent_framework_plugin/multi_agent/run_worker.py similarity index 70% rename from agent_framework_plugin/multi_agent/run_worker.py rename to microsoft_agent_framework_plugin/multi_agent/run_worker.py index 454c2687..f13090cc 100644 --- a/agent_framework_plugin/multi_agent/run_worker.py +++ b/microsoft_agent_framework_plugin/multi_agent/run_worker.py @@ -1,16 +1,16 @@ """Worker for the multi-agent sample.""" -# @@@SNIPSTART python-agent-framework-multi-agent-worker +# @@@SNIPSTART python-microsoft-agent-framework-multi-agent-worker import asyncio import os from agent_framework.openai import OpenAIChatClient from openai import AsyncOpenAI -from temporalio.agent_framework import AgentFrameworkPlugin from temporalio.client import Client +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin from temporalio.worker import Worker -from agent_framework_plugin.multi_agent.workflow import MultiAgentWorkflow +from microsoft_agent_framework_plugin.multi_agent.workflow import MultiAgentWorkflow def openai_chat_client() -> OpenAIChatClient: @@ -24,7 +24,9 @@ async def main() -> None: raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") # All three agents use the same model name; one factory serves them all. - plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + plugin = MicrosoftAgentFrameworkPlugin( + chat_clients={"gpt-4o-mini": openai_chat_client} + ) client = await Client.connect( os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), plugins=[plugin], @@ -32,7 +34,7 @@ async def main() -> None: worker = Worker( client, - task_queue="agent-framework-multi-agent", + task_queue="microsoft-agent-framework-multi-agent", workflows=[MultiAgentWorkflow], ) print("Worker started. Ctrl+C to exit.") diff --git a/agent_framework_plugin/multi_agent/run_workflow.py b/microsoft_agent_framework_plugin/multi_agent/run_workflow.py similarity index 67% rename from agent_framework_plugin/multi_agent/run_workflow.py rename to microsoft_agent_framework_plugin/multi_agent/run_workflow.py index 479c420c..ea6a5027 100644 --- a/agent_framework_plugin/multi_agent/run_workflow.py +++ b/microsoft_agent_framework_plugin/multi_agent/run_workflow.py @@ -5,7 +5,7 @@ from temporalio.client import Client -from agent_framework_plugin.multi_agent.workflow import MultiAgentWorkflow +from microsoft_agent_framework_plugin.multi_agent.workflow import MultiAgentWorkflow async def main() -> None: @@ -14,8 +14,8 @@ async def main() -> None: result = await client.execute_workflow( MultiAgentWorkflow.run, "durable execution", - id="agent-framework-multi-agent", - task_queue="agent-framework-multi-agent", + id="microsoft-agent-framework-multi-agent", + task_queue="microsoft-agent-framework-multi-agent", ) print(f"Result: {result}") diff --git a/agent_framework_plugin/multi_agent/workflow.py b/microsoft_agent_framework_plugin/multi_agent/workflow.py similarity index 92% rename from agent_framework_plugin/multi_agent/workflow.py rename to microsoft_agent_framework_plugin/multi_agent/workflow.py index 68a04cd1..9a8b7068 100644 --- a/agent_framework_plugin/multi_agent/workflow.py +++ b/microsoft_agent_framework_plugin/multi_agent/workflow.py @@ -5,12 +5,12 @@ coordinator a sub-agent is a tool like any other. """ -# @@@SNIPSTART python-agent-framework-multi-agent-workflow +# @@@SNIPSTART python-microsoft-agent-framework-multi-agent-workflow from datetime import timedelta from agent_framework import Agent from temporalio import workflow -from temporalio.agent_framework import TemporalChatClient +from temporalio.microsoft_agent_framework import TemporalChatClient def chat_client(summary: str) -> TemporalChatClient: diff --git a/agent_framework_plugin/open_telemetry/README.md b/microsoft_agent_framework_plugin/open_telemetry/README.md similarity index 66% rename from agent_framework_plugin/open_telemetry/README.md rename to microsoft_agent_framework_plugin/open_telemetry/README.md index 30d0fa0e..a91bedc9 100644 --- a/agent_framework_plugin/open_telemetry/README.md +++ b/microsoft_agent_framework_plugin/open_telemetry/README.md @@ -4,7 +4,7 @@ One trace for a Workflow and the agent it runs: the starter's root span, the Wor ## What this sample demonstrates -- `Client.connect(plugins=[OpenTelemetryPlugin(add_temporal_spans=True), AgentFrameworkPlugin(...)])` on the Worker's Client: tracing first, so the Worker inherits both in that order. The starter's Client carries `OpenTelemetryPlugin` alone and propagates the trace into the Workflow. +- `Client.connect(plugins=[OpenTelemetryPlugin(add_temporal_spans=True), MicrosoftAgentFrameworkPlugin(...)])` on the Worker's Client: tracing first, so the Worker inherits both in that order. The starter's Client carries `OpenTelemetryPlugin` alone and propagates the trace into the Workflow. - `create_tracer_provider()` from `temporalio.contrib.opentelemetry` as the global provider in both processes, from [telemetry.py](telemetry.py). Agent Framework starts its `invoke_agent` span from Workflow code, so the provider has to be replay-safe: Workflow spans keep their ids across replays and are exported once. With a plain SDK `TracerProvider` the plugin warns at Worker start, and `OpenTelemetryPlugin` refuses to start the Worker. - The OpenAI client's own `chat gpt-4o-mini` span runs inside the Activity with real timings. @@ -28,10 +28,10 @@ Then the Worker and the starter, from the repository root: ```bash # Terminal 1 -uv run agent_framework_plugin/open_telemetry/run_worker.py +uv run microsoft_agent_framework_plugin/open_telemetry/run_worker.py # Terminal 2 -uv run agent_framework_plugin/open_telemetry/run_workflow.py +uv run microsoft_agent_framework_plugin/open_telemetry/run_workflow.py ``` The starter prints the result and the trace id: @@ -44,13 +44,13 @@ Trace ID: aa8b95b37c1b39f76cc9102fa3d1d5b9 Open `http://localhost:16686/trace/`. The trace spans two services: ``` -ask-agent agent-framework-starter -└─ StartWorkflow:TracedWorkflow agent-framework-starter - └─ RunWorkflow:TracedWorkflow agent-framework-worker - └─ invoke_agent assistant agent-framework-worker - └─ StartActivity:agent_framework.invoke_model agent-framework-worker - └─ RunActivity:agent_framework.invoke_model agent-framework-worker - └─ chat gpt-4o-mini agent-framework-worker +ask-agent microsoft-agent-framework-starter +└─ StartWorkflow:TracedWorkflow microsoft-agent-framework-starter + └─ RunWorkflow:TracedWorkflow microsoft-agent-framework-worker + └─ invoke_agent assistant microsoft-agent-framework-worker + └─ StartActivity:microsoft_agent_framework.invoke_model microsoft-agent-framework-worker + └─ RunActivity:microsoft_agent_framework.invoke_model microsoft-agent-framework-worker + └─ chat gpt-4o-mini microsoft-agent-framework-worker ``` ## Files diff --git a/agent_framework_plugin/open_telemetry/__init__.py b/microsoft_agent_framework_plugin/open_telemetry/__init__.py similarity index 100% rename from agent_framework_plugin/open_telemetry/__init__.py rename to microsoft_agent_framework_plugin/open_telemetry/__init__.py diff --git a/agent_framework_plugin/open_telemetry/run_worker.py b/microsoft_agent_framework_plugin/open_telemetry/run_worker.py similarity index 66% rename from agent_framework_plugin/open_telemetry/run_worker.py rename to microsoft_agent_framework_plugin/open_telemetry/run_worker.py index 6c07dad0..259c4feb 100644 --- a/agent_framework_plugin/open_telemetry/run_worker.py +++ b/microsoft_agent_framework_plugin/open_telemetry/run_worker.py @@ -1,18 +1,20 @@ """Worker for the OpenTelemetry sample.""" -# @@@SNIPSTART python-agent-framework-open-telemetry-worker +# @@@SNIPSTART python-microsoft-agent-framework-open-telemetry-worker import asyncio import os from agent_framework.openai import OpenAIChatClient from openai import AsyncOpenAI -from temporalio.agent_framework import AgentFrameworkPlugin from temporalio.client import Client from temporalio.contrib.opentelemetry import OpenTelemetryPlugin +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin from temporalio.worker import Worker -from agent_framework_plugin.open_telemetry.telemetry import install_tracer_provider -from agent_framework_plugin.open_telemetry.workflow import TracedWorkflow +from microsoft_agent_framework_plugin.open_telemetry.telemetry import ( + install_tracer_provider, +) +from microsoft_agent_framework_plugin.open_telemetry.workflow import TracedWorkflow def openai_chat_client() -> OpenAIChatClient: @@ -26,8 +28,10 @@ async def main() -> None: raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") # Set the provider before connecting; the plugins read it at startup. - install_tracer_provider("agent-framework-worker") - plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + install_tracer_provider("microsoft-agent-framework-worker") + plugin = MicrosoftAgentFrameworkPlugin( + chat_clients={"gpt-4o-mini": openai_chat_client} + ) # The Worker inherits the Client's plugins in this order: tracing first. client = await Client.connect( os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), @@ -36,7 +40,7 @@ async def main() -> None: worker = Worker( client, - task_queue="agent-framework-open-telemetry", + task_queue="microsoft-agent-framework-open-telemetry", workflows=[TracedWorkflow], ) print("Worker started. Ctrl+C to exit.") diff --git a/agent_framework_plugin/open_telemetry/run_workflow.py b/microsoft_agent_framework_plugin/open_telemetry/run_workflow.py similarity index 69% rename from agent_framework_plugin/open_telemetry/run_workflow.py rename to microsoft_agent_framework_plugin/open_telemetry/run_workflow.py index dae519ea..1d8625b6 100644 --- a/agent_framework_plugin/open_telemetry/run_workflow.py +++ b/microsoft_agent_framework_plugin/open_telemetry/run_workflow.py @@ -1,6 +1,6 @@ """Start the traced workflow under a root span and print the trace id.""" -# @@@SNIPSTART python-agent-framework-open-telemetry-run-workflow +# @@@SNIPSTART python-microsoft-agent-framework-open-telemetry-run-workflow import asyncio import os @@ -8,12 +8,14 @@ from temporalio.client import Client from temporalio.contrib.opentelemetry import OpenTelemetryPlugin -from agent_framework_plugin.open_telemetry.telemetry import install_tracer_provider -from agent_framework_plugin.open_telemetry.workflow import TracedWorkflow +from microsoft_agent_framework_plugin.open_telemetry.telemetry import ( + install_tracer_provider, +) +from microsoft_agent_framework_plugin.open_telemetry.workflow import TracedWorkflow async def main() -> None: - install_tracer_provider("agent-framework-starter") + install_tracer_provider("microsoft-agent-framework-starter") client = await Client.connect( os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), plugins=[OpenTelemetryPlugin(add_temporal_spans=True)], @@ -26,8 +28,8 @@ async def main() -> None: result = await client.execute_workflow( TracedWorkflow.run, "What is durable execution? Answer in one sentence.", - id="agent-framework-open-telemetry", - task_queue="agent-framework-open-telemetry", + id="microsoft-agent-framework-open-telemetry", + task_queue="microsoft-agent-framework-open-telemetry", ) trace_id = format(span.get_span_context().trace_id, "032x") diff --git a/agent_framework_plugin/open_telemetry/telemetry.py b/microsoft_agent_framework_plugin/open_telemetry/telemetry.py similarity index 100% rename from agent_framework_plugin/open_telemetry/telemetry.py rename to microsoft_agent_framework_plugin/open_telemetry/telemetry.py diff --git a/agent_framework_plugin/open_telemetry/workflow.py b/microsoft_agent_framework_plugin/open_telemetry/workflow.py similarity index 82% rename from agent_framework_plugin/open_telemetry/workflow.py rename to microsoft_agent_framework_plugin/open_telemetry/workflow.py index 4c1de24b..633bec27 100644 --- a/agent_framework_plugin/open_telemetry/workflow.py +++ b/microsoft_agent_framework_plugin/open_telemetry/workflow.py @@ -1,11 +1,11 @@ """A Workflow whose agent run is traced end to end.""" -# @@@SNIPSTART python-agent-framework-open-telemetry-workflow +# @@@SNIPSTART python-microsoft-agent-framework-open-telemetry-workflow from datetime import timedelta from agent_framework import Agent from temporalio import workflow -from temporalio.agent_framework import TemporalChatClient +from temporalio.microsoft_agent_framework import TemporalChatClient @workflow.defn diff --git a/agent_framework_plugin/streaming/README.md b/microsoft_agent_framework_plugin/streaming/README.md similarity index 58% rename from agent_framework_plugin/streaming/README.md rename to microsoft_agent_framework_plugin/streaming/README.md index 4ca348d2..6cc381f8 100644 --- a/agent_framework_plugin/streaming/README.md +++ b/microsoft_agent_framework_plugin/streaming/README.md @@ -1,6 +1,6 @@ # Streaming -Model output streamed to a subscriber outside the Workflow. `TemporalChatClient(streaming_topic="events")` makes a streamed call run as the `agent_framework.invoke_model_streaming` Activity, which publishes every `ChatResponseUpdate` to the `events` topic of a `WorkflowStream` hosted on the Workflow, batched every 100 milliseconds (`streaming_batch_interval`). The Workflow still gets the whole response, so the durable result is the same as without streaming. +Model output streamed to a subscriber outside the Workflow. `TemporalChatClient(streaming_topic="events")` makes a streamed call run as the `microsoft_agent_framework.invoke_model_streaming` Activity, which publishes every `ChatResponseUpdate` to the `events` topic of a `WorkflowStream` hosted on the Workflow, batched every 100 milliseconds (`streaming_batch_interval`). The Workflow still gets the whole response, so the durable result is the same as without streaming. ## What this sample demonstrates @@ -12,13 +12,13 @@ Model output streamed to a subscriber outside the Workflow. `TemporalChatClient( ```bash # Terminal 1 -uv run agent_framework_plugin/streaming/run_worker.py +uv run microsoft_agent_framework_plugin/streaming/run_worker.py # Terminal 2 -uv run agent_framework_plugin/streaming/run_workflow.py +uv run microsoft_agent_framework_plugin/streaming/run_workflow.py ``` -The starter prints the text as it arrives, then `Final result:` with the whole text. The Workflow `agent-framework-streaming` shows one `agent_framework.invoke_model_streaming` Activity and one Signal per published batch. +The starter prints the text as it arrives, then `Final result:` with the whole text. The Workflow `microsoft-agent-framework-streaming` shows one `microsoft_agent_framework.invoke_model_streaming` Activity and one Signal per published batch. ## Files diff --git a/agent_framework_plugin/streaming/__init__.py b/microsoft_agent_framework_plugin/streaming/__init__.py similarity index 100% rename from agent_framework_plugin/streaming/__init__.py rename to microsoft_agent_framework_plugin/streaming/__init__.py diff --git a/agent_framework_plugin/streaming/run_worker.py b/microsoft_agent_framework_plugin/streaming/run_worker.py similarity index 69% rename from agent_framework_plugin/streaming/run_worker.py rename to microsoft_agent_framework_plugin/streaming/run_worker.py index 9bc682bd..e2567e1e 100644 --- a/agent_framework_plugin/streaming/run_worker.py +++ b/microsoft_agent_framework_plugin/streaming/run_worker.py @@ -1,16 +1,16 @@ """Worker for the streaming sample.""" -# @@@SNIPSTART python-agent-framework-streaming-worker +# @@@SNIPSTART python-microsoft-agent-framework-streaming-worker import asyncio import os from agent_framework.openai import OpenAIChatClient from openai import AsyncOpenAI -from temporalio.agent_framework import AgentFrameworkPlugin from temporalio.client import Client +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin from temporalio.worker import Worker -from agent_framework_plugin.streaming.workflow import StreamingWorkflow +from microsoft_agent_framework_plugin.streaming.workflow import StreamingWorkflow def openai_chat_client() -> OpenAIChatClient: @@ -23,7 +23,9 @@ async def main() -> None: if not os.environ.get("OPENAI_API_KEY"): raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") - plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + plugin = MicrosoftAgentFrameworkPlugin( + chat_clients={"gpt-4o-mini": openai_chat_client} + ) client = await Client.connect( os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), plugins=[plugin], @@ -31,7 +33,7 @@ async def main() -> None: worker = Worker( client, - task_queue="agent-framework-streaming", + task_queue="microsoft-agent-framework-streaming", workflows=[StreamingWorkflow], ) print("Worker started. Ctrl+C to exit.") diff --git a/agent_framework_plugin/streaming/run_workflow.py b/microsoft_agent_framework_plugin/streaming/run_workflow.py similarity index 86% rename from agent_framework_plugin/streaming/run_workflow.py rename to microsoft_agent_framework_plugin/streaming/run_workflow.py index dbd70fb7..b47abf1d 100644 --- a/agent_framework_plugin/streaming/run_workflow.py +++ b/microsoft_agent_framework_plugin/streaming/run_workflow.py @@ -1,6 +1,6 @@ """Start the streaming workflow and print the model's updates as they arrive.""" -# @@@SNIPSTART python-agent-framework-streaming-client +# @@@SNIPSTART python-microsoft-agent-framework-streaming-client import asyncio import os from datetime import timedelta @@ -9,18 +9,21 @@ from temporalio.client import Client from temporalio.contrib.workflow_streams import WorkflowStreamClient -from agent_framework_plugin.streaming.workflow import STREAMING_TOPIC, StreamingWorkflow +from microsoft_agent_framework_plugin.streaming.workflow import ( + STREAMING_TOPIC, + StreamingWorkflow, +) async def main() -> None: client = await Client.connect(os.environ.get("TEMPORAL_ADDRESS", "localhost:7233")) - workflow_id = "agent-framework-streaming" + workflow_id = "microsoft-agent-framework-streaming" handle = await client.start_workflow( StreamingWorkflow.run, "Count from 1 to 5, one number per sentence.", id=workflow_id, - task_queue="agent-framework-streaming", + task_queue="microsoft-agent-framework-streaming", ) printed: list[str] = [] diff --git a/agent_framework_plugin/streaming/workflow.py b/microsoft_agent_framework_plugin/streaming/workflow.py similarity index 90% rename from agent_framework_plugin/streaming/workflow.py rename to microsoft_agent_framework_plugin/streaming/workflow.py index 7146b216..e6b77e12 100644 --- a/agent_framework_plugin/streaming/workflow.py +++ b/microsoft_agent_framework_plugin/streaming/workflow.py @@ -6,13 +6,13 @@ response, so the result is the same as without streaming. """ -# @@@SNIPSTART python-agent-framework-streaming-workflow +# @@@SNIPSTART python-microsoft-agent-framework-streaming-workflow from datetime import timedelta from agent_framework import Agent from temporalio import workflow -from temporalio.agent_framework import TemporalChatClient from temporalio.contrib.workflow_streams import WorkflowStream +from temporalio.microsoft_agent_framework import TemporalChatClient STREAMING_TOPIC = "events" diff --git a/agent_framework_plugin/structured_output/README.md b/microsoft_agent_framework_plugin/structured_output/README.md similarity index 70% rename from agent_framework_plugin/structured_output/README.md rename to microsoft_agent_framework_plugin/structured_output/README.md index 4c646291..0c79d6de 100644 --- a/agent_framework_plugin/structured_output/README.md +++ b/microsoft_agent_framework_plugin/structured_output/README.md @@ -6,19 +6,19 @@ The agent fills a Pydantic model. `agent.run(prompt, options={"response_format": - `options={"response_format": PersonInfo}` and `response.value` - A Workflow whose return type is a Pydantic model -- `AgentFrameworkPlugin()` on the starter's Client too, so the starter decodes the result with the same converter +- `MicrosoftAgentFrameworkPlugin()` on the starter's Client too, so the starter decodes the result with the same converter ## Running the sample ```bash # Terminal 1 -uv run agent_framework_plugin/structured_output/run_worker.py +uv run microsoft_agent_framework_plugin/structured_output/run_worker.py # Terminal 2 -uv run agent_framework_plugin/structured_output/run_workflow.py +uv run microsoft_agent_framework_plugin/structured_output/run_workflow.py ``` -The starter prints `name=John Smith age=30 occupation=software engineer`. The Workflow `agent-framework-structured-output` shows one `agent_framework.invoke_model` Activity. +The starter prints `name=John Smith age=30 occupation=software engineer`. The Workflow `microsoft-agent-framework-structured-output` shows one `microsoft_agent_framework.invoke_model` Activity. ## Files diff --git a/agent_framework_plugin/structured_output/__init__.py b/microsoft_agent_framework_plugin/structured_output/__init__.py similarity index 100% rename from agent_framework_plugin/structured_output/__init__.py rename to microsoft_agent_framework_plugin/structured_output/__init__.py diff --git a/agent_framework_plugin/structured_output/run_worker.py b/microsoft_agent_framework_plugin/structured_output/run_worker.py similarity index 67% rename from agent_framework_plugin/structured_output/run_worker.py rename to microsoft_agent_framework_plugin/structured_output/run_worker.py index 29451283..a53dd0ad 100644 --- a/agent_framework_plugin/structured_output/run_worker.py +++ b/microsoft_agent_framework_plugin/structured_output/run_worker.py @@ -1,16 +1,18 @@ """Worker for the structured output sample.""" -# @@@SNIPSTART python-agent-framework-structured-output-worker +# @@@SNIPSTART python-microsoft-agent-framework-structured-output-worker import asyncio import os from agent_framework.openai import OpenAIChatClient from openai import AsyncOpenAI -from temporalio.agent_framework import AgentFrameworkPlugin from temporalio.client import Client +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin from temporalio.worker import Worker -from agent_framework_plugin.structured_output.workflow import StructuredOutputWorkflow +from microsoft_agent_framework_plugin.structured_output.workflow import ( + StructuredOutputWorkflow, +) def openai_chat_client() -> OpenAIChatClient: @@ -23,7 +25,9 @@ async def main() -> None: if not os.environ.get("OPENAI_API_KEY"): raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") - plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + plugin = MicrosoftAgentFrameworkPlugin( + chat_clients={"gpt-4o-mini": openai_chat_client} + ) client = await Client.connect( os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), plugins=[plugin], @@ -31,7 +35,7 @@ async def main() -> None: worker = Worker( client, - task_queue="agent-framework-structured-output", + task_queue="microsoft-agent-framework-structured-output", workflows=[StructuredOutputWorkflow], ) print("Worker started. Ctrl+C to exit.") diff --git a/agent_framework_plugin/structured_output/run_workflow.py b/microsoft_agent_framework_plugin/structured_output/run_workflow.py similarity index 60% rename from agent_framework_plugin/structured_output/run_workflow.py rename to microsoft_agent_framework_plugin/structured_output/run_workflow.py index 8403066b..159fb7b7 100644 --- a/agent_framework_plugin/structured_output/run_workflow.py +++ b/microsoft_agent_framework_plugin/structured_output/run_workflow.py @@ -1,13 +1,15 @@ """Start the structured output workflow and print the typed result.""" -# @@@SNIPSTART python-agent-framework-structured-output-run-workflow +# @@@SNIPSTART python-microsoft-agent-framework-structured-output-run-workflow import asyncio import os -from temporalio.agent_framework import AgentFrameworkPlugin from temporalio.client import Client +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin -from agent_framework_plugin.structured_output.workflow import StructuredOutputWorkflow +from microsoft_agent_framework_plugin.structured_output.workflow import ( + StructuredOutputWorkflow, +) async def main() -> None: @@ -15,14 +17,14 @@ async def main() -> None: # decodes the Workflow's PersonInfo result. client = await Client.connect( os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), - plugins=[AgentFrameworkPlugin()], + plugins=[MicrosoftAgentFrameworkPlugin()], ) person = await client.execute_workflow( StructuredOutputWorkflow.run, "John Smith is a 30 year-old software engineer.", - id="agent-framework-structured-output", - task_queue="agent-framework-structured-output", + id="microsoft-agent-framework-structured-output", + task_queue="microsoft-agent-framework-structured-output", ) print(f"name={person.name} age={person.age} occupation={person.occupation}") diff --git a/agent_framework_plugin/structured_output/workflow.py b/microsoft_agent_framework_plugin/structured_output/workflow.py similarity index 89% rename from agent_framework_plugin/structured_output/workflow.py rename to microsoft_agent_framework_plugin/structured_output/workflow.py index 5ad6a9c4..c2defe18 100644 --- a/agent_framework_plugin/structured_output/workflow.py +++ b/microsoft_agent_framework_plugin/structured_output/workflow.py @@ -1,13 +1,13 @@ """Typed output: the agent fills a Pydantic model and the Workflow returns it.""" -# @@@SNIPSTART python-agent-framework-structured-output-workflow +# @@@SNIPSTART python-microsoft-agent-framework-structured-output-workflow from datetime import timedelta from agent_framework import Agent from pydantic import BaseModel, Field from temporalio import workflow -from temporalio.agent_framework import TemporalChatClient from temporalio.exceptions import ApplicationError +from temporalio.microsoft_agent_framework import TemporalChatClient class PersonInfo(BaseModel): diff --git a/agent_framework_plugin/tools/README.md b/microsoft_agent_framework_plugin/tools/README.md similarity index 64% rename from agent_framework_plugin/tools/README.md rename to microsoft_agent_framework_plugin/tools/README.md index fac5a9c0..031be14c 100644 --- a/agent_framework_plugin/tools/README.md +++ b/microsoft_agent_framework_plugin/tools/README.md @@ -7,11 +7,11 @@ Two tool patterns on one agent: | `@tool` from `agent_framework` | Pure logic with no I/O. Runs in the Workflow and leaves no Activity. | | `@activity.defn` wrapped with `activity_as_tool` | Anything with I/O or a clock. Runs as an Activity with its own timeout and retries. | -The prompt exercises both. The history shows one `agent_framework.invoke_model` Activity per model turn plus one `fetch_weather` Activity; `letter_counter` runs in the Workflow and does not appear. +The prompt exercises both. The history shows one `microsoft_agent_framework.invoke_model` Activity per model turn plus one `fetch_weather` Activity; `letter_counter` runs in the Workflow and does not appear. ## What this sample demonstrates -- `activity_as_tool(fetch_weather, start_to_close_timeout=timedelta(seconds=30))` from `temporalio.agent_framework.workflow`; Agent Framework reads the tool's schema from the Activity's signature and docstring +- `activity_as_tool(fetch_weather, start_to_close_timeout=timedelta(seconds=30))` from `temporalio.microsoft_agent_framework.workflow`; Agent Framework reads the tool's schema from the Activity's signature and docstring - The plugin cannot tell a pure tool from an I/O tool, so you choose per tool - Registering the Activity on the Worker next to the Workflow - A synchronous `def` tool: the plugin runs it on the Workflow's event loop, so keep it quick @@ -20,13 +20,13 @@ The prompt exercises both. The history shows one `agent_framework.invoke_model` ```bash # Terminal 1 -uv run agent_framework_plugin/tools/run_worker.py +uv run microsoft_agent_framework_plugin/tools/run_worker.py # Terminal 2 -uv run agent_framework_plugin/tools/run_workflow.py +uv run microsoft_agent_framework_plugin/tools/run_workflow.py ``` -The starter prints the agent's answer with the letter count and the weather. The Workflow `agent-framework-tools` shows three model Activities and one `fetch_weather` Activity. +The starter prints the agent's answer with the letter count and the weather. The Workflow `microsoft-agent-framework-tools` shows three model Activities and one `fetch_weather` Activity. ## Files diff --git a/agent_framework_plugin/tools/__init__.py b/microsoft_agent_framework_plugin/tools/__init__.py similarity index 100% rename from agent_framework_plugin/tools/__init__.py rename to microsoft_agent_framework_plugin/tools/__init__.py diff --git a/agent_framework_plugin/tools/run_worker.py b/microsoft_agent_framework_plugin/tools/run_worker.py similarity index 70% rename from agent_framework_plugin/tools/run_worker.py rename to microsoft_agent_framework_plugin/tools/run_worker.py index afa33cef..aac29ec7 100644 --- a/agent_framework_plugin/tools/run_worker.py +++ b/microsoft_agent_framework_plugin/tools/run_worker.py @@ -1,16 +1,16 @@ """Worker for the tools sample.""" -# @@@SNIPSTART python-agent-framework-tools-worker +# @@@SNIPSTART python-microsoft-agent-framework-tools-worker import asyncio import os from agent_framework.openai import OpenAIChatClient from openai import AsyncOpenAI -from temporalio.agent_framework import AgentFrameworkPlugin from temporalio.client import Client +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin from temporalio.worker import Worker -from agent_framework_plugin.tools.workflow import ToolsWorkflow, fetch_weather +from microsoft_agent_framework_plugin.tools.workflow import ToolsWorkflow, fetch_weather def openai_chat_client() -> OpenAIChatClient: @@ -23,7 +23,9 @@ async def main() -> None: if not os.environ.get("OPENAI_API_KEY"): raise SystemExit("Set OPENAI_API_KEY before starting the Worker.") - plugin = AgentFrameworkPlugin(chat_clients={"gpt-4o-mini": openai_chat_client}) + plugin = MicrosoftAgentFrameworkPlugin( + chat_clients={"gpt-4o-mini": openai_chat_client} + ) client = await Client.connect( os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), plugins=[plugin], @@ -31,7 +33,7 @@ async def main() -> None: worker = Worker( client, - task_queue="agent-framework-tools", + task_queue="microsoft-agent-framework-tools", workflows=[ToolsWorkflow], # The Activity behind the fetch_weather tool. activities=[fetch_weather], diff --git a/agent_framework_plugin/tools/run_workflow.py b/microsoft_agent_framework_plugin/tools/run_workflow.py similarity index 75% rename from agent_framework_plugin/tools/run_workflow.py rename to microsoft_agent_framework_plugin/tools/run_workflow.py index d258cfce..e2daec0d 100644 --- a/agent_framework_plugin/tools/run_workflow.py +++ b/microsoft_agent_framework_plugin/tools/run_workflow.py @@ -5,7 +5,7 @@ from temporalio.client import Client -from agent_framework_plugin.tools.workflow import ToolsWorkflow +from microsoft_agent_framework_plugin.tools.workflow import ToolsWorkflow async def main() -> None: @@ -18,8 +18,8 @@ async def main() -> None: "1. Count the letter R's in 'strawberry'.\n" "2. Fetch the weather in San Francisco." ), - id="agent-framework-tools", - task_queue="agent-framework-tools", + id="microsoft-agent-framework-tools", + task_queue="microsoft-agent-framework-tools", ) print(f"Result: {result}") diff --git a/agent_framework_plugin/tools/workflow.py b/microsoft_agent_framework_plugin/tools/workflow.py similarity index 82% rename from agent_framework_plugin/tools/workflow.py rename to microsoft_agent_framework_plugin/tools/workflow.py index ac06a31b..333e7ea2 100644 --- a/agent_framework_plugin/tools/workflow.py +++ b/microsoft_agent_framework_plugin/tools/workflow.py @@ -10,11 +10,11 @@ from agent_framework import Agent, tool from temporalio import activity, workflow -from temporalio.agent_framework import TemporalChatClient -from temporalio.agent_framework.workflow import activity_as_tool +from temporalio.microsoft_agent_framework import TemporalChatClient +from temporalio.microsoft_agent_framework.workflow import activity_as_tool -# @@@SNIPSTART python-agent-framework-tools-tool +# @@@SNIPSTART python-microsoft-agent-framework-tools-tool @tool def letter_counter(word: str, letter: str) -> int: """Count how many times a letter appears in a word, ignoring case.""" @@ -24,7 +24,7 @@ def letter_counter(word: str, letter: str) -> int: # @@@SNIPEND -# @@@SNIPSTART python-agent-framework-tools-activity +# @@@SNIPSTART python-microsoft-agent-framework-tools-activity @activity.defn async def fetch_weather(city: str) -> dict[str, Any]: """Look up the current weather for a city.""" @@ -35,7 +35,7 @@ async def fetch_weather(city: str) -> dict[str, Any]: # @@@SNIPEND -# @@@SNIPSTART python-agent-framework-tools-workflow +# @@@SNIPSTART python-microsoft-agent-framework-tools-workflow @workflow.defn class ToolsWorkflow: @workflow.run diff --git a/pyproject.toml b/pyproject.toml index c45e202f..0af6ac1f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,16 +27,6 @@ dev = [ "pytest-pretty>=1.3.0", "poethepoet>=0.36.0", ] -agent-framework = [ - # No lower bound while [tool.uv.sources] pins the unreleased plugin; set - # ">=0.1.0" when that entry goes. The mcp sample is its own uv project - # (agent_framework_plugin/mcp) because it needs MCP SDK v2 and other - # groups here still pin v1. - "temporalio-agent-framework ; python_version >= '3.11'", - "agent-framework-openai>=1.17,<2 ; python_version >= '3.11'", - "temporalio[opentelemetry]>=1.36.0,<2 ; python_version >= '3.11'", - "opentelemetry-exporter-otlp-proto-grpc>=1.30.0,<2 ; python_version >= '3.11'", -] bedrock = ["boto3>=1.34.92,<2"] claude-agent-sdk = [ "temporalio-claude-agent-sdk>=0.0.2", @@ -88,6 +78,16 @@ langgraph = [ "temporalio-langsmith>=0.1.0", ] litellm = ["litellm>=1.85.0,<2"] +microsoft-agent-framework = [ + # No lower bound while [tool.uv.sources] pins the unreleased plugin; set + # ">=0.1.0" when that entry goes. The mcp sample is its own uv project + # (microsoft_agent_framework_plugin/mcp) because it needs MCP SDK v2 and other + # groups here still pin v1. + "temporalio-microsoft-agent-framework ; python_version >= '3.11'", + "agent-framework-openai>=1.17,<2 ; python_version >= '3.11'", + "temporalio[opentelemetry]>=1.36.0,<2 ; python_version >= '3.11'", + "opentelemetry-exporter-otlp-proto-grpc>=1.30.0,<2 ; python_version >= '3.11'", +] nexus = ["nexus-rpc>=1.1.0,<2"] open-telemetry = [ "temporalio[opentelemetry]", @@ -124,8 +124,8 @@ constraint-dependencies = [ ] [tool.uv.sources] -# Temporary while the plugin is unreleased: remove once temporalio-agent-framework is on PyPI. -temporalio-agent-framework = { git = "https://github.com/temporalio/ai-integrations", subdirectory = "python/agent_framework", branch = "dabh/agent-framework-python" } +# Temporary while the plugin is unreleased: remove once temporalio-microsoft-agent-framework is on PyPI. +temporalio-microsoft-agent-framework = { git = "https://github.com/temporalio/ai-integrations", subdirectory = "python/microsoft_agent_framework", branch = "dabh/agent-framework-python" } [tool.setuptools.packages.find] exclude = ["lambda_worker*"] diff --git a/tests/agent_framework_plugin/__init__.py b/tests/microsoft_agent_framework_plugin/__init__.py similarity index 100% rename from tests/agent_framework_plugin/__init__.py rename to tests/microsoft_agent_framework_plugin/__init__.py diff --git a/tests/agent_framework_plugin/_fake_chat_client.py b/tests/microsoft_agent_framework_plugin/_fake_chat_client.py similarity index 69% rename from tests/agent_framework_plugin/_fake_chat_client.py rename to tests/microsoft_agent_framework_plugin/_fake_chat_client.py index e615a4cc..64e70138 100644 --- a/tests/agent_framework_plugin/_fake_chat_client.py +++ b/tests/microsoft_agent_framework_plugin/_fake_chat_client.py @@ -1,6 +1,6 @@ """Scripted chat client for the sample tests. -Built on ``temporalio.agent_framework.testing``. ``scripted_plugin`` registers +Built on ``temporalio.microsoft_agent_framework.testing``. ``scripted_plugin`` registers one ``FakeChatClient`` under the ``"gpt-4o-mini"`` name the samples use, so the sample Workflows run unchanged against the script and no API key is needed. """ @@ -8,8 +8,8 @@ from collections.abc import Callable, Sequence from typing import Any -from temporalio.agent_framework import AgentFrameworkPlugin -from temporalio.agent_framework.testing import ( +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin +from temporalio.microsoft_agent_framework.testing import ( FakeChatClient, ScriptEntry, fake_chat_client, @@ -24,7 +24,7 @@ def scripted_plugin( responses: Sequence[ScriptEntry], **kwargs: Any -) -> tuple[AgentFrameworkPlugin, Callable[[], FakeChatClient]]: +) -> tuple[MicrosoftAgentFrameworkPlugin, Callable[[], FakeChatClient]]: """Return a plugin whose ``gpt-4o-mini`` client replays ``responses``, and its factory. Each model call takes the next entry; the last one repeats. The factory @@ -32,4 +32,6 @@ def scripted_plugin( ``calls``, the requests the model received. """ factory = fake_chat_client(responses) - return AgentFrameworkPlugin(chat_clients={MODEL: factory}, **kwargs), factory + return MicrosoftAgentFrameworkPlugin( + chat_clients={MODEL: factory}, **kwargs + ), factory diff --git a/tests/agent_framework_plugin/conftest.py b/tests/microsoft_agent_framework_plugin/conftest.py similarity index 73% rename from tests/agent_framework_plugin/conftest.py rename to tests/microsoft_agent_framework_plugin/conftest.py index 99b88960..b04aac1f 100644 --- a/tests/agent_framework_plugin/conftest.py +++ b/tests/microsoft_agent_framework_plugin/conftest.py @@ -1,10 +1,10 @@ """Collection guard for the Agent Framework plugin tests. -The plugin ships as the standalone `temporalio-agent-framework` package, which -the `agent-framework` dependency group installs on Python >= 3.11 only, because +The plugin ships as the standalone `temporalio-microsoft-agent-framework` package, which +the `microsoft-agent-framework` dependency group installs on Python >= 3.11 only, because the plugin declares `requires-python = ">=3.11"`, as Temporal Python SDK 1.36 does. These test modules import -`temporalio.agent_framework` at module load, which would raise `ImportError` +`temporalio.microsoft_agent_framework` at module load, which would raise `ImportError` during collection on 3.10 or in an environment synced without the group, and fail the whole session. @@ -25,7 +25,7 @@ _plugin_available = False if sys.version_info >= (3, 11): try: - import temporalio.agent_framework # noqa: F401 + import temporalio.microsoft_agent_framework # noqa: F401 _plugin_available = True except ImportError: @@ -39,8 +39,8 @@ def pytest_report_header(config) -> str | None: """Make the guard visible in the pytest header instead of silent.""" if collect_ignore_glob: return ( - "agent_framework_plugin: temporalio.agent_framework not importable " + "microsoft_agent_framework_plugin: temporalio.microsoft_agent_framework not importable " "on this interpreter/environment; sample tests NOT collected " - "(see tests/agent_framework_plugin/conftest.py)" + "(see tests/microsoft_agent_framework_plugin/conftest.py)" ) return None diff --git a/tests/agent_framework_plugin/continue_as_new_test.py b/tests/microsoft_agent_framework_plugin/continue_as_new_test.py similarity index 89% rename from tests/agent_framework_plugin/continue_as_new_test.py rename to tests/microsoft_agent_framework_plugin/continue_as_new_test.py index 0d6d568a..56af3a43 100644 --- a/tests/agent_framework_plugin/continue_as_new_test.py +++ b/tests/microsoft_agent_framework_plugin/continue_as_new_test.py @@ -5,9 +5,12 @@ from temporalio.testing import WorkflowEnvironment from temporalio.worker import Worker -from agent_framework_plugin.continue_as_new.workflow import ChatInput, ChatWorkflow -from tests.agent_framework_plugin._fake_chat_client import scripted_plugin -from tests.agent_framework_plugin.helpers import ( +from microsoft_agent_framework_plugin.continue_as_new.workflow import ( + ChatInput, + ChatWorkflow, +) +from tests.microsoft_agent_framework_plugin._fake_chat_client import scripted_plugin +from tests.microsoft_agent_framework_plugin.helpers import ( count_scheduled_activities, with_plugin, ) @@ -31,8 +34,8 @@ async def test_continue_as_new_carries_the_session() -> None: ] ) as env: client = with_plugin(env.client, plugin) - task_queue = f"agent-framework-chat-{uuid.uuid4()}" - workflow_id = f"agent-framework-chat-{uuid.uuid4()}" + task_queue = f"microsoft-agent-framework-chat-{uuid.uuid4()}" + workflow_id = f"microsoft-agent-framework-chat-{uuid.uuid4()}" async with Worker( client, diff --git a/tests/agent_framework_plugin/hello_world_test.py b/tests/microsoft_agent_framework_plugin/hello_world_test.py similarity index 74% rename from tests/agent_framework_plugin/hello_world_test.py rename to tests/microsoft_agent_framework_plugin/hello_world_test.py index 2044aa64..60143ac1 100644 --- a/tests/agent_framework_plugin/hello_world_test.py +++ b/tests/microsoft_agent_framework_plugin/hello_world_test.py @@ -3,9 +3,9 @@ from temporalio.client import Client from temporalio.worker import Worker -from agent_framework_plugin.hello_world.workflow import HelloWorldWorkflow -from tests.agent_framework_plugin._fake_chat_client import scripted_plugin -from tests.agent_framework_plugin.helpers import ( +from microsoft_agent_framework_plugin.hello_world.workflow import HelloWorldWorkflow +from tests.microsoft_agent_framework_plugin._fake_chat_client import scripted_plugin +from tests.microsoft_agent_framework_plugin.helpers import ( INVOKE_MODEL, count_scheduled_activities, with_plugin, @@ -16,7 +16,7 @@ async def test_hello_world(client: Client) -> None: # A scripted model, so the test runs offline with no OPENAI_API_KEY. plugin, factory = scripted_plugin(["Workflows outlive crashes."]) client = with_plugin(client, plugin) - task_queue = f"agent-framework-hello-world-{uuid.uuid4()}" + task_queue = f"microsoft-agent-framework-hello-world-{uuid.uuid4()}" async with Worker( client, @@ -27,7 +27,7 @@ async def test_hello_world(client: Client) -> None: handle = await client.start_workflow( HelloWorldWorkflow.run, "Write a haiku.", - id=f"agent-framework-hello-world-{uuid.uuid4()}", + id=f"microsoft-agent-framework-hello-world-{uuid.uuid4()}", task_queue=task_queue, ) result = await handle.result() diff --git a/tests/agent_framework_plugin/helpers.py b/tests/microsoft_agent_framework_plugin/helpers.py similarity index 89% rename from tests/agent_framework_plugin/helpers.py rename to tests/microsoft_agent_framework_plugin/helpers.py index 9705e475..6571163f 100644 --- a/tests/agent_framework_plugin/helpers.py +++ b/tests/microsoft_agent_framework_plugin/helpers.py @@ -6,15 +6,15 @@ from typing import Any from agent_framework import Message -from temporalio.agent_framework import AgentFrameworkPlugin from temporalio.api.enums.v1 import EventType from temporalio.client import Client, WorkflowHandle +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin -INVOKE_MODEL = "agent_framework.invoke_model" -INVOKE_MODEL_STREAMING = "agent_framework.invoke_model_streaming" +INVOKE_MODEL = "microsoft_agent_framework.invoke_model" +INVOKE_MODEL_STREAMING = "microsoft_agent_framework.invoke_model_streaming" -def with_plugin(client: Client, plugin: AgentFrameworkPlugin) -> Client: +def with_plugin(client: Client, plugin: MicrosoftAgentFrameworkPlugin) -> Client: """Return a client with ``plugin`` added after the environment's own plugins.""" config = client.config() config["plugins"] = [*config["plugins"], plugin] diff --git a/tests/agent_framework_plugin/human_in_the_loop_test.py b/tests/microsoft_agent_framework_plugin/human_in_the_loop_test.py similarity index 89% rename from tests/agent_framework_plugin/human_in_the_loop_test.py rename to tests/microsoft_agent_framework_plugin/human_in_the_loop_test.py index fe8137d9..1712905b 100644 --- a/tests/agent_framework_plugin/human_in_the_loop_test.py +++ b/tests/microsoft_agent_framework_plugin/human_in_the_loop_test.py @@ -5,17 +5,17 @@ from temporalio.client import Client, WorkflowHandle, WorkflowUpdateFailedError from temporalio.worker import Worker -from agent_framework_plugin.human_in_the_loop.workflow import ( +from microsoft_agent_framework_plugin.human_in_the_loop.workflow import ( ApprovalRequest, Decision, HumanInTheLoopWorkflow, ) -from tests.agent_framework_plugin._fake_chat_client import ( +from tests.microsoft_agent_framework_plugin._fake_chat_client import ( function_call_response, scripted_plugin, text_response, ) -from tests.agent_framework_plugin.helpers import ( +from tests.microsoft_agent_framework_plugin.helpers import ( INVOKE_MODEL, count_scheduled_activities, function_results, @@ -48,7 +48,7 @@ async def _pending( async def test_approve(client: Client) -> None: plugin, factory = scripted_plugin(_script("Deleted /tmp/report.txt.")) client = with_plugin(client, plugin) - task_queue = f"agent-framework-human-in-the-loop-{uuid.uuid4()}" + task_queue = f"microsoft-agent-framework-human-in-the-loop-{uuid.uuid4()}" async with Worker( client, @@ -59,7 +59,7 @@ async def test_approve(client: Client) -> None: handle = await client.start_workflow( HumanInTheLoopWorkflow.run, "Please delete /tmp/report.txt", - id=f"agent-framework-human-in-the-loop-{uuid.uuid4()}", + id=f"microsoft-agent-framework-human-in-the-loop-{uuid.uuid4()}", task_queue=task_queue, ) (request,) = await _pending(handle) @@ -85,7 +85,7 @@ async def test_approve(client: Client) -> None: async def test_reject(client: Client) -> None: plugin, factory = scripted_plugin(_script("Understood, I did not delete it.")) client = with_plugin(client, plugin) - task_queue = f"agent-framework-hitl-reject-{uuid.uuid4()}" + task_queue = f"microsoft-agent-framework-hitl-reject-{uuid.uuid4()}" async with Worker( client, @@ -96,7 +96,7 @@ async def test_reject(client: Client) -> None: handle = await client.start_workflow( HumanInTheLoopWorkflow.run, "Please delete /tmp/report.txt", - id=f"agent-framework-hitl-reject-{uuid.uuid4()}", + id=f"microsoft-agent-framework-hitl-reject-{uuid.uuid4()}", task_queue=task_queue, ) (request,) = await _pending(handle) @@ -130,7 +130,7 @@ async def test_bad_decisions_are_rejected(client: Client) -> None: ] ) client = with_plugin(client, plugin) - task_queue = f"agent-framework-hitl-bad-{uuid.uuid4()}" + task_queue = f"microsoft-agent-framework-hitl-bad-{uuid.uuid4()}" async with Worker( client, @@ -141,7 +141,7 @@ async def test_bad_decisions_are_rejected(client: Client) -> None: handle = await client.start_workflow( HumanInTheLoopWorkflow.run, "Please delete /tmp/a.txt and /tmp/b.txt", - id=f"agent-framework-hitl-bad-{uuid.uuid4()}", + id=f"microsoft-agent-framework-hitl-bad-{uuid.uuid4()}", task_queue=task_queue, ) (first,) = await _pending(handle) diff --git a/tests/agent_framework_plugin/middleware_test.py b/tests/microsoft_agent_framework_plugin/middleware_test.py similarity index 82% rename from tests/agent_framework_plugin/middleware_test.py rename to tests/microsoft_agent_framework_plugin/middleware_test.py index 46937832..eddee6d4 100644 --- a/tests/agent_framework_plugin/middleware_test.py +++ b/tests/microsoft_agent_framework_plugin/middleware_test.py @@ -3,13 +3,16 @@ from temporalio.client import Client from temporalio.worker import Worker -from agent_framework_plugin.middleware.workflow import REMINDER, MiddlewareWorkflow -from tests.agent_framework_plugin._fake_chat_client import ( +from microsoft_agent_framework_plugin.middleware.workflow import ( + REMINDER, + MiddlewareWorkflow, +) +from tests.microsoft_agent_framework_plugin._fake_chat_client import ( function_call_response, scripted_plugin, text_response, ) -from tests.agent_framework_plugin.helpers import ( +from tests.microsoft_agent_framework_plugin.helpers import ( INVOKE_MODEL, count_scheduled_activities, function_results, @@ -30,7 +33,7 @@ async def test_middleware(client: Client) -> None: ] ) client = with_plugin(client, plugin) - task_queue = f"agent-framework-middleware-{uuid.uuid4()}" + task_queue = f"microsoft-agent-framework-middleware-{uuid.uuid4()}" async with Worker( client, @@ -41,7 +44,7 @@ async def test_middleware(client: Client) -> None: handle = await client.start_workflow( MiddlewareWorkflow.run, "Delete /etc/passwd, then delete /tmp/scratch.txt.", - id=f"agent-framework-middleware-{uuid.uuid4()}", + id=f"microsoft-agent-framework-middleware-{uuid.uuid4()}", task_queue=task_queue, ) result = await handle.result() diff --git a/tests/agent_framework_plugin/multi_agent_test.py b/tests/microsoft_agent_framework_plugin/multi_agent_test.py similarity index 86% rename from tests/agent_framework_plugin/multi_agent_test.py rename to tests/microsoft_agent_framework_plugin/multi_agent_test.py index aa9d7d3e..ab700603 100644 --- a/tests/agent_framework_plugin/multi_agent_test.py +++ b/tests/microsoft_agent_framework_plugin/multi_agent_test.py @@ -3,13 +3,13 @@ from temporalio.client import Client from temporalio.worker import Worker -from agent_framework_plugin.multi_agent.workflow import MultiAgentWorkflow -from tests.agent_framework_plugin._fake_chat_client import ( +from microsoft_agent_framework_plugin.multi_agent.workflow import MultiAgentWorkflow +from tests.microsoft_agent_framework_plugin._fake_chat_client import ( function_call_response, scripted_plugin, text_response, ) -from tests.agent_framework_plugin.helpers import ( +from tests.microsoft_agent_framework_plugin.helpers import ( INVOKE_MODEL, INVOKE_MODEL_STREAMING, activity_summaries, @@ -36,7 +36,7 @@ async def test_multi_agent(client: Client) -> None: ] ) client = with_plugin(client, plugin) - task_queue = f"agent-framework-multi-agent-{uuid.uuid4()}" + task_queue = f"microsoft-agent-framework-multi-agent-{uuid.uuid4()}" async with Worker( client, @@ -47,7 +47,7 @@ async def test_multi_agent(client: Client) -> None: handle = await client.start_workflow( MultiAgentWorkflow.run, "durable execution", - id=f"agent-framework-multi-agent-{uuid.uuid4()}", + id=f"microsoft-agent-framework-multi-agent-{uuid.uuid4()}", task_queue=task_queue, ) result = await handle.result() diff --git a/tests/agent_framework_plugin/open_telemetry_test.py b/tests/microsoft_agent_framework_plugin/open_telemetry_test.py similarity index 85% rename from tests/agent_framework_plugin/open_telemetry_test.py rename to tests/microsoft_agent_framework_plugin/open_telemetry_test.py index 6bbcf88d..e912730b 100644 --- a/tests/agent_framework_plugin/open_telemetry_test.py +++ b/tests/microsoft_agent_framework_plugin/open_telemetry_test.py @@ -10,9 +10,9 @@ from temporalio.contrib.opentelemetry import OpenTelemetryPlugin, create_tracer_provider from temporalio.worker import Worker -from agent_framework_plugin.open_telemetry.workflow import TracedWorkflow -from tests.agent_framework_plugin._fake_chat_client import scripted_plugin -from tests.agent_framework_plugin.helpers import ( +from microsoft_agent_framework_plugin.open_telemetry.workflow import TracedWorkflow +from tests.microsoft_agent_framework_plugin._fake_chat_client import scripted_plugin +from tests.microsoft_agent_framework_plugin.helpers import ( INVOKE_MODEL, count_scheduled_activities, ) @@ -37,7 +37,7 @@ async def test_model_activity_span_joins_the_trace( plugin, ] client = Client(**config) - task_queue = f"agent-framework-open-telemetry-{uuid.uuid4()}" + task_queue = f"microsoft-agent-framework-open-telemetry-{uuid.uuid4()}" async with Worker( client, @@ -50,7 +50,7 @@ async def test_model_activity_span_joins_the_trace( handle = await client.start_workflow( TracedWorkflow.run, "What is durable execution?", - id=f"agent-framework-open-telemetry-{uuid.uuid4()}", + id=f"microsoft-agent-framework-open-telemetry-{uuid.uuid4()}", task_queue=task_queue, ) result = await handle.result() diff --git a/tests/agent_framework_plugin/streaming_test.py b/tests/microsoft_agent_framework_plugin/streaming_test.py similarity index 84% rename from tests/agent_framework_plugin/streaming_test.py rename to tests/microsoft_agent_framework_plugin/streaming_test.py index a3c2feae..cfdf7af8 100644 --- a/tests/agent_framework_plugin/streaming_test.py +++ b/tests/microsoft_agent_framework_plugin/streaming_test.py @@ -7,9 +7,12 @@ from temporalio.contrib.workflow_streams import WorkflowStreamClient from temporalio.worker import Worker -from agent_framework_plugin.streaming.workflow import STREAMING_TOPIC, StreamingWorkflow -from tests.agent_framework_plugin._fake_chat_client import scripted_plugin -from tests.agent_framework_plugin.helpers import ( +from microsoft_agent_framework_plugin.streaming.workflow import ( + STREAMING_TOPIC, + StreamingWorkflow, +) +from tests.microsoft_agent_framework_plugin._fake_chat_client import scripted_plugin +from tests.microsoft_agent_framework_plugin.helpers import ( INVOKE_MODEL, INVOKE_MODEL_STREAMING, count_scheduled_activities, @@ -21,8 +24,8 @@ async def test_streaming(client: Client) -> None: expected = "One. Two. Three. Four. Five. That is the whole count." plugin, _ = scripted_plugin([expected]) client = with_plugin(client, plugin) - task_queue = f"agent-framework-streaming-{uuid.uuid4()}" - workflow_id = f"agent-framework-streaming-{uuid.uuid4()}" + task_queue = f"microsoft-agent-framework-streaming-{uuid.uuid4()}" + workflow_id = f"microsoft-agent-framework-streaming-{uuid.uuid4()}" async with Worker( client, diff --git a/tests/agent_framework_plugin/structured_output_test.py b/tests/microsoft_agent_framework_plugin/structured_output_test.py similarity index 77% rename from tests/agent_framework_plugin/structured_output_test.py rename to tests/microsoft_agent_framework_plugin/structured_output_test.py index bf410ad6..aa60932f 100644 --- a/tests/agent_framework_plugin/structured_output_test.py +++ b/tests/microsoft_agent_framework_plugin/structured_output_test.py @@ -3,12 +3,12 @@ from temporalio.client import Client from temporalio.worker import Worker -from agent_framework_plugin.structured_output.workflow import ( +from microsoft_agent_framework_plugin.structured_output.workflow import ( PersonInfo, StructuredOutputWorkflow, ) -from tests.agent_framework_plugin._fake_chat_client import scripted_plugin -from tests.agent_framework_plugin.helpers import ( +from tests.microsoft_agent_framework_plugin._fake_chat_client import scripted_plugin +from tests.microsoft_agent_framework_plugin.helpers import ( INVOKE_MODEL, count_scheduled_activities, with_plugin, @@ -20,7 +20,7 @@ async def test_structured_output(client: Client) -> None: ['{"name": "John Smith", "age": 30, "occupation": "software engineer"}'] ) client = with_plugin(client, plugin) - task_queue = f"agent-framework-structured-output-{uuid.uuid4()}" + task_queue = f"microsoft-agent-framework-structured-output-{uuid.uuid4()}" async with Worker( client, @@ -31,7 +31,7 @@ async def test_structured_output(client: Client) -> None: handle = await client.start_workflow( StructuredOutputWorkflow.run, "John Smith is a 30 year-old software engineer.", - id=f"agent-framework-structured-output-{uuid.uuid4()}", + id=f"microsoft-agent-framework-structured-output-{uuid.uuid4()}", task_queue=task_queue, ) result = await handle.result() diff --git a/tests/agent_framework_plugin/tools_test.py b/tests/microsoft_agent_framework_plugin/tools_test.py similarity index 82% rename from tests/agent_framework_plugin/tools_test.py rename to tests/microsoft_agent_framework_plugin/tools_test.py index 28bfd5a7..faefc409 100644 --- a/tests/agent_framework_plugin/tools_test.py +++ b/tests/microsoft_agent_framework_plugin/tools_test.py @@ -3,13 +3,13 @@ from temporalio.client import Client from temporalio.worker import Worker -from agent_framework_plugin.tools.workflow import ToolsWorkflow, fetch_weather -from tests.agent_framework_plugin._fake_chat_client import ( +from microsoft_agent_framework_plugin.tools.workflow import ToolsWorkflow, fetch_weather +from tests.microsoft_agent_framework_plugin._fake_chat_client import ( function_call_response, scripted_plugin, text_response, ) -from tests.agent_framework_plugin.helpers import ( +from tests.microsoft_agent_framework_plugin.helpers import ( INVOKE_MODEL, count_scheduled_activities, function_results, @@ -30,7 +30,7 @@ async def test_tools(client: Client) -> None: ] ) client = with_plugin(client, plugin) - task_queue = f"agent-framework-tools-{uuid.uuid4()}" + task_queue = f"microsoft-agent-framework-tools-{uuid.uuid4()}" async with Worker( client, @@ -42,7 +42,7 @@ async def test_tools(client: Client) -> None: handle = await client.start_workflow( ToolsWorkflow.run, "Count the R's in strawberry and fetch the weather in San Francisco.", - id=f"agent-framework-tools-{uuid.uuid4()}", + id=f"microsoft-agent-framework-tools-{uuid.uuid4()}", task_queue=task_queue, ) result = await handle.result() diff --git a/uv.lock b/uv.lock index d345bd7d..231d97d3 100644 --- a/uv.lock +++ b/uv.lock @@ -4851,16 +4851,6 @@ pydantic = [ { name = "pydantic" }, ] -[[package]] -name = "temporalio-agent-framework" -version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python#509bfab4ffc16410ce28aa4c092c8c15853b0874" } -dependencies = [ - { name = "agent-framework-core" }, - { name = "pydantic" }, - { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" } }, -] - [[package]] name = "temporalio-claude-agent-sdk" version = "0.0.2" @@ -4994,6 +4984,16 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/e6/95dc7eba97dfa3c821fb2931cf367632d1b303e7370d65ec2e541d434937/temporalio_langsmith-0.1.1-py3-none-any.whl", hash = "sha256:74a71f4ceb59eb285007dd09cdb83f88d670ee8384fe3ad6e873f2afcb7bc938", size = 214523, upload-time = "2026-10-28T18:55:43.142Z" }, ] +[[package]] +name = "temporalio-microsoft-agent-framework" +version = "0.0.0" +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#d20bc0c83d7e0009842ce1a3368d789b08767dd6" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "pydantic" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" } }, +] + [[package]] name = "temporalio-openai-agents" version = "1.0.0" @@ -5024,12 +5024,6 @@ dependencies = [ ] [package.dev-dependencies] -agent-framework = [ - { name = "agent-framework-openai", marker = "python_full_version >= '3.11'" }, - { name = "opentelemetry-exporter-otlp-proto-grpc", marker = "python_full_version >= '3.11'" }, - { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, extra = ["opentelemetry"], marker = "python_full_version >= '3.11'" }, - { name = "temporalio-agent-framework", marker = "python_full_version >= '3.11'" }, -] bedrock = [ { name = "boto3" }, ] @@ -5125,6 +5119,12 @@ langsmith-tracing = [ litellm = [ { name = "litellm" }, ] +microsoft-agent-framework = [ + { name = "agent-framework-openai", marker = "python_full_version >= '3.11'" }, + { name = "opentelemetry-exporter-otlp-proto-grpc", marker = "python_full_version >= '3.11'" }, + { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" }, extra = ["opentelemetry"], marker = "python_full_version >= '3.11'" }, + { name = "temporalio-microsoft-agent-framework", marker = "python_full_version >= '3.11'" }, +] nexus = [ { name = "nexus-rpc", version = "1.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "nexus-rpc", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -5167,12 +5167,6 @@ requires-dist = [ ] [package.metadata.requires-dev] -agent-framework = [ - { name = "agent-framework-openai", marker = "python_full_version >= '3.11'", specifier = ">=1.17,<2" }, - { name = "opentelemetry-exporter-otlp-proto-grpc", marker = "python_full_version >= '3.11'", specifier = ">=1.30.0,<2" }, - { name = "temporalio", extras = ["opentelemetry"], marker = "python_full_version >= '3.11'", specifier = ">=1.36.0,<2" }, - { name = "temporalio-agent-framework", marker = "python_full_version >= '3.11'", git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fagent_framework&branch=dabh%2Fagent-framework-python" }, -] bedrock = [{ name = "boto3", specifier = ">=1.34.92,<2" }] claude-agent-sdk = [ { name = "opentelemetry-exporter-otlp-proto-grpc", specifier = ">=1.30.0,<2" }, @@ -5248,6 +5242,12 @@ langsmith-tracing = [ { name = "temporalio-langsmith", specifier = ">=0.1.0" }, ] litellm = [{ name = "litellm", specifier = ">=1.85.0,<2" }] +microsoft-agent-framework = [ + { name = "agent-framework-openai", marker = "python_full_version >= '3.11'", specifier = ">=1.17,<2" }, + { name = "opentelemetry-exporter-otlp-proto-grpc", marker = "python_full_version >= '3.11'", specifier = ">=1.30.0,<2" }, + { name = "temporalio", extras = ["opentelemetry"], marker = "python_full_version >= '3.11'", specifier = ">=1.36.0,<2" }, + { name = "temporalio-microsoft-agent-framework", marker = "python_full_version >= '3.11'", git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python" }, +] nexus = [{ name = "nexus-rpc", specifier = ">=1.1.0,<2" }] open-telemetry = [ { name = "opentelemetry-exporter-otlp-proto-grpc" }, From cca8634ca9f59fe3bcb9f8fc17b8152dac3be28e Mon Sep 17 00:00:00 2001 From: DABH Date: Fri, 13 Nov 2026 11:50:21 -0600 Subject: [PATCH 19/26] Pin the plugin to d374e26c Both locks move temporalio-microsoft-agent-framework from d20bc0c8 to d374e26c on dabh/agent-framework-python; the only change in each lock is that commit hash. --- microsoft_agent_framework_plugin/mcp/uv.lock | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/microsoft_agent_framework_plugin/mcp/uv.lock b/microsoft_agent_framework_plugin/mcp/uv.lock index 6fdda106..2971816a 100644 --- a/microsoft_agent_framework_plugin/mcp/uv.lock +++ b/microsoft_agent_framework_plugin/mcp/uv.lock @@ -1619,7 +1619,7 @@ wheels = [ [[package]] name = "temporalio-microsoft-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#d20bc0c83d7e0009842ce1a3368d789b08767dd6" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#d374e26c01c907ae08bdceccef346b42d3fd3dec" } dependencies = [ { name = "agent-framework-core" }, { name = "pydantic" }, diff --git a/uv.lock b/uv.lock index 231d97d3..007b46b5 100644 --- a/uv.lock +++ b/uv.lock @@ -4987,7 +4987,7 @@ wheels = [ [[package]] name = "temporalio-microsoft-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#d20bc0c83d7e0009842ce1a3368d789b08767dd6" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#d374e26c01c907ae08bdceccef346b42d3fd3dec" } dependencies = [ { name = "agent-framework-core" }, { name = "pydantic" }, From 0b94d388f67c61ec80ad529bf5554a8a60629c57 Mon Sep 17 00:00:00 2001 From: DABH Date: Fri, 13 Nov 2026 11:50:21 -0600 Subject: [PATCH 20/26] Let the streaming topic route the model call Since d374e26c a client with streaming_topic runs every model call as the streaming Activity, with or without stream=True, so the sample calls agent.run(prompt) and the README no longer says a topic needs stream=True. The test's expectations were already met: one invoke_model_streaming Activity and every update at the subscriber. --- .../streaming/README.md | 4 ++-- .../streaming/workflow.py | 14 ++++++++------ .../streaming_test.py | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/microsoft_agent_framework_plugin/streaming/README.md b/microsoft_agent_framework_plugin/streaming/README.md index 6cc381f8..142d6405 100644 --- a/microsoft_agent_framework_plugin/streaming/README.md +++ b/microsoft_agent_framework_plugin/streaming/README.md @@ -1,11 +1,11 @@ # Streaming -Model output streamed to a subscriber outside the Workflow. `TemporalChatClient(streaming_topic="events")` makes a streamed call run as the `microsoft_agent_framework.invoke_model_streaming` Activity, which publishes every `ChatResponseUpdate` to the `events` topic of a `WorkflowStream` hosted on the Workflow, batched every 100 milliseconds (`streaming_batch_interval`). The Workflow still gets the whole response, so the durable result is the same as without streaming. +Model output streamed to a subscriber outside the Workflow. `TemporalChatClient(streaming_topic="events")` makes every model call of that client run as the `microsoft_agent_framework.invoke_model_streaming` Activity, with or without `stream=True`. The Activity publishes every `ChatResponseUpdate` to the `events` topic of a `WorkflowStream` hosted on the Workflow, in batches every 100 milliseconds (`streaming_batch_interval`) or every 256 updates, whichever comes first. The Workflow still gets the whole response, so the durable result is the same as without a topic. ## What this sample demonstrates - A `WorkflowStream()` hosted on the Workflow -- `agent.run(prompt, stream=True)` with `streaming_topic` set; without `stream=True` the call runs as the plain model Activity and publishes nothing +- `streaming_topic` on the client: a plain `agent.run(prompt)` then runs as the streaming Activity and publishes; `stream=True` still works and lets the Workflow read the updates too - `WorkflowStreamClient.subscribe(["events"])` in the starter, decoding each item with `ChatResponseUpdate.from_dict` ## Running the sample diff --git a/microsoft_agent_framework_plugin/streaming/workflow.py b/microsoft_agent_framework_plugin/streaming/workflow.py index e6b77e12..5e14909d 100644 --- a/microsoft_agent_framework_plugin/streaming/workflow.py +++ b/microsoft_agent_framework_plugin/streaming/workflow.py @@ -1,9 +1,10 @@ """Stream model output to a subscriber outside the Workflow. -``TemporalChatClient(streaming_topic="events")`` makes the model Activity -publish every update to the ``events`` topic of a ``WorkflowStream`` hosted on -the Workflow, batched every 100 milliseconds. The Workflow still gets the whole -response, so the result is the same as without streaming. +``TemporalChatClient(streaming_topic="events")`` makes every model call of that +client run as the streaming Activity, which publishes each update to the +``events`` topic of a ``WorkflowStream`` hosted on the Workflow, batched every +100 milliseconds. The Workflow still gets the whole response, so the result is +the same as without a topic. """ # @@@SNIPSTART python-microsoft-agent-framework-streaming-workflow @@ -33,8 +34,9 @@ async def run(self, prompt: str) -> str: ), instructions="You are a concise assistant.", ) - # stream=True selects the streaming Activity; the final response is the same. - response = await agent.run(prompt, stream=True).get_final_response() + # With a topic set, this call runs as the streaming Activity and + # publishes its updates, with or without stream=True. + response = await agent.run(prompt) return response.text diff --git a/tests/microsoft_agent_framework_plugin/streaming_test.py b/tests/microsoft_agent_framework_plugin/streaming_test.py index cfdf7af8..8490bd37 100644 --- a/tests/microsoft_agent_framework_plugin/streaming_test.py +++ b/tests/microsoft_agent_framework_plugin/streaming_test.py @@ -67,7 +67,7 @@ async def consume() -> None: # ...and the same text reached the subscriber in more than one update. assert len(chunks) > 1 assert "".join(chunks) == expected - # stream=True ran the streaming Activity, not the plain one. + # The topic routed the call to the streaming Activity, not the plain one. counts = await count_scheduled_activities(handle) assert counts == {INVOKE_MODEL_STREAMING: 1} assert counts[INVOKE_MODEL] == 0 From 45e76fe0efcee853e00a252c656d502bc53d9695 Mon Sep 17 00:00:00 2001 From: DABH Date: Fri, 13 Nov 2026 13:11:10 -0600 Subject: [PATCH 21/26] Pin the plugin to 8b472d21 Both locks move temporalio-microsoft-agent-framework from d374e26c to 8b472d21 on dabh/agent-framework-python. Each lock changes the git revision line and adds opentelemetry-api to the plugin's dependency list, which the plugin now declares; no other package moves. --- microsoft_agent_framework_plugin/mcp/uv.lock | 3 ++- uv.lock | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/microsoft_agent_framework_plugin/mcp/uv.lock b/microsoft_agent_framework_plugin/mcp/uv.lock index 2971816a..1cdf7ec3 100644 --- a/microsoft_agent_framework_plugin/mcp/uv.lock +++ b/microsoft_agent_framework_plugin/mcp/uv.lock @@ -1619,9 +1619,10 @@ wheels = [ [[package]] name = "temporalio-microsoft-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#d374e26c01c907ae08bdceccef346b42d3fd3dec" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#8b472d211bec0d246b4f0978ea0feef75c128e55" } dependencies = [ { name = "agent-framework-core" }, + { name = "opentelemetry-api" }, { name = "pydantic" }, { name = "temporalio" }, ] diff --git a/uv.lock b/uv.lock index 007b46b5..ef460fd3 100644 --- a/uv.lock +++ b/uv.lock @@ -4987,9 +4987,10 @@ wheels = [ [[package]] name = "temporalio-microsoft-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#d374e26c01c907ae08bdceccef346b42d3fd3dec" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#8b472d211bec0d246b4f0978ea0feef75c128e55" } dependencies = [ { name = "agent-framework-core" }, + { name = "opentelemetry-api" }, { name = "pydantic" }, { name = "temporalio", version = "1.36.0", source = { registry = "https://pypi.org/simple" } }, ] From e711f1ded7a0821100cbbb760d703ec29970ff94 Mon Sep 17 00:00:00 2001 From: DABH Date: Fri, 13 Nov 2026 13:11:10 -0600 Subject: [PATCH 22/26] Test turns and end_chat sent while a turn is running A turn sent during another turn's model call gets its reply from the run the server delivers it to, and an end_chat sent during a turn ends the chat after that turn. The sample's turns are Updates, so nothing waits in Workflow fields at the handoff; the README and the module docstring say so, and the new test drives three turns and the end_chat across a continue-as-new chain with a slow scripted client. --- .../continue_as_new/README.md | 2 +- .../continue_as_new/workflow.py | 4 + .../continue_as_new_test.py | 176 +++++++++++++++++- 3 files changed, 173 insertions(+), 9 deletions(-) diff --git a/microsoft_agent_framework_plugin/continue_as_new/README.md b/microsoft_agent_framework_plugin/continue_as_new/README.md index d0640311..cb29f5da 100644 --- a/microsoft_agent_framework_plugin/continue_as_new/README.md +++ b/microsoft_agent_framework_plugin/continue_as_new/README.md @@ -2,7 +2,7 @@ A chat that runs as long as you like. Each user turn is an Update, so the caller gets the reply from the same call. History grows with every turn, and `workflow.info().is_continue_as_new_suggested()` turns `True` once the Temporal Service decides it is large enough. The Workflow waits on that or on the `end_chat` Signal, lets in-flight Updates finish with `workflow.all_handlers_finished`, and hands `session.to_dict()` to a fresh run, which restores it with `AgentSession.from_dict`. -What crosses the boundary: the serialized session, which holds the transcript, any pending approval state and the provider's conversation id. What does not: the `Agent` object, provider connections and spans; the new run builds the agent again. +What crosses the boundary: the serialized session, which holds the transcript, any pending approval state and the provider's conversation id. What does not: the `Agent` object, provider connections and spans; the new run builds the agent again. Nothing waits in Workflow fields at the handoff: a `turn` is an Update, so it completes in the run that accepted it and returns its reply from there, `workflow.all_handlers_finished` lets an accepted turn finish before the handoff, and an `end_chat` that arrives during a turn ends the chat after that turn instead of being lost. ## What this sample demonstrates diff --git a/microsoft_agent_framework_plugin/continue_as_new/workflow.py b/microsoft_agent_framework_plugin/continue_as_new/workflow.py index d486a421..e8a2e911 100644 --- a/microsoft_agent_framework_plugin/continue_as_new/workflow.py +++ b/microsoft_agent_framework_plugin/continue_as_new/workflow.py @@ -5,6 +5,10 @@ finish and hands the serialized session to a fresh run. ``session.to_dict()`` carries the transcript and Agent Framework's own session state across the boundary; the ``Agent`` object does not cross and is built again in the new run. +Nothing waits in Workflow fields at the handoff: a ``turn`` is an Update, so it +completes in the run that accepted it, ``workflow.all_handlers_finished`` lets +an accepted turn finish before the handoff, and an ``end_chat`` that arrives +during a turn ends the chat after that turn. """ # @@@SNIPSTART python-microsoft-agent-framework-continue-as-new-workflow diff --git a/tests/microsoft_agent_framework_plugin/continue_as_new_test.py b/tests/microsoft_agent_framework_plugin/continue_as_new_test.py index 56af3a43..a7132c6f 100644 --- a/tests/microsoft_agent_framework_plugin/continue_as_new_test.py +++ b/tests/microsoft_agent_framework_plugin/continue_as_new_test.py @@ -1,7 +1,13 @@ import asyncio import uuid +from collections.abc import Awaitable, Mapping, Sequence +from typing import Any -from temporalio.client import Client, WorkflowExecutionStatus +from agent_framework import ChatResponse, ChatResponseUpdate, Message, ResponseStream +from temporalio.api.enums.v1 import EventType +from temporalio.client import Client, WorkflowExecutionStatus, WorkflowHandle +from temporalio.microsoft_agent_framework import MicrosoftAgentFrameworkPlugin +from temporalio.microsoft_agent_framework.testing import FakeChatClient, ScriptEntry from temporalio.testing import WorkflowEnvironment from temporalio.worker import Worker @@ -9,12 +15,22 @@ ChatInput, ChatWorkflow, ) -from tests.microsoft_agent_framework_plugin._fake_chat_client import scripted_plugin +from tests.microsoft_agent_framework_plugin._fake_chat_client import ( + MODEL, + scripted_plugin, +) from tests.microsoft_agent_framework_plugin.helpers import ( count_scheduled_activities, with_plugin, ) +# The server suggests continue-as-new once history passes a threshold (4096 +# events by default). Lower it so one turn trips the suggestion. +SUGGEST_EARLY = [ + "--dynamic-config-value", + "limit.historyCount.suggestContinueAsNew=5", +] + async def test_continue_as_new_carries_the_session() -> None: """Drive a chat across a continue-as-new boundary. @@ -25,13 +41,8 @@ async def test_continue_as_new_carries_the_session() -> None: """ plugin, factory = scripted_plugin(["First reply.", "Second reply."]) - # The server suggests continue-as-new once history passes a threshold - # (4096 events by default). Lower it so one turn trips the suggestion. async with await WorkflowEnvironment.start_local( - dev_server_extra_args=[ - "--dynamic-config-value", - "limit.historyCount.suggestContinueAsNew=5", - ] + dev_server_extra_args=SUGGEST_EARLY ) as env: client = with_plugin(env.client, plugin) task_queue = f"microsoft-agent-framework-chat-{uuid.uuid4()}" @@ -92,3 +103,152 @@ async def test_continue_as_new_carries_the_session() -> None: "First reply.", "How are you?", ] + + +class _SlowFakeChatClient(FakeChatClient): + """A scripted client that takes ``delay`` seconds per call, so a turn is in progress for a while.""" + + def __init__(self, responses: Sequence[ScriptEntry], *, delay: float) -> None: + super().__init__(responses) + self.delay = delay + + def _inner_get_response( + self, + *, + messages: Sequence[Message], + stream: bool, + options: Mapping[str, Any], + **kwargs: Any, + ) -> Awaitable[ChatResponse] | ResponseStream[ChatResponseUpdate, ChatResponse]: + inner = super()._inner_get_response( + messages=messages, stream=stream, options=options, **kwargs + ) + if isinstance(inner, ResponseStream): + return inner + + async def slow() -> ChatResponse: + await asyncio.sleep(self.delay) + return await inner + + return slow() + + +async def _wait_for_model_calls_started(handle: WorkflowHandle, count: int) -> None: + """Return once the run's history shows ``count`` model Activities started.""" + for _ in range(200): + started = 0 + async for event in handle.fetch_history_events(): + if event.event_type == EventType.EVENT_TYPE_ACTIVITY_TASK_STARTED: + started += 1 + if started >= count: + return + await asyncio.sleep(0.05) + raise AssertionError(f"model call {count} never started") + + +async def _run_chain(client: Client, workflow_id: str, first_run_id: str) -> list[str]: + """Return the run ids of a continue-as-new chain, first run first.""" + run_ids = [first_run_id] + while True: + handle = client.get_workflow_handle(workflow_id, run_id=run_ids[-1]) + next_run_id = None + async for event in handle.fetch_history_events(): + if ( + event.event_type + == EventType.EVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW + ): + attrs = event.workflow_execution_continued_as_new_event_attributes + next_run_id = attrs.new_execution_run_id + if not next_run_id: + return run_ids + run_ids.append(next_run_id) + + +async def test_messages_sent_during_a_turn_are_not_lost() -> None: + """A turn and an end_chat that arrive while a turn is running are honored. + + Each turn is an Update: it completes in the run that accepted it, and + `all_handlers_finished` lets an accepted turn finish before the handoff, so + a turn sent during another turn gets its reply from whichever run the + server delivers it to. The end_chat arrives during the last turn and ends + the chat after it. Every turn ran exactly once across the chain. + """ + slow = _SlowFakeChatClient( + ["First reply.", "Second reply.", "Third reply."], delay=1.5 + ) + plugin = MicrosoftAgentFrameworkPlugin(chat_clients={MODEL: lambda: slow}) + + async with await WorkflowEnvironment.start_local( + dev_server_extra_args=SUGGEST_EARLY + ) as env: + client = with_plugin(env.client, plugin) + task_queue = f"agent-framework-chat-{uuid.uuid4()}" + workflow_id = f"agent-framework-chat-{uuid.uuid4()}" + + async with Worker( + client, + task_queue=task_queue, + workflows=[ChatWorkflow], + max_cached_workflows=0, + ): + handle = await client.start_workflow( + ChatWorkflow.run, + ChatInput(), + id=workflow_id, + task_queue=task_queue, + ) + first_run_id = handle.result_run_id + assert first_run_id is not None + first_run = client.get_workflow_handle(workflow_id, run_id=first_run_id) + + first = asyncio.create_task( + handle.execute_update(ChatWorkflow.turn, "Hello") + ) + await _wait_for_model_calls_started(first_run, 1) + # Sent while the first turn's model call is running. + second = asyncio.create_task( + handle.execute_update(ChatWorkflow.turn, "And again?") + ) + assert await first == "First reply." + assert await second == "Second reply." + + # One turn is enough history to hand off; the chain has moved on. + for _ in range(100): + status = (await first_run.describe()).status + if status == WorkflowExecutionStatus.CONTINUED_AS_NEW: + break + await asyncio.sleep(0.1) + else: + raise AssertionError("the workflow did not continue as new") + + latest = client.get_workflow_handle(workflow_id) + third = asyncio.create_task( + latest.execute_update(ChatWorkflow.turn, "Last one") + ) + latest_run = client.get_workflow_handle( + workflow_id, run_id=(await latest.describe()).run_id + ) + await _wait_for_model_calls_started(latest_run, 1) + # Sent while the third turn's model call is running. + await latest.signal(ChatWorkflow.end_chat) + assert await third == "Third reply." + # The chat ends after the turn that was running. + await latest.result() + + transcript = await latest.query(ChatWorkflow.transcript) + assert [m["text"] for m in transcript] == [ + "Hello", + "First reply.", + "And again?", + "Second reply.", + "Last one", + "Third reply.", + ] + # Three model calls across the whole chain: nothing ran twice or not at all. + run_ids = await _run_chain(client, workflow_id, first_run_id) + assert len(run_ids) >= 2 + calls = 0 + for run_id in run_ids: + run = client.get_workflow_handle(workflow_id, run_id=run_id) + calls += (await count_scheduled_activities(run)).total() + assert calls == 3 From 485c4061ab9354a90d20dec5b28d3297697596b1 Mon Sep 17 00:00:00 2001 From: DABH Date: Fri, 13 Nov 2026 13:13:02 -0600 Subject: [PATCH 23/26] Pin the plugin to a453fa6c Both locks move temporalio-microsoft-agent-framework from 8b472d21 to a453fa6c on dabh/agent-framework-python. The mcp project's lock also moves temporalio-mcp from 0.3.0 to 0.4.0, which the plugin now requires; no other package moves. --- microsoft_agent_framework_plugin/mcp/uv.lock | 8 ++++---- uv.lock | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/microsoft_agent_framework_plugin/mcp/uv.lock b/microsoft_agent_framework_plugin/mcp/uv.lock index 1cdf7ec3..e7299c3a 100644 --- a/microsoft_agent_framework_plugin/mcp/uv.lock +++ b/microsoft_agent_framework_plugin/mcp/uv.lock @@ -1603,7 +1603,7 @@ wheels = [ [[package]] name = "temporalio-mcp" -version = "0.3.0" +version = "0.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mcp" }, @@ -1611,15 +1611,15 @@ dependencies = [ { name = "pydantic" }, { name = "temporalio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/d2/6b1a7cfc08b6554862e241526cee3ad5a91bfe14baa854b6cf20c9885781/temporalio_mcp-0.3.0.tar.gz", hash = "sha256:c42ab0ab878fb2b4105822b28b199a438c1c8c9f81051a847234eb517b4ce408", size = 17497, upload-time = "2026-11-09T20:32:26.624Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/81/c44d7de24ebb18feb0fdb098866f2a80c06975fc76e3d94aacc902a2f6a5/temporalio_mcp-0.4.0.tar.gz", hash = "sha256:442fee7dcb7b44e5479d61f4b704cdd1d3c4fa5644853d04a087733056846729", size = 17878, upload-time = "2026-11-13T17:34:21.839Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/26/22398c7a39f43999167e8800f1860b867b014fea3903c8d6c2109cbc5a82/temporalio_mcp-0.3.0-py3-none-any.whl", hash = "sha256:90b030e50382a246546528dcf85557b3428837dd28e6f13675fa72ade021777b", size = 21388, upload-time = "2026-11-09T20:32:25.297Z" }, + { url = "https://files.pythonhosted.org/packages/ff/16/a4828a00ab56e9038742c809d3d3a41d018a19b3eb720c298cfda5bd1e17/temporalio_mcp-0.4.0-py3-none-any.whl", hash = "sha256:ab8295e03f1db7a9daeb67101f9e7a1182cab520ba3f9cbb52160cdef539760a", size = 21767, upload-time = "2026-11-13T17:34:20.676Z" }, ] [[package]] name = "temporalio-microsoft-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#8b472d211bec0d246b4f0978ea0feef75c128e55" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#a453fa6c799e9e8f7d1af2b918afa85e0539193c" } dependencies = [ { name = "agent-framework-core" }, { name = "opentelemetry-api" }, diff --git a/uv.lock b/uv.lock index ef460fd3..4d3f282d 100644 --- a/uv.lock +++ b/uv.lock @@ -4987,7 +4987,7 @@ wheels = [ [[package]] name = "temporalio-microsoft-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#8b472d211bec0d246b4f0978ea0feef75c128e55" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#a453fa6c799e9e8f7d1af2b918afa85e0539193c" } dependencies = [ { name = "agent-framework-core" }, { name = "opentelemetry-api" }, From 6fad07a42fd7671d013077100f06d4d8e8434852 Mon Sep 17 00:00:00 2001 From: DABH Date: Sat, 14 Nov 2026 00:09:30 -0600 Subject: [PATCH 24/26] Pin the plugin to 54998df8 Both locks move temporalio-microsoft-agent-framework from a453fa6c to 54998df8 on dabh/agent-framework-python; the only change in each lock is that commit hash. agent-framework-core stays at 1.26.0 here: this repository has no cooldown, and the plugin's floor allows it. --- microsoft_agent_framework_plugin/mcp/uv.lock | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/microsoft_agent_framework_plugin/mcp/uv.lock b/microsoft_agent_framework_plugin/mcp/uv.lock index e7299c3a..bbeca753 100644 --- a/microsoft_agent_framework_plugin/mcp/uv.lock +++ b/microsoft_agent_framework_plugin/mcp/uv.lock @@ -1619,7 +1619,7 @@ wheels = [ [[package]] name = "temporalio-microsoft-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#a453fa6c799e9e8f7d1af2b918afa85e0539193c" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#54998df812f930fffccb97d58d00ee74cdb65f70" } dependencies = [ { name = "agent-framework-core" }, { name = "opentelemetry-api" }, diff --git a/uv.lock b/uv.lock index 4d3f282d..7423426c 100644 --- a/uv.lock +++ b/uv.lock @@ -4987,7 +4987,7 @@ wheels = [ [[package]] name = "temporalio-microsoft-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#a453fa6c799e9e8f7d1af2b918afa85e0539193c" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#54998df812f930fffccb97d58d00ee74cdb65f70" } dependencies = [ { name = "agent-framework-core" }, { name = "opentelemetry-api" }, From 9ac1b812c169d9b94352948991c0a81aa5c31f1f Mon Sep 17 00:00:00 2001 From: DABH Date: Sat, 14 Nov 2026 00:14:26 -0600 Subject: [PATCH 25/26] Pin the plugin to its merge commit on main ai-integrations#522 merged as e7303bbc, the same tree as 54998df8, and the branch was deleted, so both [tool.uv.sources] entries now name the commit with rev instead of the branch. Each lock changes its source line and the dependency reference accordingly; no package moves. --- microsoft_agent_framework_plugin/mcp/pyproject.toml | 2 +- microsoft_agent_framework_plugin/mcp/uv.lock | 4 ++-- pyproject.toml | 2 +- uv.lock | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/microsoft_agent_framework_plugin/mcp/pyproject.toml b/microsoft_agent_framework_plugin/mcp/pyproject.toml index c7f26093..88a7e8f0 100644 --- a/microsoft_agent_framework_plugin/mcp/pyproject.toml +++ b/microsoft_agent_framework_plugin/mcp/pyproject.toml @@ -22,7 +22,7 @@ dev = [ [tool.uv.sources] # Temporary while the plugin is unreleased: remove once temporalio-microsoft-agent-framework is on PyPI. -temporalio-microsoft-agent-framework = { git = "https://github.com/temporalio/ai-integrations", subdirectory = "python/microsoft_agent_framework", branch = "dabh/agent-framework-python" } +temporalio-microsoft-agent-framework = { git = "https://github.com/temporalio/ai-integrations", subdirectory = "python/microsoft_agent_framework", rev = "e7303bbc65c4507106fb69f1cca1e978d88b6955" } [tool.poe.tasks] format = [ diff --git a/microsoft_agent_framework_plugin/mcp/uv.lock b/microsoft_agent_framework_plugin/mcp/uv.lock index bbeca753..f2d19341 100644 --- a/microsoft_agent_framework_plugin/mcp/uv.lock +++ b/microsoft_agent_framework_plugin/mcp/uv.lock @@ -1619,7 +1619,7 @@ wheels = [ [[package]] name = "temporalio-microsoft-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#54998df812f930fffccb97d58d00ee74cdb65f70" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&rev=e7303bbc65c4507106fb69f1cca1e978d88b6955#e7303bbc65c4507106fb69f1cca1e978d88b6955" } dependencies = [ { name = "agent-framework-core" }, { name = "opentelemetry-api" }, @@ -1655,7 +1655,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "agent-framework-openai", marker = "python_full_version >= '3.11'", specifier = ">=1.17,<2" }, - { name = "temporalio-microsoft-agent-framework", extras = ["mcp"], marker = "python_full_version >= '3.11'", git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python" }, + { name = "temporalio-microsoft-agent-framework", extras = ["mcp"], marker = "python_full_version >= '3.11'", git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&rev=e7303bbc65c4507106fb69f1cca1e978d88b6955" }, ] [package.metadata.requires-dev] diff --git a/pyproject.toml b/pyproject.toml index 0af6ac1f..3408d215 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -125,7 +125,7 @@ constraint-dependencies = [ [tool.uv.sources] # Temporary while the plugin is unreleased: remove once temporalio-microsoft-agent-framework is on PyPI. -temporalio-microsoft-agent-framework = { git = "https://github.com/temporalio/ai-integrations", subdirectory = "python/microsoft_agent_framework", branch = "dabh/agent-framework-python" } +temporalio-microsoft-agent-framework = { git = "https://github.com/temporalio/ai-integrations", subdirectory = "python/microsoft_agent_framework", rev = "e7303bbc65c4507106fb69f1cca1e978d88b6955" } [tool.setuptools.packages.find] exclude = ["lambda_worker*"] diff --git a/uv.lock b/uv.lock index 7423426c..34424e22 100644 --- a/uv.lock +++ b/uv.lock @@ -4987,7 +4987,7 @@ wheels = [ [[package]] name = "temporalio-microsoft-agent-framework" version = "0.0.0" -source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python#54998df812f930fffccb97d58d00ee74cdb65f70" } +source = { git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&rev=e7303bbc65c4507106fb69f1cca1e978d88b6955#e7303bbc65c4507106fb69f1cca1e978d88b6955" } dependencies = [ { name = "agent-framework-core" }, { name = "opentelemetry-api" }, @@ -5247,7 +5247,7 @@ microsoft-agent-framework = [ { name = "agent-framework-openai", marker = "python_full_version >= '3.11'", specifier = ">=1.17,<2" }, { name = "opentelemetry-exporter-otlp-proto-grpc", marker = "python_full_version >= '3.11'", specifier = ">=1.30.0,<2" }, { name = "temporalio", extras = ["opentelemetry"], marker = "python_full_version >= '3.11'", specifier = ">=1.36.0,<2" }, - { name = "temporalio-microsoft-agent-framework", marker = "python_full_version >= '3.11'", git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&branch=dabh%2Fagent-framework-python" }, + { name = "temporalio-microsoft-agent-framework", marker = "python_full_version >= '3.11'", git = "https://github.com/temporalio/ai-integrations?subdirectory=python%2Fmicrosoft_agent_framework&rev=e7303bbc65c4507106fb69f1cca1e978d88b6955" }, ] nexus = [{ name = "nexus-rpc", specifier = ">=1.1.0,<2" }] open-telemetry = [ From df932db0815b7e80395bb4880d087f54bcc2fb76 Mon Sep 17 00:00:00 2001 From: DABH Date: Sat, 14 Nov 2026 12:19:10 -0600 Subject: [PATCH 26/26] Add plugins to the loaded client config on temporalio 1.36 On temporalio 1.36 the SDK's ClientConnectConfig carries plugins, so mypy rejects Client.connect(**config, plugins=[...]) as passing plugins twice. The SDK's load_client_connect_config docstring asks callers to combine their plugins with the config's plugins rather than pass them separately, and these 17 sites now do that and pass **config alone. The config is copied into a plain dict first because the Python 3.10 cell lints against temporalio 1.35, whose ClientConnectConfig has no plugins key. --- deepagents_plugin/external_storage/client.py | 6 +++-- langfuse_tracing/ticket_triage/starter.py | 12 +++++----- langfuse_tracing/ticket_triage/worker.py | 12 +++++----- langsmith_tracing/basic/starter.py | 9 ++++---- langsmith_tracing/chatbot/starter.py | 10 ++++----- .../basic/run_remote_image_workflow.py | 9 ++++---- .../handoffs/run_message_filter_workflow.py | 11 ++++------ openai_agents/mcp/run_file_system_worker.py | 22 +++++++++---------- openai_agents/mcp/run_file_system_workflow.py | 11 ++++------ .../run_memory_research_scratchpad_worker.py | 22 +++++++++---------- ...run_memory_research_scratchpad_workflow.py | 11 ++++------ openai_agents/mcp/run_prompt_server_worker.py | 22 +++++++++---------- .../mcp/run_prompt_server_workflow.py | 9 ++++---- openai_agents/mcp/run_sse_worker.py | 22 +++++++++---------- openai_agents/mcp/run_sse_workflow.py | 9 ++++---- .../mcp/run_streamable_http_worker.py | 22 +++++++++---------- .../mcp/run_streamable_http_workflow.py | 9 ++++---- 17 files changed, 109 insertions(+), 119 deletions(-) diff --git a/deepagents_plugin/external_storage/client.py b/deepagents_plugin/external_storage/client.py index dabad827..66ed2666 100644 --- a/deepagents_plugin/external_storage/client.py +++ b/deepagents_plugin/external_storage/client.py @@ -2,6 +2,7 @@ from collections.abc import AsyncIterator from contextlib import asynccontextmanager +from typing import Any import aioboto3 from langchain_core.messages import AIMessage @@ -24,7 +25,7 @@ async def connect_client( s3_endpoint: str = S3_ENDPOINT, ) -> AsyncIterator[Client]: """Keep the S3 client alive while Temporal reads and writes payloads.""" - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") if target_host is not None: config["target_host"] = target_host @@ -62,4 +63,5 @@ async def connect_client( ] ), ) - yield await Client.connect(**config, plugins=[plugin]) # type: ignore[misc] + config["plugins"] = [*config.get("plugins", []), plugin] + yield await Client.connect(**config) diff --git a/langfuse_tracing/ticket_triage/starter.py b/langfuse_tracing/ticket_triage/starter.py index 97d38fca..27f4c828 100644 --- a/langfuse_tracing/ticket_triage/starter.py +++ b/langfuse_tracing/ticket_triage/starter.py @@ -11,6 +11,7 @@ import asyncio import os import uuid +from typing import Any from opentelemetry import trace from temporalio.client import Client @@ -41,12 +42,13 @@ async def main() -> None: setup_tracing("ticket-triage-starter") - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[OpenTelemetryPlugin(add_temporal_spans=True)], - ) + config["plugins"] = [ + *config.get("plugins", []), + OpenTelemetryPlugin(add_temporal_spans=True), + ] + client = await Client.connect(**config) # A fresh workflow ID per run avoids Temporal workflow ID conflicts and # gives each run its own Langfuse session (langfuse.session.id on the root diff --git a/langfuse_tracing/ticket_triage/worker.py b/langfuse_tracing/ticket_triage/worker.py index aded0492..75cd792e 100644 --- a/langfuse_tracing/ticket_triage/worker.py +++ b/langfuse_tracing/ticket_triage/worker.py @@ -3,6 +3,7 @@ import argparse import asyncio import logging +from typing import Any from temporalio.client import Client from temporalio.contrib.opentelemetry import OpenTelemetryPlugin @@ -38,16 +39,17 @@ async def main() -> None: setup_tracing("ticket-triage-worker") flavor = instrument_openai() - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") # add_temporal_spans=True emits spans for Temporal operations (StartWorkflow, # RunWorkflow, RunActivity, HandleUpdate, ...) in addition to propagating # trace context across the client/workflow/activity boundaries. - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[OpenTelemetryPlugin(add_temporal_spans=True)], - ) + config["plugins"] = [ + *config.get("plugins", []), + OpenTelemetryPlugin(add_temporal_spans=True), + ] + client = await Client.connect(**config) worker = Worker( client, diff --git a/langsmith_tracing/basic/starter.py b/langsmith_tracing/basic/starter.py index e6b22754..6486a936 100644 --- a/langsmith_tracing/basic/starter.py +++ b/langsmith_tracing/basic/starter.py @@ -2,6 +2,7 @@ import asyncio import sys +from typing import Any from langsmith import traceable from temporalio.client import Client @@ -25,7 +26,7 @@ async def main() -> str: add_temporal_runs = "--add-temporal-runs" in sys.argv - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") plugin = LangSmithPlugin( @@ -33,10 +34,8 @@ async def main() -> str: add_temporal_runs=add_temporal_runs, ) - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[plugin], - ) + config["plugins"] = [*config.get("plugins", []), plugin] + client = await Client.connect(**config) result = await client.execute_workflow( BasicLLMWorkflow.run, diff --git a/langsmith_tracing/chatbot/starter.py b/langsmith_tracing/chatbot/starter.py index 5e8cb07c..cc35db36 100644 --- a/langsmith_tracing/chatbot/starter.py +++ b/langsmith_tracing/chatbot/starter.py @@ -4,6 +4,7 @@ import readline # noqa: F401 — enables input() line editing import sys import uuid +from typing import Any from langsmith import traceable from temporalio.client import Client @@ -19,7 +20,7 @@ async def main(): add_temporal_runs = "--add-temporal-runs" in sys.argv - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") plugin = LangSmithPlugin( @@ -27,11 +28,8 @@ async def main(): add_temporal_runs=add_temporal_runs, ) - client = await Client.connect( # type: ignore[misc] - **config, - data_converter=pydantic_data_converter, - plugins=[plugin], - ) + config["plugins"] = [*config.get("plugins", []), plugin] + client = await Client.connect(**config, data_converter=pydantic_data_converter) wf_id = f"langsmith-chatbot-{uuid.uuid4().hex[:8]}" diff --git a/openai_agents/basic/run_remote_image_workflow.py b/openai_agents/basic/run_remote_image_workflow.py index d0f3ceaa..0c471b33 100644 --- a/openai_agents/basic/run_remote_image_workflow.py +++ b/openai_agents/basic/run_remote_image_workflow.py @@ -1,4 +1,5 @@ import asyncio +from typing import Any from temporalio.client import Client from temporalio.envconfig import ClientConfig @@ -8,13 +9,11 @@ async def main(): - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[OpenAIAgentsPlugin()], - ) + config["plugins"] = [*config.get("plugins", []), OpenAIAgentsPlugin()] + client = await Client.connect(**config) # Use the URL from the original example image_url = ( diff --git a/openai_agents/handoffs/run_message_filter_workflow.py b/openai_agents/handoffs/run_message_filter_workflow.py index 64b5e4e3..f57652d9 100644 --- a/openai_agents/handoffs/run_message_filter_workflow.py +++ b/openai_agents/handoffs/run_message_filter_workflow.py @@ -1,5 +1,6 @@ import asyncio import json +from typing import Any from temporalio.client import Client from temporalio.envconfig import ClientConfig @@ -11,16 +12,12 @@ async def main(): - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") # Create client connected to server at the given address - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[ - OpenAIAgentsPlugin(), - ], - ) + config["plugins"] = [*config.get("plugins", []), OpenAIAgentsPlugin()] + client = await Client.connect(**config) # Execute a workflow result = await client.execute_workflow( diff --git a/openai_agents/mcp/run_file_system_worker.py b/openai_agents/mcp/run_file_system_worker.py index 8e72e0b0..2c52e804 100644 --- a/openai_agents/mcp/run_file_system_worker.py +++ b/openai_agents/mcp/run_file_system_worker.py @@ -4,6 +4,7 @@ import logging import os from datetime import timedelta +from typing import Any from agents.mcp import MCPServerStdio from temporalio.client import Client @@ -36,19 +37,18 @@ async def main(): ) # Create client connected to server at the given address - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[ - OpenAIAgentsPlugin( - model_params=ModelActivityParameters( - start_to_close_timeout=timedelta(seconds=60) - ), - mcp_server_providers=[file_system_server], + config["plugins"] = [ + *config.get("plugins", []), + OpenAIAgentsPlugin( + model_params=ModelActivityParameters( + start_to_close_timeout=timedelta(seconds=60) ), - ], - ) + mcp_server_providers=[file_system_server], + ), + ] + client = await Client.connect(**config) # @@@SNIPEND worker = Worker( diff --git a/openai_agents/mcp/run_file_system_workflow.py b/openai_agents/mcp/run_file_system_workflow.py index d16ba309..b45d0b26 100644 --- a/openai_agents/mcp/run_file_system_workflow.py +++ b/openai_agents/mcp/run_file_system_workflow.py @@ -1,4 +1,5 @@ import asyncio +from typing import Any from temporalio.client import Client from temporalio.envconfig import ClientConfig @@ -9,14 +10,10 @@ async def main(): # Create client connected to server at the given address - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[ - OpenAIAgentsPlugin(), - ], - ) + config["plugins"] = [*config.get("plugins", []), OpenAIAgentsPlugin()] + client = await Client.connect(**config) # Execute a workflow result = await client.execute_workflow( diff --git a/openai_agents/mcp/run_memory_research_scratchpad_worker.py b/openai_agents/mcp/run_memory_research_scratchpad_worker.py index e8fe7211..0c8326f1 100644 --- a/openai_agents/mcp/run_memory_research_scratchpad_worker.py +++ b/openai_agents/mcp/run_memory_research_scratchpad_worker.py @@ -3,6 +3,7 @@ import asyncio import logging from datetime import timedelta +from typing import Any from agents.mcp import MCPServerStdio from temporalio.client import Client @@ -35,19 +36,18 @@ async def main(): ) # Create client connected to server at the given address - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[ - OpenAIAgentsPlugin( - model_params=ModelActivityParameters( - start_to_close_timeout=timedelta(seconds=60) - ), - mcp_server_providers=[memory_server_provider], + config["plugins"] = [ + *config.get("plugins", []), + OpenAIAgentsPlugin( + model_params=ModelActivityParameters( + start_to_close_timeout=timedelta(seconds=60) ), - ], - ) + mcp_server_providers=[memory_server_provider], + ), + ] + client = await Client.connect(**config) # @@@SNIPEND worker = Worker( diff --git a/openai_agents/mcp/run_memory_research_scratchpad_workflow.py b/openai_agents/mcp/run_memory_research_scratchpad_workflow.py index a033b452..2d931563 100644 --- a/openai_agents/mcp/run_memory_research_scratchpad_workflow.py +++ b/openai_agents/mcp/run_memory_research_scratchpad_workflow.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +from typing import Any from temporalio.client import Client from temporalio.envconfig import ClientConfig @@ -12,14 +13,10 @@ async def main(): - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[ - OpenAIAgentsPlugin(), - ], - ) + config["plugins"] = [*config.get("plugins", []), OpenAIAgentsPlugin()] + client = await Client.connect(**config) result = await client.execute_workflow( MemoryResearchScratchpadWorkflow.run, diff --git a/openai_agents/mcp/run_prompt_server_worker.py b/openai_agents/mcp/run_prompt_server_worker.py index af4ab1f8..8131d148 100644 --- a/openai_agents/mcp/run_prompt_server_worker.py +++ b/openai_agents/mcp/run_prompt_server_worker.py @@ -3,6 +3,7 @@ import asyncio import logging from datetime import timedelta +from typing import Any from agents.mcp import MCPServerStreamableHttp from temporalio.client import Client @@ -34,19 +35,18 @@ async def main(): ) # Create client connected to server at the given address - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[ - OpenAIAgentsPlugin( - model_params=ModelActivityParameters( - start_to_close_timeout=timedelta(seconds=120) - ), - mcp_server_providers=[prompt_server_provider], + config["plugins"] = [ + *config.get("plugins", []), + OpenAIAgentsPlugin( + model_params=ModelActivityParameters( + start_to_close_timeout=timedelta(seconds=120) ), - ], - ) + mcp_server_providers=[prompt_server_provider], + ), + ] + client = await Client.connect(**config) worker = Worker( client, diff --git a/openai_agents/mcp/run_prompt_server_workflow.py b/openai_agents/mcp/run_prompt_server_workflow.py index 41af6d56..f3d6f916 100644 --- a/openai_agents/mcp/run_prompt_server_workflow.py +++ b/openai_agents/mcp/run_prompt_server_workflow.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +from typing import Any from temporalio.client import Client from temporalio.envconfig import ClientConfig @@ -11,12 +12,10 @@ async def main(): # Create client connected to server at the given address - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[OpenAIAgentsPlugin()], - ) + config["plugins"] = [*config.get("plugins", []), OpenAIAgentsPlugin()] + client = await Client.connect(**config) # Execute a workflow result = await client.execute_workflow( diff --git a/openai_agents/mcp/run_sse_worker.py b/openai_agents/mcp/run_sse_worker.py index f93eeb49..e2b38959 100644 --- a/openai_agents/mcp/run_sse_worker.py +++ b/openai_agents/mcp/run_sse_worker.py @@ -3,6 +3,7 @@ import asyncio import logging from datetime import timedelta +from typing import Any from agents.mcp import MCPServerSse from temporalio.client import Client @@ -34,19 +35,18 @@ async def main(): ) # Create client connected to server at the given address - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[ - OpenAIAgentsPlugin( - model_params=ModelActivityParameters( - start_to_close_timeout=timedelta(seconds=60) - ), - mcp_server_providers=[sse_server_provider], + config["plugins"] = [ + *config.get("plugins", []), + OpenAIAgentsPlugin( + model_params=ModelActivityParameters( + start_to_close_timeout=timedelta(seconds=60) ), - ], - ) + mcp_server_providers=[sse_server_provider], + ), + ] + client = await Client.connect(**config) worker = Worker( client, diff --git a/openai_agents/mcp/run_sse_workflow.py b/openai_agents/mcp/run_sse_workflow.py index 6827e72c..d568e7cf 100644 --- a/openai_agents/mcp/run_sse_workflow.py +++ b/openai_agents/mcp/run_sse_workflow.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +from typing import Any from temporalio.client import Client from temporalio.envconfig import ClientConfig @@ -11,12 +12,10 @@ async def main(): # Create client connected to server at the given address - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[OpenAIAgentsPlugin()], - ) + config["plugins"] = [*config.get("plugins", []), OpenAIAgentsPlugin()] + client = await Client.connect(**config) # Execute a workflow result = await client.execute_workflow( diff --git a/openai_agents/mcp/run_streamable_http_worker.py b/openai_agents/mcp/run_streamable_http_worker.py index eed1738a..f853e4c4 100644 --- a/openai_agents/mcp/run_streamable_http_worker.py +++ b/openai_agents/mcp/run_streamable_http_worker.py @@ -3,6 +3,7 @@ import asyncio import logging from datetime import timedelta +from typing import Any from agents.mcp import MCPServerStreamableHttp from temporalio.client import Client @@ -34,19 +35,18 @@ async def main(): ) # Create client connected to server at the given address - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[ - OpenAIAgentsPlugin( - model_params=ModelActivityParameters( - start_to_close_timeout=timedelta(seconds=60) - ), - mcp_server_providers=[streamable_http_server_provider], + config["plugins"] = [ + *config.get("plugins", []), + OpenAIAgentsPlugin( + model_params=ModelActivityParameters( + start_to_close_timeout=timedelta(seconds=60) ), - ], - ) + mcp_server_providers=[streamable_http_server_provider], + ), + ] + client = await Client.connect(**config) worker = Worker( client, diff --git a/openai_agents/mcp/run_streamable_http_workflow.py b/openai_agents/mcp/run_streamable_http_workflow.py index f5ea06d7..1362bcea 100644 --- a/openai_agents/mcp/run_streamable_http_workflow.py +++ b/openai_agents/mcp/run_streamable_http_workflow.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +from typing import Any from temporalio.client import Client from temporalio.envconfig import ClientConfig @@ -11,12 +12,10 @@ async def main(): # Create client connected to server at the given address - config = ClientConfig.load_client_connect_config() + config: dict[str, Any] = dict(ClientConfig.load_client_connect_config()) config.setdefault("target_host", "localhost:7233") - client = await Client.connect( # type: ignore[misc] - **config, - plugins=[OpenAIAgentsPlugin()], - ) + config["plugins"] = [*config.get("plugins", []), OpenAIAgentsPlugin()] + client = await Client.connect(**config) # Execute a workflow result = await client.execute_workflow(