From de5e6d3f3d1ffa2fc646d89d625cb5f8c72a35e1 Mon Sep 17 00:00:00 2001 From: Luke Mainwaring Date: Thu, 16 Jul 2026 13:31:37 -0400 Subject: [PATCH] chore: bump all dependencies to latest versions --- backend/pyproject.toml | 24 +- backend/uv.lock | 857 ++- docs/pydantic-ai-llms-full.txt | 9150 ++++++++++++++++++++++++++++++-- docs/vercel-ai-sdk-ui.txt | 94 +- frontend/package.json | 46 +- frontend/pnpm-lock.yaml | 2223 +++++--- 6 files changed, 10691 insertions(+), 1703 deletions(-) diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 35fe41a..83dfd95 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -11,34 +11,34 @@ requires-python = ">=3.13,<3.14" dependencies = [ "alembic>=1.18.5", "asyncpg>=0.31.0", - "braindecode>=1.5.2", + "braindecode>=1.6.1", "safetensors>=0.8.0", - "fastapi[standard]>=0.138.1", + "fastapi[standard]>=0.139.2", "httpx>=0.28.1", "librosa>=0.11.0", "mne>=1.12.1", - "modal>=1.5.1", + "modal>=1.5.2", "numpy>=2.4.6", - "pgvector>=0.4.2", + "pgvector>=0.5.0", "psycopg[binary,pool]>=3.3.4", - "pydantic-ai>=2.0.0", + "pydantic-ai>=2.11.0", "pydantic>=2.13.4", "scipy>=1.18.0", "spotipy>=2.26.0", "sqlalchemy[asyncio,mypy]>=2.0.51", - "tensorboard>=2.20.0", - "torch>=2.12.1", - "transformers>=5.12.1", - "uvicorn>=0.49.0", + "tensorboard>=2.21.0", + "torch>=2.13.0", + "transformers>=5.14.1", + "uvicorn>=0.51.0", ] [dependency-groups] dev = [ - "mypy>=2.1.0", + "mypy>=2.3.0", "pre-commit>=4.6.0", - "pydantic-evals>=2.0.0", + "pydantic-evals>=2.11.0", "pytest>=9.1.1", - "ruff>=0.15.20", + "ruff>=0.15.22", ] [tool.pytest.ini_options] diff --git a/backend/uv.lock b/backend/uv.lock index 2d0f5ca..feaf4aa 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -9,11 +9,11 @@ resolution-markers = [ [[package]] name = "absl-py" -version = "2.4.0" +version = "2.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/64/c7/8de93764ad66968d19329a7e0c147a2bb3c7054c554d4a119111b8f9440f/absl_py-2.4.0.tar.gz", hash = "sha256:8c6af82722b35cf71e0f4d1d47dcaebfff286e27110a99fc359349b247dfb5d4", size = 116543, upload-time = "2026-01-28T10:17:05.322Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/4f/d79676ab82f2e42fc3611618139f13a9c4c31d0cff4b486982047679a802/absl_py-2.5.0.tar.gz", hash = "sha256:0c996f25c0490700fadabe6351630f6111534fa0ae252cc6d2014ea3b141135f", size = 118119, upload-time = "2026-07-03T10:57:48.157Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/a6/907a406bb7d359e6a63f99c313846d9eec4f7e6f7437809e03aa00fa3074/absl_py-2.4.0-py3-none-any.whl", hash = "sha256:88476fd881ca8aab94ffa78b7b6c632a782ab3ba1cd19c9bd423abc4fb4cd28d", size = 135750, upload-time = "2026-01-28T10:17:04.19Z" }, + { url = "https://files.pythonhosted.org/packages/58/0a/a10b45aab35b175aded078a462dc8d0c698f5b13946e7cb0869097b78bb6/absl_py-2.5.0-py3-none-any.whl", hash = "sha256:0f17b89f2a4eaaedc4f28c622998aa690564b3012a396a4ffad0821007fe03ba", size = 137410, upload-time = "2026-07-03T10:57:46.735Z" }, ] [[package]] @@ -30,11 +30,11 @@ wheels = [ [[package]] name = "aiohappyeyeballs" -version = "2.6.2" +version = "2.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591, upload-time = "2026-05-20T15:12:24.631Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/f4/eec0465c2f67b2664688d0240b3212d5196fd89e741df67ddb81f8d35658/aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d", size = 24757, upload-time = "2026-07-01T17:11:55.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062, upload-time = "2026-05-20T15:12:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472", size = 15038, upload-time = "2026-07-01T17:11:54.055Z" }, ] [[package]] @@ -123,7 +123,7 @@ wheels = [ [[package]] name = "anthropic" -version = "0.112.0" +version = "0.116.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -135,54 +135,55 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7b/dd/808c144d4a883fcfd12fe0d7689b1d86bbbea6666c1cc957ad19f1017c22/anthropic-0.112.0.tar.gz", hash = "sha256:e180cd91aa5b9b32e4007fe69892ab128d8a86b9f90825103b1903fbc977d0af", size = 937460, upload-time = "2026-06-24T18:45:56.844Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/a2/d31f14e28d49bae983a3634e38dfb4b31c50110b5e403596c5c6a20b23f8/anthropic-0.116.0.tar.gz", hash = "sha256:5fc248fbb9fe03ef686f8a774f81586bca31a043260aab88b387ea3660f4a396", size = 949149, upload-time = "2026-07-02T19:08:10.534Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/26/ea71185027956325be1903d4fcaf7461d5ef40ca8f0e64f992e24ea9db0e/anthropic-0.112.0-py3-none-any.whl", hash = "sha256:bcc6268612c716dbb77133dd60fc41d26016d1b81dee9a52314d210193638751", size = 931954, upload-time = "2026-06-24T18:45:58.205Z" }, + { url = "https://files.pythonhosted.org/packages/c7/dd/2a1e81cf1b163acc340afc4ec74ed1d86f5eed1a809fabdeed3e0997b346/anthropic-0.116.0-py3-none-any.whl", hash = "sha256:6c0a7698e8d652455da3499978279bb2588c7264d0a35be3666009a4258c8256", size = 956896, upload-time = "2026-07-02T19:08:08.756Z" }, ] [[package]] name = "anyio" -version = "4.14.1" +version = "4.14.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3b/72/5562aabb8dd7181e8e860622a38bea08d17842b99ecd4c91f84ac95251b0/anyio-4.14.1.tar.gz", hash = "sha256:8d648a3544c1a700e3ff78615cd679e4c5c3f149904287e73687b2596963629e", size = 254831, upload-time = "2026-06-24T20:56:06.017Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/7b/90df4a0a816d98d6ea26f559d87836d494a2cf1fcf063be67df50a7bcc30/anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72", size = 124875, upload-time = "2026-06-24T20:56:04.413Z" }, + { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, ] [[package]] name = "argcomplete" -version = "3.6.3" +version = "3.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/61/0b9ae6399dd4a58d8c1b1dc5a27d6f2808023d0b5dd3104bb99f45a33ff6/argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c", size = 73754, upload-time = "2025-10-20T03:33:34.741Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/c0/c8e94135e66fabf89a120d9b4b123fe6993506beca6c1938a74c24cfa5fd/argcomplete-3.7.0.tar.gz", hash = "sha256:afde224f753f874807b1dc1414e883ab8fe0cda9c04807b6047dcb8e1ac23913", size = 73284, upload-time = "2026-06-30T22:28:22.249Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/74/f5/9373290775639cb67a2fce7f629a1c240dce9f12fe927bc32b2736e16dfc/argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce", size = 43846, upload-time = "2025-10-20T03:33:33.021Z" }, + { url = "https://files.pythonhosted.org/packages/12/f6/5b8ec087cd9cfa9449491ec83f76fb6b7006b4dff57d2ba8aaab330fe8e4/argcomplete-3.7.0-py3-none-any.whl", hash = "sha256:d8f0f22d2a8a7caa383be1e22b6caf1ecaf0ebd10d8f83cc125e36540c95830c", size = 42575, upload-time = "2026-06-30T22:28:20.547Z" }, ] [[package]] name = "ast-serialize" -version = "0.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/9d/09e27731bd5864a9ce04e3244074e674bb8936bf62b45e0357248717adac/ast_serialize-0.5.0.tar.gz", hash = "sha256:5880091bfe6f4f986f22866375c2e884843e7a0b6343ae41aeea659613d879b6", size = 61157, upload-time = "2026-05-17T17:48:29.429Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/9e/dc2530acb3a60dc6e46d65abf27d1d9f86721694757906a148d90a6860de/ast_serialize-0.5.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0668aa9459cfa8c9c49ddd2163ebcf43088ba045ef7492af6fe22e0098303101", size = 1191380, upload-time = "2026-05-17T17:48:03.738Z" }, - { url = "https://files.pythonhosted.org/packages/26/0a/bd3d18a582f273d6c843d16bb9e22e9e16365ff7991e92f18f798e9f1224/ast_serialize-0.5.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf683d6363edf2b39eed6b6d4fe22d34b6203867a67e27134d9e2a2680c4bc4a", size = 1183879, upload-time = "2026-05-17T17:48:05.463Z" }, - { url = "https://files.pythonhosted.org/packages/40/ae/1f919100f8620887af58fcc381c61a1f218cdf89c6e155f87b213e61010a/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc22cf0c9be65e71cf88fda130af60d61eb4a79370ad4cfe7900d48a4aa2211", size = 1244529, upload-time = "2026-05-17T17:48:07.008Z" }, - { url = "https://files.pythonhosted.org/packages/c6/ca/6376559dcce707cdbc1d0d9a13c8d3baaaa501e949ce0ebdc4230cd881aa/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f66173891548c9f2726bf27957b41cabce12fa679dc6da505ddbde4d4b3b31cf", size = 1240560, upload-time = "2026-05-17T17:48:08.46Z" }, - { url = "https://files.pythonhosted.org/packages/35/b2/a620e206b5aeb7efbf2710336df57d457cffbb3991076bbcc1147ef9abd4/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e42d729ef2be96a14efbad355093284739e3670ece3e534f82cc8832790911d9", size = 1451172, upload-time = "2026-05-17T17:48:09.922Z" }, - { url = "https://files.pythonhosted.org/packages/fa/e0/4ad5c04c24a40481b2935ce9a0ccdb6023dc8b667167d06ae530cc3512f2/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b725026bafa801dbd7310eb13a75f0a2e370e7e51b2cb225f9d21fcfadf919ee", size = 1265072, upload-time = "2026-05-17T17:48:11.469Z" }, - { url = "https://files.pythonhosted.org/packages/b2/71/4d1d479aa56d0101c40e17720c3d6ac2af7269ea0487a80b18e7bfd1a5b7/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b54f60c1d78767a53b67eaa663f0dfac3afe606aa07f1301572f588b73d64809", size = 1270488, upload-time = "2026-05-17T17:48:13.575Z" }, - { url = "https://files.pythonhosted.org/packages/6d/4f/0de1bbe06f6edef9fde4ed12ca8e7b3ec7e6e2bd4e672c5af487f7957665/ast_serialize-0.5.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:27d51654fc240a1e87e742d353d98eb45b75f62f129086b3596ab53df2ac2a43", size = 1260702, upload-time = "2026-05-17T17:48:15.141Z" }, - { url = "https://files.pythonhosted.org/packages/75/61/e00872439cfdddcc3c1b6cdaa6e5d904ba8e26a18807c67c4e14409d0ca8/ast_serialize-0.5.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c36237c46dd1674542f2109740ea5ea485a169bf1431939ada0434e17934", size = 1311182, upload-time = "2026-05-17T17:48:16.779Z" }, - { url = "https://files.pythonhosted.org/packages/76/8e/699a5b955f7926956c95e9e1d74132acad73c2fe7a426f94da89123c20aa/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1943db345233cc7194a470f13afa9c59772c0b123dea0c9414c4d4ca54369759", size = 1421410, upload-time = "2026-05-17T17:48:18.527Z" }, - { url = "https://files.pythonhosted.org/packages/a9/ae/d5b7626874478997adc7a29ab28accf21e596fb590c944290401dfd0b29e/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df1c00022cbbcb064bfaa505aa9c9295362443ce5dacb459d1331d3da353f887", size = 1516587, upload-time = "2026-05-17T17:48:20.133Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ce/b59e02a82d9c4244d64cde502e0b00e83e38816abe19155ceb5437402c7f/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cae65289fc456fde04af979a2be09302ef5d8ab92ef23e596d6746dc267ada27", size = 1515171, upload-time = "2026-05-17T17:48:21.921Z" }, - { url = "https://files.pythonhosted.org/packages/8b/38/d8d90042747d05aa08d4efcf1c99035a5f670a6bf4c214d31644392afbca/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:239a4c354e8d676e9d94631d1d4a64edc6b266f86ff3a5a80aedd344f342c01d", size = 1464668, upload-time = "2026-05-17T17:48:23.544Z" }, - { url = "https://files.pythonhosted.org/packages/dd/51/5b840c4df7334104cecffa28f23904fe81ca89ca223d2450e288de39fd3c/ast_serialize-0.5.0-cp39-abi3-win32.whl", hash = "sha256:143a4ef63285a075871908fda3672dc21864b83a8ec3ee12304aa3e4c5387b9a", size = 1068311, upload-time = "2026-05-17T17:48:25.027Z" }, - { url = "https://files.pythonhosted.org/packages/41/11/ca5672c7d491825bc4cd6702dea106a6b60d928707712ec257c7833ae476/ast_serialize-0.5.0-cp39-abi3-win_amd64.whl", hash = "sha256:cf25572c526add400f26a4750dc6ce0c3bb93fc1f75e7ae0cad4ce4f2cd5c590", size = 1108931, upload-time = "2026-05-17T17:48:26.591Z" }, - { url = "https://files.pythonhosted.org/packages/45/19/cc8bd127d28a43da249aa955cfd164cf8fd534e79e42cea96c4854d72fd0/ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642", size = 1081181, upload-time = "2026-05-17T17:48:28.122Z" }, +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/ad/0d70a3a2d6e01968d985415259e8ec7ad3f777903f9b1c1f3c8c44642c60/ast_serialize-0.6.0.tar.gz", hash = "sha256:aadd3ffcf4858c9726bf3515f7b199c7eadbe504f96028e4a87172c0da65a8fe", size = 61489, upload-time = "2026-06-30T20:02:55.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/19/ac8348ae8711c9b5ae834634f635780cab62a0f5e6f988882e048b89c2ae/ast_serialize-0.6.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:093cb8bb91b720d8523580498d031791bb1bbaa048599c3d21085d380e11a596", size = 1185367, upload-time = "2026-06-30T20:02:30.427Z" }, + { url = "https://files.pythonhosted.org/packages/c1/f6/ec7ec652c51db77c2f61d8573338e13e4704303265ccc658cb4031d9f354/ast_serialize-0.6.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:e61580a69faf47e3689795367ed211f2a10fd741478cc0f36a0f128793360aad", size = 1178657, upload-time = "2026-06-30T20:02:31.964Z" }, + { url = "https://files.pythonhosted.org/packages/6f/02/613a7534a41d0122f37d1e0c64aa8ac78bfb831f8c92f6db057a311abb3c/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305802f2ce2a7c4e87835078ea85c58b586ddda8095b92fe2ead9364ae19c80a", size = 1238620, upload-time = "2026-06-30T20:02:33.664Z" }, + { url = "https://files.pythonhosted.org/packages/4d/21/087957bba486242afc52f49b2d9e21c9dad00289356cf9efe67084015a9d/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c7b8b8f0c42f752ea00b2b7d7c090b3f80d9c1c5c75cadf16423790a0cc74081", size = 1236075, upload-time = "2026-06-30T20:02:34.936Z" }, + { url = "https://files.pythonhosted.org/packages/82/04/78128bbb170071c2c72a210a181f1c00e11cc1cec60a8beef747b07f9201/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd5b91b9e6f2356ace3a556963b0cd783b395fbbb0bb17b4defc283415466e77", size = 1441348, upload-time = "2026-06-30T20:02:36.245Z" }, + { url = "https://files.pythonhosted.org/packages/64/64/62fb99d6faf199b4c3e5b08a07136e9a0d7664bb249c6de3670e5b63e9b6/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d6ef91590258ada18909b9caea344dac4de2013906b035473cd674a43f4b790", size = 1258580, upload-time = "2026-06-30T20:02:37.53Z" }, + { url = "https://files.pythonhosted.org/packages/ca/87/b4d6c38e0ccd5e85dc54cecdf933a152c60b28fe5d993a6d8a72fa6d5896/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcbed41e9386059fc0261d602445ede0976c2ecec2939688bcbcb9ed0b6f28b7", size = 1261693, upload-time = "2026-06-30T20:02:39.123Z" }, + { url = "https://files.pythonhosted.org/packages/0e/4b/3676ca2191f39bafb75f93f99b2f429ec464586158fece2165f3572805dc/ast_serialize-0.6.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:cdc4e6f930b9090c2f92c9036ad12ffb8e6e44d4a5ba06f1458a05d60f203f7b", size = 1252517, upload-time = "2026-06-30T20:02:40.511Z" }, + { url = "https://files.pythonhosted.org/packages/f3/58/494ef8c4b4acb2f4a265ac934caf45f792a08fe27d6b853de35ad991941a/ast_serialize-0.6.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:897ac47b5637be41c0c07061c8a912fafa967ef1dc73fa115e4bfa70882a093b", size = 1304843, upload-time = "2026-06-30T20:02:41.961Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f2/13736d920ab3d49bbee80ef1a277dd7b7aaf3b3545efd9d2a8114fe05525/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c4af9a1386166e40ed01464991806f89038a2d89782576c7774876fa77034e32", size = 1413698, upload-time = "2026-06-30T20:02:44.179Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5a/e046f3899e2acba4677d7427b76431443a1aa1a0e583dfb05b55b69d55cf/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:c901adbd750029b9ac4ad3d6aa56853e0ad4875119fbf52b7b8298afc223828b", size = 1512209, upload-time = "2026-06-30T20:02:45.584Z" }, + { url = "https://files.pythonhosted.org/packages/cc/c7/e42aaca7bb2d22a7c06d5a8c7930086c5a334e93d716e6fa5e6647a4515f/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:3ae22a366b752ab4496191525b78b097b5b72d531752e3c1dd7e383a8f2c8a1a", size = 1508464, upload-time = "2026-06-30T20:02:46.942Z" }, + { url = "https://files.pythonhosted.org/packages/95/93/5524a3dc6c3f593de3228ed9cbef73afa047625b7000ec21b7f58e6eb4d4/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4ed29121da8b3fdc291002801a1de0f76248fa07dce89157a5f277842cf6126e", size = 1457164, upload-time = "2026-06-30T20:02:48.294Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c0/36a6ffb4d653cf621427b4c4928671f53ad800c453474de2b82564a44ad9/ast_serialize-0.6.0-cp39-abi3-pyemscripten_2026_0_wasm32.whl", hash = "sha256:b1dac4e09d341c1300ba69cdcbe62867b32a8c75d90db9bf4d083bec3b039f0b", size = 863014, upload-time = "2026-06-30T20:02:49.742Z" }, + { url = "https://files.pythonhosted.org/packages/09/c7/7d5ad8b49e1278e1c2a1e0274bd7850560b3f09313aa00c13bc8d5544792/ast_serialize-0.6.0-cp39-abi3-win32.whl", hash = "sha256:82c312a7844d2fdeb4d5c48bd3d215bf940dafd4704e1a9bcf252a99010a99b1", size = 1063165, upload-time = "2026-06-30T20:02:50.98Z" }, + { url = "https://files.pythonhosted.org/packages/47/ae/6710c14ecb276031cf10249f6adf5a59e2d3fdb3b5183bd59f70524067ee/ast_serialize-0.6.0-cp39-abi3-win_amd64.whl", hash = "sha256:113b58346f9ceb664352032770caca817d4a3c86f611c6088e6ef65ddaa70f0e", size = 1101444, upload-time = "2026-06-30T20:02:52.554Z" }, + { url = "https://files.pythonhosted.org/packages/66/40/c53deb2cd0c9b0fb636d24d9f40924cf2e65028e6b20b10cd5c1eeb2c730/ast_serialize-0.6.0-cp39-abi3-win_arm64.whl", hash = "sha256:ccd132fe8db56f61fe743b1f644d01b8d65b83248a8da506f3132bda86d6ed5e", size = 1072965, upload-time = "2026-06-30T20:02:54.097Z" }, ] [[package]] @@ -300,7 +301,7 @@ wheels = [ [[package]] name = "braindecode" -version = "1.5.2" +version = "1.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docstring-inheritance" }, @@ -318,9 +319,9 @@ dependencies = [ { name = "torchinfo" }, { name = "wfdb" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/68/08dc010f98d64462a82fc3486a22a0ed42e58a975a44902907c8d2a11efd/braindecode-1.5.2.tar.gz", hash = "sha256:20977649acf7591b1a1c32560db3871da48d394772982be392a814e15d0fe25a", size = 560625, upload-time = "2026-05-28T19:59:27.14Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/5c/113d02e295dce25e2cabb0c1dc4fb31bbdbb9a6f4e086b851557a15837a3/braindecode-1.6.1.tar.gz", hash = "sha256:d465a9cb810873b30b652db4b922cb3769709d3880459741be0e2f531bcda7ae", size = 612409, upload-time = "2026-07-01T10:20:36.276Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/bd/e301b43193defd240b0284e4150cda73d268b488ae5acd22238103e92ed7/braindecode-1.5.2-py3-none-any.whl", hash = "sha256:bd1172c63c487a0d555bd2e2597f07ee7d7178ac1542615ed89e607ced85bde9", size = 575557, upload-time = "2026-05-28T19:59:25.421Z" }, + { url = "https://files.pythonhosted.org/packages/ca/45/0aae59c3d6bc4fa84e5786588131e74e2441e4fe17028ef5f52be14ebb71/braindecode-1.6.1-py3-none-any.whl", hash = "sha256:400594d8af168453c6e826c34b8a887620d3e90c9b3fbb7d32ceb08c56f243d2", size = 626932, upload-time = "2026-07-01T10:20:34.415Z" }, ] [[package]] @@ -347,18 +348,18 @@ wheels = [ [[package]] name = "cbor2" -version = "6.1.2" +version = "6.1.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/af/473c241e41c142ea06ebef8d1f660fa6ff928fb97210e7bec8ee5974f8cd/cbor2-6.1.2.tar.gz", hash = "sha256:6b43037a66947dee5af0abb1a4c3a13b3abac5a4a3f32f9771efbbcd030fd909", size = 86760, upload-time = "2026-06-02T19:01:29.333Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/6f/07b4af8da8bd27f640362b1ac8271d80895407f2ede0c2bcc9433c06e1ca/cbor2-6.1.3.tar.gz", hash = "sha256:8d70680acb55c04ea5b5ad86da094f9612b53d5a8a65d0f5b3aafc3ce917ecbb", size = 89503, upload-time = "2026-07-04T10:36:48.793Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/dc/bc045c8f36317e4e5f7a60d94b36833139909fc32e3a65f44bc61a36def0/cbor2-6.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f1aa38c422d87ea61849b2a823b10b64053fb4da8763f19ac78ea9a69d682b2a", size = 408846, upload-time = "2026-06-02T19:00:55.476Z" }, - { url = "https://files.pythonhosted.org/packages/2b/36/d66f5f0dd98ecbdcfc7da1fbd423f7b3782a27719f0062a560476f00b334/cbor2-6.1.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ff7d0bd8ff432832338a8d2430aee34f8a082342480ff537c0ba90e2b8ff7894", size = 454624, upload-time = "2026-06-02T19:00:56.744Z" }, - { url = "https://files.pythonhosted.org/packages/38/6b/4884b9cf03db14dc5007825d5d1bf8678a75c49d4268d8e0c1c6e9580104/cbor2-6.1.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c1eedf3290d88a5f663bd8b4b8f0f0e2103d0594c293fa5f4e62e53100972309", size = 466585, upload-time = "2026-06-02T19:00:58.209Z" }, - { url = "https://files.pythonhosted.org/packages/50/f6/36a15beb3915f56a79d6e9213c6d40c0f5cb90cd3462923f555d78068847/cbor2-6.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3049b04bddf9a5a2d0e5bb25dccdaf4552fcaf607b404e249d4f78f010fcc7d0", size = 521678, upload-time = "2026-06-02T19:00:59.524Z" }, - { url = "https://files.pythonhosted.org/packages/c6/3f/e899313371ebeb7a191d751de97ccd8242abc24bbc9d8e2c58e04475cfb0/cbor2-6.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:96eb687a62040401668f06a85de8f47361ef44574de1493899e0ec678109fc04", size = 534044, upload-time = "2026-06-02T19:01:00.875Z" }, - { url = "https://files.pythonhosted.org/packages/1e/5e/1a872acdeb1ab9a884ec3460f73a43e02154dc20d8ccb627bbd60f4c0ea1/cbor2-6.1.2-cp313-cp313-win32.whl", hash = "sha256:03440b505882280023db1fedcee6844804e9968bb50f9eb4ff12aaf27777fcfe", size = 282328, upload-time = "2026-06-02T19:01:02.347Z" }, - { url = "https://files.pythonhosted.org/packages/70/79/29721bc15d38889e7bec214ede2346ee15970bedcc5e6ce1fa30f21e9a4e/cbor2-6.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:d2c8da2c0f821827dcc9eb59a5c9351791a8aa3b389a2ea7ca64c4f97bcb94cf", size = 300313, upload-time = "2026-06-02T19:01:03.69Z" }, - { url = "https://files.pythonhosted.org/packages/07/98/a13b424fb2f14fe332b57f71f479953b2f291a051f797d42ddab9fcd2027/cbor2-6.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:8e1478d3b980ddfcaf56e27cecbfe13057e0f67d5e8240fe8a398815acb9c4bf", size = 288725, upload-time = "2026-06-02T19:01:04.933Z" }, + { url = "https://files.pythonhosted.org/packages/1c/8c/5024d623dcf3f2057ec8c991f584b939ba5f9025a5ce8c31f6fac067137a/cbor2-6.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:21c74b8ab67977c8b87b727247eeb730145b0068ad6d47f71e9f80f6b48c65f8", size = 412334, upload-time = "2026-07-04T10:36:16.299Z" }, + { url = "https://files.pythonhosted.org/packages/61/f3/e50654203c3b746166a96bea680eb6463b20c2c160cc14dfbe43f215ef6c/cbor2-6.1.3-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f291a0ae4c1ed96eadb0afa9752568c7424f7d6fa818676d5e33005fcd22ddd9", size = 457125, upload-time = "2026-07-04T10:36:17.684Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/6ef007f0d4f7afba90a80cb1657984de542e7474d2afaa7e920ac9860df3/cbor2-6.1.3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:dc8e44c7bf172687195dcd428157885bc00ea06efc0ea30fb371163b92bef733", size = 467651, upload-time = "2026-07-04T10:36:19.007Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f4/b5aa27813c02f37e03eb86bd908163562edd6fc7f99665bc7bbb25ef5e6c/cbor2-6.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8ccf4d263983d830dd429d2b01f27be58ac02ba7c790c45d861f767eb63963e5", size = 523296, upload-time = "2026-07-04T10:36:20.504Z" }, + { url = "https://files.pythonhosted.org/packages/5b/46/e17b2bce2efdc26bfa045b4f6168f02923ac5f0e79732a1b3c42ce9ca9de/cbor2-6.1.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8719a7a2a2a82168844533389957b8f617a139f5f40e4d0ad7ed905fd3abebd1", size = 535537, upload-time = "2026-07-04T10:36:21.761Z" }, + { url = "https://files.pythonhosted.org/packages/54/bc/add350acf37f367ae429f997f2e047b042f2d5ef9ca62461c923fbb0f3c3/cbor2-6.1.3-cp313-cp313-win32.whl", hash = "sha256:c73b54ce09dd8d522f3c1540426e36172ba0f34abf3d89eb93909a5e14590003", size = 279233, upload-time = "2026-07-04T10:36:23.071Z" }, + { url = "https://files.pythonhosted.org/packages/e8/6f/1bbfce3b3131e4e03e8a86966a38ff92ebb72215fcf36aeecea1547f3e4b/cbor2-6.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:b77df56c462c10eb3444db8ef78d8c3e71d9ef8d021ea92e97c1f9e3aa918690", size = 300585, upload-time = "2026-07-04T10:36:24.563Z" }, + { url = "https://files.pythonhosted.org/packages/e7/65/3945702dd84b6e5b7800c9c7f1ada038d33d12d0042de10e38164cc03dfd/cbor2-6.1.3-cp313-cp313-win_arm64.whl", hash = "sha256:b144be2ab3e9584ee7b6359d2a92fee0a5bec1d00dbd34c215ccc2040ac0b2ab", size = 290357, upload-time = "2026-07-04T10:36:25.983Z" }, ] [[package]] @@ -372,25 +373,27 @@ wheels = [ [[package]] name = "cffi" -version = "2.0.0" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/96/88/a996879e2eeccb815f6e3a5967b12a308257412acec882039d386bd2aa7b/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:10537b1df4967ca26d21e5072d7d54188354483b91dc75058968d3f0cf13fbda", size = 194331, upload-time = "2026-07-06T21:33:03.697Z" }, + { url = "https://files.pythonhosted.org/packages/58/85/7ae00d5c8dd6266f4e944c3db630f3c5c9a98b61d469c714d848b1d8138a/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a95b05f9baf29b91171b3a8bd2020b028835243e7b0ff6bb23e2a3c228518b1b", size = 196966, upload-time = "2026-07-06T21:33:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e9/45c3a76ad8d43ad9261f4c95436da61128d3ca545d72b9612c0ab5be0b1c/cffi-2.1.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:15faec4adfff450819f3aee0e2e02c812de6edb88203aa58807955db2003472a", size = 184795, upload-time = "2026-07-06T21:33:06.699Z" }, + { url = "https://files.pythonhosted.org/packages/84/4c/82f132cb4418ee6d953d982b19191e87e2a6372c8a4ce36e50b69d6ade4a/cffi-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:716ff8ec22f20b4d988b12884086bcef0fc99737043e503f7a3935a6be99b1ea", size = 184746, upload-time = "2026-07-06T21:33:08.071Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1c/4ed5a0e5bdca6cbc275556de3328dd1b76fd0c11cc13c88fe66d1d8715f2/cffi-2.1.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:63960549e4f8dc41e31accb97b975abaecfc44c03e396c093a6436763c2ea7db", size = 214747, upload-time = "2026-07-06T21:33:09.671Z" }, + { url = "https://files.pythonhosted.org/packages/3a/a6/e879bb68cc23a2bc9ba8f4b7d8019f0c2694bad2ab6c4a3701d429439f58/cffi-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff067a8d8d880e7809e4ac88eb009bb848870115317b306666502ccad30b147f", size = 222392, upload-time = "2026-07-06T21:33:10.896Z" }, + { url = "https://files.pythonhosted.org/packages/88/f6/01890cfd63c08f8eb96a8319b0443690197d240a8bd6346048cf7bde9190/cffi-2.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3b926723c13eba9f81d2ef3820d63aeceec3b2d4639906047bf675cb8a7a500d", size = 210285, upload-time = "2026-07-06T21:33:12.251Z" }, + { url = "https://files.pythonhosted.org/packages/a6/cf/2b684132056f438567b61e19d690dd31cd0921ace051e0a458be6074369e/cffi-2.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:47ff3a8bfd8cb9da1af7524b965127095055654c177fcfc7578debcb015eecd0", size = 208801, upload-time = "2026-07-06T21:33:13.617Z" }, + { url = "https://files.pythonhosted.org/packages/6f/08/f2e7d62c460faae0926f2d6e423694aa409ced3bc1fe2927a0a6e5f05416/cffi-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:799416bae98336e400981ff6e532d67d5c709cfb30afb79865a1315f94b0e224", size = 221808, upload-time = "2026-07-06T21:33:15.466Z" }, + { url = "https://files.pythonhosted.org/packages/38/37/04f54b8e63a02f3d908332c9effbf8c366167c6f733ed8a3d4f79b7e2a1e/cffi-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:961be50688f7fba2fa65f63712d3b9b341a22311f5253460ce933f52f0de1c8c", size = 225241, upload-time = "2026-07-06T21:33:16.869Z" }, + { url = "https://files.pythonhosted.org/packages/a9/d6/c72eecca433cd3e681c65ed313ab4835d9d4a379704d0f628a6a05f51c2e/cffi-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf5c6cf48238b0eb4c086978c492ad1cbc22373fc5b2d7353b3a598ce6db887a", size = 223588, upload-time = "2026-07-06T21:33:18.239Z" }, + { url = "https://files.pythonhosted.org/packages/c6/4b/e706f67279140f92939da3475ad610df18bfd52d50f14953a8e5fede71d5/cffi-2.1.0-cp313-cp313-win32.whl", hash = "sha256:db3eb7d46527159a878ec3460e9d40615bc25ba337d477db681aea6e4f05c5d2", size = 175248, upload-time = "2026-07-06T21:33:19.799Z" }, + { url = "https://files.pythonhosted.org/packages/5a/47/59eb7975cb0e4ef0afa764ea945b29a5bb4537a9f771cb7d6c8a5dd74c95/cffi-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:8e74a6135550c4748af665b1b1118b6aab33b1fc6a16f9aff630af107c3b4512", size = 185717, upload-time = "2026-07-06T21:33:21.47Z" }, + { url = "https://files.pythonhosted.org/packages/5a/af/34fee85c48f8d94efc8597bc09470c9dd274c145f1c12e0fbc6ab6d38d74/cffi-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:2282cd5e38aa8accd03e99d1256af8411c84cdbee6a89d841b563fdbd1f3e50f", size = 180114, upload-time = "2026-07-06T21:33:22.515Z" }, ] [[package]] @@ -404,27 +407,24 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.4.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, - { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, - { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, - { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, - { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, - { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +version = "3.4.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/2a/23f34ec9d04624958e137efdc394888716353190e75f25dd22c7a2c7a8aa/charset_normalizer-3.4.9.tar.gz", hash = "sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b", size = 152439, upload-time = "2026-07-07T14:34:58.454Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/06/97ec2aeae780b31d742b6352218b43841a6871e2564578ca522dce4a45c3/charset_normalizer-3.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:440eede837960000d74978f0eba527be106b5b9aee0daf779d395276ed0b0614", size = 317688, upload-time = "2026-07-07T14:33:35.408Z" }, + { url = "https://files.pythonhosted.org/packages/d0/39/8ff066c672434225f8d25f8b739f992af250944392173dcc88362681c9bf/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21e764fd1e70b6a3e205a0e46f3051701f98a8cb3fad66eeb80e48bb502f8698", size = 214982, upload-time = "2026-07-07T14:33:36.996Z" }, + { url = "https://files.pythonhosted.org/packages/92/8f/3a47a3667c83c2df9483d91644c6c107de3bf8874aa1793da9d3012eb986/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e4fd89cc178bced6ad29cb3e6dd4aa63fa5017c3524dbd0b25998fb64a87cc8b", size = 236460, upload-time = "2026-07-07T14:33:38.536Z" }, + { url = "https://files.pythonhosted.org/packages/f1/60/b22cdbee7e4013dab8b0d7647fc6181120fbbbc8f7025c226d15bd5a47fc/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bd47ba7fc3ca94896759ea0109775132d3e7ab921fbf54038e1bab2e46c313c9", size = 232003, upload-time = "2026-07-07T14:33:40.059Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f8/72eb13dcabe7257035cea8aefd922caad2f110d252bf9f67c4c2ca763aee/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84fd18bcc17526fc2b3c1af7d2b9217d32c9c04448c16ec693b9b4f1985c3d33", size = 223149, upload-time = "2026-07-07T14:33:41.631Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3e/faee8f9de92b14ee1198e9163252bb15efee7301b31256a3b6d9ebfdd0dd/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:5b10cd92fc5c498b35a8635df6d5a100207f88b63a4dc1de7ef9a548e1e2cd63", size = 207901, upload-time = "2026-07-07T14:33:43.209Z" }, + { url = "https://files.pythonhosted.org/packages/3a/25/45f30093ae27dd7b92a793b61882a38685f993700113ca36e0c9c14965e1/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4fbdde9dd4a9ce5fd52c2b3a347bb50cc89483ef783f1cb00d408c13f7a96c0", size = 219176, upload-time = "2026-07-07T14:33:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/48/18/c8f397329c35e32f6a837e488986f4ae03bd2abebc453b48714991630c2f/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:416c229f77e5ea25b3dfd4b582f8d73d7e43c22320302b9ab128a2d3a0b38efe", size = 217356, upload-time = "2026-07-07T14:33:46.192Z" }, + { url = "https://files.pythonhosted.org/packages/86/7e/5ce0bba863470fd1902d5e5843968951bddf38abe4742fc97116ef4598b3/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:75286256590a6320cf106a0d28970d3560aad9ee09aa7b34fb40524792436d35", size = 209614, upload-time = "2026-07-07T14:33:47.705Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ef/2473d3c4d869155be4af1191111d59c4d5c4e0173026f7e85b176e23bf65/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69b157c5d3292bcd443faca052f3096f637f1e074b98212a933c074ae23dc3b8", size = 224991, upload-time = "2026-07-07T14:33:49.238Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a3/53ddae3db108a088156aa8ddfafd411ebbc1340f48c5573f697b27f69a39/charset_normalizer-3.4.9-cp313-cp313-win32.whl", hash = "sha256:51307f5c71007673a2bf8232ad973483d281e74cb99c8c5a990af1eefa6277d9", size = 150622, upload-time = "2026-07-07T14:33:50.711Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ef/6953a77c7cf2c2ff9998e6f575ab3e380119f100223381565a4f94c1f836/charset_normalizer-3.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:fe2c7201c642b7c308f1675355ad7ff7b66acfe3541625efe5a3ad38f29d6115", size = 161947, upload-time = "2026-07-07T14:33:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/6e/fb/d560d1d1555debbfe7849d9cac6145c1b537709d79576bf22557ed803b82/charset_normalizer-3.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:611057cc5d5c0afc743ba8be6bd828c17e0aaa8643f9d0a9b9bb7dea80eb8012", size = 152594, upload-time = "2026-07-07T14:33:53.486Z" }, + { url = "https://files.pythonhosted.org/packages/98/2b/f97f1c193fb855c345d678f5077d6926034db0722df74c8f057020e05a25/charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5", size = 64538, upload-time = "2026-07-07T14:34:56.993Z" }, ] [[package]] @@ -537,34 +537,34 @@ dev = [ requires-dist = [ { name = "alembic", specifier = ">=1.18.5" }, { name = "asyncpg", specifier = ">=0.31.0" }, - { name = "braindecode", specifier = ">=1.5.2" }, - { name = "fastapi", extras = ["standard"], specifier = ">=0.138.1" }, + { name = "braindecode", specifier = ">=1.6.1" }, + { name = "fastapi", extras = ["standard"], specifier = ">=0.139.2" }, { name = "httpx", specifier = ">=0.28.1" }, { name = "librosa", specifier = ">=0.11.0" }, { name = "mne", specifier = ">=1.12.1" }, - { name = "modal", specifier = ">=1.5.1" }, + { name = "modal", specifier = ">=1.5.2" }, { name = "numpy", specifier = ">=2.4.6" }, - { name = "pgvector", specifier = ">=0.4.2" }, + { name = "pgvector", specifier = ">=0.5.0" }, { name = "psycopg", extras = ["binary", "pool"], specifier = ">=3.3.4" }, { name = "pydantic", specifier = ">=2.13.4" }, - { name = "pydantic-ai", specifier = ">=2.0.0" }, + { name = "pydantic-ai", specifier = ">=2.11.0" }, { name = "safetensors", specifier = ">=0.8.0" }, { name = "scipy", specifier = ">=1.18.0" }, { name = "spotipy", specifier = ">=2.26.0" }, { name = "sqlalchemy", extras = ["asyncio", "mypy"], specifier = ">=2.0.51" }, - { name = "tensorboard", specifier = ">=2.20.0" }, - { name = "torch", specifier = ">=2.12.1" }, - { name = "transformers", specifier = ">=5.12.1" }, - { name = "uvicorn", specifier = ">=0.49.0" }, + { name = "tensorboard", specifier = ">=2.21.0" }, + { name = "torch", specifier = ">=2.13.0" }, + { name = "transformers", specifier = ">=5.14.1" }, + { name = "uvicorn", specifier = ">=0.51.0" }, ] [package.metadata.requires-dev] dev = [ - { name = "mypy", specifier = ">=2.1.0" }, + { name = "mypy", specifier = ">=2.3.0" }, { name = "pre-commit", specifier = ">=4.6.0" }, - { name = "pydantic-evals", specifier = ">=2.0.0" }, + { name = "pydantic-evals", specifier = ">=2.11.0" }, { name = "pytest", specifier = ">=9.1.1" }, - { name = "ruff", specifier = ">=0.15.20" }, + { name = "ruff", specifier = ">=0.15.22" }, ] [[package]] @@ -618,50 +618,59 @@ wheels = [ [[package]] name = "cuda-pathfinder" -version = "1.5.5" +version = "1.5.6" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/c8/26f2e4aae92f11522a96043892ba39a90eac610d5242523aa863212bc1c7/cuda_pathfinder-1.5.5-py3-none-any.whl", hash = "sha256:0228c023f95d1480f143ef5c8922d27a2ab052087a942e81dc289c9eb8f91689", size = 51671, upload-time = "2026-05-27T01:21:25.413Z" }, + { url = "https://files.pythonhosted.org/packages/d2/53/8fc9b0cdc5b7f62746e6a01b85b6461e5ae27f871010a5fcf8fa6950766d/cuda_pathfinder-1.5.6-py3-none-any.whl", hash = "sha256:7e4c07c117b78ba1fb35dac4c444d21f3677b1b1ff56175c53a8e3025c5b43c0", size = 52972, upload-time = "2026-06-30T00:58:04.34Z" }, ] [[package]] name = "cuda-toolkit" -version = "13.0.2" +version = "13.0.3.0" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c7/a79086a62c98befcdb8349656c6f114e2db3b8b2422f6e25c97a7f2a9a3c/cuda_toolkit-13.0.3.0-py2.py3-none-any.whl", hash = "sha256:d693caaa261214ddd7dbb60d68e71cbed884e68c2be7509778f3051da0b91c3f", size = 2512, upload-time = "2026-04-14T00:50:08.173Z" }, ] [package.optional-dependencies] +cublas = [ + { name = "nvidia-cublas", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cuda-nvrtc", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] cudart = [ - { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, ] cufft = [ - { name = "nvidia-cufft", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cufft", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, ] cufile = [ - { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cufile", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, ] cupti = [ - { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, ] curand = [ - { name = "nvidia-curand", marker = "sys_platform == 'linux'" }, + { name = "nvidia-curand", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, ] cusolver = [ - { name = "nvidia-cusolver", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cublas", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cusolver", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cusparse", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, ] cusparse = [ - { name = "nvidia-cusparse", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparse", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, ] nvjitlink = [ - { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, ] nvrtc = [ - { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, ] nvtx = [ - { name = "nvidia-nvtx", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvtx", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, ] [[package]] @@ -781,7 +790,7 @@ wheels = [ [[package]] name = "fastapi" -version = "0.138.1" +version = "0.139.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -790,9 +799,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/c9/5e8defe249899c0dc900643695fc07829a67fc88b4ff2cdb03fcbdbf5a4b/fastapi-0.138.1.tar.gz", hash = "sha256:96e3702dce09ee0dce48856135620d3d865ca684a79fe7513fd7b13a12f82862", size = 419646, upload-time = "2026-06-25T15:40:42.115Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/95/d3f0ae10836324a2eab98a52b61210ac609f08200bf4bb0dc8132d32f78a/fastapi-0.139.2.tar.gz", hash = "sha256:333145a6891e9b5b3cfceb69baf817e8240cde4d4588ae5a10bf56ffacb6255e", size = 423428, upload-time = "2026-07-16T15:06:17.912Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/a9/69a6924f645eb4dd8cd625bf255b3625990eb3e14e073438a53c405dcd3e/fastapi-0.138.1-py3-none-any.whl", hash = "sha256:b994cae7ba8b82c976a728b544244de31333fa5f7d261f9a1dffe526444cae23", size = 129182, upload-time = "2026-06-25T15:40:40.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c7/cb03251d9dfb177246a9809a76f189d21df32dbd4a845951881d11323b7f/fastapi-0.139.2-py3-none-any.whl", hash = "sha256:b9ad015a835173d59865e2f5d8296fbc2b317bf56a2ba1a5bfbdd03de2fd4b1c", size = 130234, upload-time = "2026-07-16T15:06:19.557Z" }, ] [package.optional-dependencies] @@ -810,16 +819,16 @@ standard = [ [[package]] name = "fastapi-cli" -version = "0.0.27" +version = "0.0.32" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "rich-toolkit" }, { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/37/d0/ee5678346811967b8d096d5d5604e71b50d6bf5a2abfbdb331157e2bbaa9/fastapi_cli-0.0.27.tar.gz", hash = "sha256:1dffb1e40c0c88f2e0171a8a252a2b615c1e63ff8c05626649e4badd6a84336a", size = 23630, upload-time = "2026-06-18T14:48:43.421Z" } +sdist = { url = "https://files.pythonhosted.org/packages/33/eb/3b534c6f8e157f9ddbf2a153512307c886cad0b258739c200dd8ff8c4452/fastapi_cli-0.0.32.tar.gz", hash = "sha256:38024d2345275e1b37ce8848727a580d84901b570e96b3256d9d36a9a5039424", size = 26636, upload-time = "2026-07-16T12:16:58.678Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/ab/0a709f9488fe62647db80f8a277fb0ee62e85adc6746abf477ed373c9eb7/fastapi_cli-0.0.27-py3-none-any.whl", hash = "sha256:2e389a40f318e29fec8cb1e289f267f17c048876fb82dbfa869a10b16740495d", size = 13070, upload-time = "2026-06-18T14:48:44.311Z" }, + { url = "https://files.pythonhosted.org/packages/d5/53/56ae5ae17bb0a5d89d1d31e5320eb1865553ebbfbde91cdc4c221245f2a8/fastapi_cli-0.0.32-py3-none-any.whl", hash = "sha256:8dcc286fa32f01bbd3f65dd09cfd5a2540ed5f2230b77db7fd30978d6165f3c4", size = 14670, upload-time = "2026-07-16T12:16:57.297Z" }, ] [package.optional-dependencies] @@ -830,7 +839,7 @@ standard = [ [[package]] name = "fastapi-cloud-cli" -version = "0.21.0" +version = "0.22.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "detect-installer" }, @@ -843,9 +852,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/c3/5cd0b824ca4b4fd073ce998321881ed68b8e0b85d441653eb1bd997c6fc0/fastapi_cloud_cli-0.21.0.tar.gz", hash = "sha256:463350dcbdcbb9b3baa32a99a3db0610f34a3ae143dd1d35a71173aec5dd245d", size = 93804, upload-time = "2026-06-24T15:23:07.554Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/f2/36bfe990baa656de89a2b98a77a15dcd018474f7245c8e4a10cada0553c5/fastapi_cloud_cli-0.22.2.tar.gz", hash = "sha256:7ec78c1fed58f578af5eb1fb54ec4b456eba4dc1eaca1c3a93cf499a7cbc7ab3", size = 94480, upload-time = "2026-07-14T09:27:01.349Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/a0/2ec3fb041fd41281073cb11ed6c63a33bc1f7a315554e26256a9709dbe47/fastapi_cloud_cli-0.21.0-py3-none-any.whl", hash = "sha256:ea49208fb301b2298e6ddaf9225163cf6dbb255bc9126001242f3cd21486bbe6", size = 76195, upload-time = "2026-06-24T15:23:06.609Z" }, + { url = "https://files.pythonhosted.org/packages/ae/25/e01631a63a5213fc783e5b84e8a27eca800f21549aa414644a0a29181045/fastapi_cloud_cli-0.22.2-py3-none-any.whl", hash = "sha256:37c6b05adb94a4c9f59916a559d041c565db36b5f3dddeed420bf0a51182c363", size = 77744, upload-time = "2026-07-14T09:27:02.571Z" }, ] [[package]] @@ -874,7 +883,7 @@ wheels = [ [[package]] name = "fastmcp-slim" -version = "3.4.2" +version = "3.4.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "platformdirs" }, @@ -884,9 +893,9 @@ dependencies = [ { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/2e/d627b28b7403ecc526991ef732921b08bde010006e6148635f053fd29f4c/fastmcp_slim-3.4.2.tar.gz", hash = "sha256:290646e0955a516235a317151034559aa48336cb843d3f006131aedad8759bb4", size = 576291, upload-time = "2026-06-06T01:30:12.553Z" } +sdist = { url = "https://files.pythonhosted.org/packages/45/79/f35661c6a1d76dfbe17a079f912d96fffcfdd40fad5a9144bb9e7dfb1fdf/fastmcp_slim-3.4.4.tar.gz", hash = "sha256:dcaa3e0be2127d7eacdce592c2ef0039204923dc0ec396454615cb4a3275b078", size = 590203, upload-time = "2026-07-09T00:32:20.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/58/22afebf18df7260b09148199cbeb90cdcc4b3a4e1b5d7460e3591c3a7add/fastmcp_slim-3.4.2-py3-none-any.whl", hash = "sha256:bdc72492212681ca502755fa8acc0457f559295da1fc3dfc0599adc1c04b82f3", size = 749195, upload-time = "2026-06-06T01:30:11.22Z" }, + { url = "https://files.pythonhosted.org/packages/16/91/321e0b2e9ed70d0628b17ddaec76fc7b09f3e1d5d290f70bf101a2890142/fastmcp_slim-3.4.4-py3-none-any.whl", hash = "sha256:9d3a6327b9ee835188eb7323fc3b5d4cd061631b48da8ece56794bb538972505", size = 765158, upload-time = "2026-07-09T00:32:19.11Z" }, ] [package.optional-dependencies] @@ -902,11 +911,11 @@ client = [ [[package]] name = "filelock" -version = "3.29.4" +version = "3.30.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e6/dc/be6cbe99670cd6e4ad387123647cb08e0c32975e223f82551e914c5568a6/filelock-3.29.4.tar.gz", hash = "sha256:10cdb3656fc44541cdf30652a93fb10ec6b05325620eb316bd26893e4201538a", size = 63028, upload-time = "2026-06-13T16:12:00.744Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/b7/e82e2bf111ba6ea135062e556f2ed7f4ff1c17a6c385e95ffddd99885535/filelock-3.30.1.tar.gz", hash = "sha256:6ad1b1ec6a5e2c91e02979b7d1096e5e964d83ad8a55679bacd1d96f917fda6e", size = 176008, upload-time = "2026-07-16T16:59:22.201Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/37/a065dc3bd6e49423a6532c642ca7378d3f467b1ef44c2800c937af7f9739/filelock-3.29.4-py3-none-any.whl", hash = "sha256:dac1648087d5115554850d113e7dd8c83ab2d38e3435dde2d4f163847e57b767", size = 42757, upload-time = "2026-06-13T16:11:59.582Z" }, + { url = "https://files.pythonhosted.org/packages/90/5e/6060a5791d3d5b54aae21c510eb2f76a51e1b28c2122cc1e39b12237e2e7/filelock-3.30.1-py3-none-any.whl", hash = "sha256:69b98bab51bed4c86b141ef338f85ca07937573e8a7798f7fe6a31ccd94b9338", size = 93698, upload-time = "2026-07-16T16:59:20.727Z" }, ] [[package]] @@ -978,28 +987,28 @@ wheels = [ [[package]] name = "genai-prices" -version = "0.0.67" +version = "0.0.71" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx2" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/17/9e/f96ad08d62f7bd33a5b24e65d4eb220569714b9a2a8813ada2e1fa47b4dd/genai_prices-0.0.67.tar.gz", hash = "sha256:54e07eb6541fda377187a471c5dba21a81b439c57f8dc44d89db3103c29ca343", size = 80015, upload-time = "2026-06-24T20:16:23.661Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/e4/5072862613fba039da2b7c981a8649c6c6bbcb2863bd8bc81617c09ce5ee/genai_prices-0.0.71.tar.gz", hash = "sha256:de4db34ec38404f9ef383cb1ab29e204d16ccf27071af0b16d5747ee7affe36b", size = 82105, upload-time = "2026-07-10T00:38:30.491Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/05/d1ca6b960a3305f86d1c5f4274f2ddf8c94611ec7edc436a01cd38a01742/genai_prices-0.0.67-py3-none-any.whl", hash = "sha256:08977f1e83b4132abcfc60dabf21ff13c2d25958afb9199e59c4407bf5c9ed3f", size = 82495, upload-time = "2026-06-24T20:16:22.4Z" }, + { url = "https://files.pythonhosted.org/packages/0d/98/c06c1318f6834a26268a2d4280e4183f60c5ea92152aea841feff29826ff/genai_prices-0.0.71-py3-none-any.whl", hash = "sha256:1d13111563af2b1ce43ccfacf77b7ac3216ad704c644408a56e11b181fe0d128", size = 84586, upload-time = "2026-07-10T00:38:29.252Z" }, ] [[package]] name = "google-auth" -version = "2.55.1" +version = "2.55.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "pyasn1-modules" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/6f/f3f4ac177c67bbee8fe8e88f2ab4f36af88c44a096e165c5217accf6e5d3/google_auth-2.55.1.tar.gz", hash = "sha256:fb2d9b730f2c9b8d326ec8d7222f21aef2ead15bf0513793d6442485d87af0a1", size = 349527, upload-time = "2026-06-25T23:39:27.182Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/b9/e370d86fea3da13ec0256df30323dd26c0cb9c8c85f0c6ec42ac9df0106b/google_auth-2.55.2.tar.gz", hash = "sha256:97ae7790ff740f2bc9db60eb864a7804f4ac19f5f02c38b3d942f2fea6e9b9ae", size = 361414, upload-time = "2026-07-07T18:43:21.227Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/1d/f6d3ca1ad0725f2e08a1c6915640748a52de2e66596160a4d53b010cccf0/google_auth-2.55.1-py3-none-any.whl", hash = "sha256:eada68dfd52b3b81191827601e2a0c3fa12540c818534b630ddc5355769c3995", size = 252349, upload-time = "2026-06-25T23:38:52.946Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c6/02eb5a337ac316a4c30c012e747bad5cea36e1a876efecdf80865541f7d8/google_auth-2.55.2-py3-none-any.whl", hash = "sha256:d715f265f2cafc6a5f1bf0dc19870d20e3119f6f6682785a250bce3d03d38a3b", size = 256778, upload-time = "2026-07-07T18:43:19.52Z" }, ] [package.optional-dependencies] @@ -1009,7 +1018,7 @@ requests = [ [[package]] name = "google-genai" -version = "2.10.0" +version = "2.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1023,9 +1032,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cd/fe/b796087493c3c55371aa58b9f264841ace5bfdf8c668cafa7afa33c44bec/google_genai-2.10.0.tar.gz", hash = "sha256:77912cd558cd7dfd5b75c25fd1c609e78d7954dde583331104022a46ea90f9ee", size = 600039, upload-time = "2026-06-24T01:33:18.157Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/59/9ea84cbeb8f09694564d3b0ee9dd59003551b308d47b61f251415df93982/google_genai-2.12.1.tar.gz", hash = "sha256:78c25217885d63dc430ca7c4526853512b164a25a93a8a0d0af5b85971aa1db0", size = 636710, upload-time = "2026-07-16T16:15:02.035Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/39/00bcfd94de255d24249401efff4f48d77bf6066b46447e519fa193c0c299/google_genai-2.10.0-py3-none-any.whl", hash = "sha256:d5350311567ae660c24cbc1752aee4b3d660f89c0106d2dcd2a69978c35afe1e", size = 957974, upload-time = "2026-06-24T01:33:16.296Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b4/1369fb413fc2ba7f78acace5590b6e9990c52ab5d1d166aafaa1ae2c28c8/google_genai-2.12.1-py3-none-any.whl", hash = "sha256:686d5ec39bda345151d3ed1bac3915f01f49138b1ea519af2eb98f11cc55ebc4", size = 1023403, upload-time = "2026-07-16T16:14:59.79Z" }, ] [[package]] @@ -1044,11 +1053,14 @@ wheels = [ name = "greenlet" version = "3.5.3" source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/f1/fbbfef6af0bad0548f09bc28948ea3c275b4edb19e17fc5ca9900a6a634d/greenlet-3.5.3.tar.gz", hash = "sha256:a61efc018fd3eb317eeca31aba90ee9e7f26f22884a79b6c6ec715bf71bb62f1", size = 200270, upload-time = "2026-06-26T19:28:24.832Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9b/ff/a620267401db30a50cc8450ee90730e2d4a85658c055c0e760d4ed47fb13/greenlet-3.5.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c8d87c2134d871df96ecdea9cec7cbaab286dadab0f56476e57aaf9e8ac11550", size = 287609, upload-time = "2026-06-26T18:21:14.724Z" }, { url = "https://files.pythonhosted.org/packages/d6/fa/5401ac78021c826a25b6dde0c705e0a8f29b617509f9185a31dac15fbe1b/greenlet-3.5.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2d185dd1621757e70c3861cceffd5317ab4e7ed7eb09c82994828468527ade5", size = 607435, upload-time = "2026-06-26T19:07:11.412Z" }, { url = "https://files.pythonhosted.org/packages/e9/76/1dc144a2e56e65d36405078ed774224375ea520a1870a6e46e08bb4ac7bf/greenlet-3.5.3-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1c514a468149bf8fbbab874188a3535cd8a48a3e353eb53a3d424296f8dbacd3", size = 619787, upload-time = "2026-06-26T19:10:08.396Z" }, + { url = "https://files.pythonhosted.org/packages/57/61/2f5b1adf256d039f5dab8005de8d3d7ad2b0070a3219c0e036b3fbfeb440/greenlet-3.5.3-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9ad04dd75458c6300b047c61b8639092433d205a25a14e310d6582a480efcca1", size = 625580, upload-time = "2026-06-26T19:24:18.344Z" }, { url = "https://files.pythonhosted.org/packages/bf/87/c298cee62df1de4ad7fec32abda73526cff347fd143a6ed4ac369246668a/greenlet-3.5.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:915f887cf2682b66419b879423a2e072634aa7b7dce6f3ada4957cfced3f1e9a", size = 616786, upload-time = "2026-06-26T18:32:19.128Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d9/ab7fc9e543e44d6879b0a6ef9a4b2188940fd180cc65d6f646883ddf7201/greenlet-3.5.3-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:afaabdd554cd7ae9bbb3ca070b0d7fdfd207dbf1d16865f7233837709d354bda", size = 427933, upload-time = "2026-06-26T19:25:38.219Z" }, { url = "https://files.pythonhosted.org/packages/9e/2e/e6f009885ed0705ccf33fe0583c117cfd03cde77e31a596dd5785a30762b/greenlet-3.5.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:766cfd421c13e450feb340cd472a3ed9957d438727b7b4593ad7c76c5d2b0deb", size = 1574316, upload-time = "2026-06-26T19:09:04.273Z" }, { url = "https://files.pythonhosted.org/packages/ef/fe/43fd110b01e40da0adb7c90ac7ea744bef2d43dca00de5095fd2351c2a68/greenlet-3.5.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2ecda9ec22edf38fa389369eaed8c3d37c05f3c54e69f69438dbb2cc1de1458b", size = 1638614, upload-time = "2026-06-26T18:31:46.297Z" }, { url = "https://files.pythonhosted.org/packages/0f/7c/062447147a61f8b4337b156fe70d32a165fcf2f89d7ca6255e572806705c/greenlet-3.5.3-cp313-cp313-win_amd64.whl", hash = "sha256:c82304750f057167ff60d188df1d0cc1764ce9567eadf03e6a7443bcedd0b30b", size = 239850, upload-time = "2026-06-26T18:21:54.613Z" }, @@ -1066,23 +1078,23 @@ wheels = [ [[package]] name = "grpcio" -version = "1.81.1" +version = "1.82.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b0/b5/1ff353970a87eda4c98251e34d2dfd214abd4982dc89119c9252a2a482d2/grpcio-1.81.1.tar.gz", hash = "sha256:6fa10a767143a5e82e8eaab53918af0cd8909a57a27f8cb2288b80a613ac671b", size = 13026582, upload-time = "2026-06-11T12:46:51.673Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/bc/656b89387d6f4ed7e0686c7b64c2ae7e554a759aa58122c8e5fb99392c32/grpcio-1.82.1.tar.gz", hash = "sha256:707b24abd90fcb1e45bcc080577da1dbf9971d107490589b9539af8e1e77b4b5", size = 13187300, upload-time = "2026-07-08T12:36:16.588Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/42/dcc2e4b600538ef18327c0839d56b7d3c3812337c5d710df5877dbb39b1e/grpcio-1.81.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:b10e1ff4756ed27d5a29d7fc79cfce7ef1ff56ad20025b89bac7cf79e09abbbe", size = 6054466, upload-time = "2026-06-11T12:45:48.43Z" }, - { url = "https://files.pythonhosted.org/packages/7b/4a/a36e03210183a8a7d4c80c3936acee679f4bd77d5861f369db47b2cc5f05/grpcio-1.81.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:819edbdcb42ab8598b494bcf0222684bbb7a3c772bd1b1f0be7e029a6063c28e", size = 12048795, upload-time = "2026-06-11T12:45:54.011Z" }, - { url = "https://files.pythonhosted.org/packages/b0/d5/d68e30b29098f63beab6fe501100fe82674ff142b32c672532da86a99b3a/grpcio-1.81.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c5bf2dc311127d91230cc79b92188c082634a06cf66c5234db49a43b910183b0", size = 6599094, upload-time = "2026-06-11T12:45:57.799Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b3/e837954d279754f638a11cca5dcf6b24a005efb398984cefaf7735945a54/grpcio-1.81.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e8ca6a1fcdb2943c9cbc1804a1baf3acb6071d72a471591678ded84218006e14", size = 7307182, upload-time = "2026-06-11T12:46:00.568Z" }, - { url = "https://files.pythonhosted.org/packages/0d/1e/b47957057e729adc6cdf519a47f8be2562b7140e280f1418443eb4022192/grpcio-1.81.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e64dd101d380a115cc5a0c7856788adb535f1a4e21fc543775602f8be95180ae", size = 6810962, upload-time = "2026-06-11T12:46:03.312Z" }, - { url = "https://files.pythonhosted.org/packages/40/26/569868e364e05b19ec8f969da53d230bcd89c962cd198f7c29943155c4d3/grpcio-1.81.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:98a07f9bf591e3a8919797bee1c53f026ba4acd587e5a4404c8e57c9ec36b2a5", size = 7415698, upload-time = "2026-06-11T12:46:06.005Z" }, - { url = "https://files.pythonhosted.org/packages/36/0c/5440a0582cb5653fc42a6e262eeb22700943313f8076f9dc927491b20a59/grpcio-1.81.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c261d74b1a945cf895a9d6eccd1685a8e837531beaab782da4d630a8d12deffb", size = 8407779, upload-time = "2026-06-11T12:46:08.84Z" }, - { url = "https://files.pythonhosted.org/packages/ff/aa/66fe9f39871d766987d869a03ee0842a026f499c7b1e62decb9e78a8088e/grpcio-1.81.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:58ad1131c300d3c9b933802b3cc4dc69d380822935ba50b28703156ea826fbf7", size = 7844521, upload-time = "2026-06-11T12:46:12.171Z" }, - { url = "https://files.pythonhosted.org/packages/f0/9e/69bb7194861bcd28fb3193261d4f9c3831b4446993f002cf59068943e7ab/grpcio-1.81.1-cp313-cp313-win32.whl", hash = "sha256:78e29211f26da2fdd0e9c6d2b79f489476140cf7029b6a64808ade7ca4156a42", size = 4182786, upload-time = "2026-06-11T12:46:15.192Z" }, - { url = "https://files.pythonhosted.org/packages/0d/20/3da8bb0d637feccdc3e1e419bb511ce93651ce7d54164f95de22cc0b8b34/grpcio-1.81.1-cp313-cp313-win_amd64.whl", hash = "sha256:edb59506291b647a30884b1d51a599d605f40b20af4a7dc3d33786a47a31de60", size = 4928648, upload-time = "2026-06-11T12:46:17.823Z" }, + { url = "https://files.pythonhosted.org/packages/1b/3e/496992d08c0aaa11272eb6228dc8ab947da01fe835de243cd00521bce4c4/grpcio-1.82.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:b454a2d97bfab7565683a02345f86bd182ab69fd7c2bdb7414171e7538f266b1", size = 6146068, upload-time = "2026-07-08T12:35:21.365Z" }, + { url = "https://files.pythonhosted.org/packages/e7/8f/f263d6f14fdba6b56cfadd91fd3e158a52682b72c6016d1f8723d435659f/grpcio-1.82.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:3dde70abfc80b3be11de53ba0d601c439e7fb2afd3583ad1788d1146bec92fdc", size = 11948600, upload-time = "2026-07-08T12:35:24.312Z" }, + { url = "https://files.pythonhosted.org/packages/8c/14/3a02e6ee49c2d85bc15eaae321e0e11ab3542cad3c5b2de121ecce0c4296/grpcio-1.82.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5523099c98c292ea1ae08e617249db760c56a78f8deae879027fe7d1ffbcbf6", size = 6714591, upload-time = "2026-07-08T12:35:27.027Z" }, + { url = "https://files.pythonhosted.org/packages/69/80/58e3738696f48ab7645347b98d8a7f93d10e00e6218388fbfcd6c9310e3d/grpcio-1.82.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:5e5c4dc0a59b0f8490a6bdfd6fc8395b9d8ad8a8407c7d67ca7b5bba15c0877f", size = 7454995, upload-time = "2026-07-08T12:35:29.599Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6c/2557c1a889363072fbf2285ecd0e8c44860d4dbd60f017a32537c5b863e2/grpcio-1.82.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c40d94ba820329cc191981bc22fa6f6eed0799c6d921f3c6709521d59d4a2fd7", size = 6888621, upload-time = "2026-07-08T12:35:32.38Z" }, + { url = "https://files.pythonhosted.org/packages/d2/66/907706ccaff1223f1e10fd5b37fc16faead43392fccb4e786e7e390ac141/grpcio-1.82.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4c816180e31e273caaec6f8bd86a8392499d5bbb26f41da44e3dce48bde69095", size = 7505069, upload-time = "2026-07-08T12:35:35.072Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7c/ff97b0d0f635987ee5ec80dfedafa1aad629303745d48e8637d10eec5b80/grpcio-1.82.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e31fd780b261830720cb70b0fd8f0aa51d49e75a66d7464ad2e31d4b765f2580", size = 8535384, upload-time = "2026-07-08T12:35:37.954Z" }, + { url = "https://files.pythonhosted.org/packages/62/9e/a97fddd970a8d1588cade06eca20443761c1858b0ad6590a5c835aa18062/grpcio-1.82.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d76152d7c31d7210d4a106e5d8b64da5bba5d6abf11be30e2f7b0a0c59bbcbf", size = 7910707, upload-time = "2026-07-08T12:35:40.797Z" }, + { url = "https://files.pythonhosted.org/packages/20/e4/eaba1517888af483a88d449eb7566f0f7f63446d46f339c5891798435875/grpcio-1.82.1-cp313-cp313-win32.whl", hash = "sha256:38e9dcb5258226fb3282630b31b16a968df52c8c6ad514af540646e0a4578f8a", size = 4240363, upload-time = "2026-07-08T12:35:43.298Z" }, + { url = "https://files.pythonhosted.org/packages/b0/42/66a98d47732e35290bef722f6149fed3709cd4cf61166f6f53a12f417302/grpcio-1.82.1-cp313-cp313-win_amd64.whl", hash = "sha256:3dbfb52c36d9511ac2b8e6c94fdde837b393ae520cc321f52a333a2deedf5a90", size = 5000980, upload-time = "2026-07-08T12:35:46.262Z" }, ] [[package]] @@ -1187,15 +1199,15 @@ wheels = [ [[package]] name = "httpcore2" -version = "2.5.0" +version = "2.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "h11" }, { name = "truststore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/47/06/5c12df521b5322fb1114a83d46911b2fbcb8855ddb3a635f11c01a214af5/httpcore2-2.5.0.tar.gz", hash = "sha256:88aa170137c17328d5ac44234f9fd10706466d5fb347f3edac4d39b91137b09d", size = 64808, upload-time = "2026-06-25T14:16:56.472Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/fe/6a3f9f1a8bb8733326140737446aaf72fddb8b54b8f202302f5c84960613/httpcore2-2.7.0.tar.gz", hash = "sha256:6dc0fedf329a52a990930a5579edfebaea81118ea700ea0dd7de2b5e5be49efc", size = 65593, upload-time = "2026-07-14T20:40:01.111Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/a1/7564199d1a8728fe737b0a72e5b3f8d92dfe085a74ddf7cdd83bce5f206d/httpcore2-2.5.0-py3-none-any.whl", hash = "sha256:5ce35188de461d31e8d000bfb8ef8bf22c6c16587a211e5571deaa5e9bdf842a", size = 80330, upload-time = "2026-06-25T14:16:53.634Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6c/62e2e279e63fc4f7a5ee841ef13175a8bbc613f258e9dcc186e9de803a42/httpcore2-2.7.0-py3-none-any.whl", hash = "sha256:1452f589fe23f55b44546cd884294c41a29330af902bc0b71a761fd52d18f92b", size = 81506, upload-time = "2026-07-14T20:39:58.053Z" }, ] [[package]] @@ -1239,7 +1251,7 @@ wheels = [ [[package]] name = "httpx2" -version = "2.5.0" +version = "2.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1247,14 +1259,14 @@ dependencies = [ { name = "idna" }, { name = "truststore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d0/e2/b5dedc0cf35aa65de5f541ccd30d2bc1fd7f1d43c9ab09f8ed9a7342317b/httpx2-2.5.0.tar.gz", hash = "sha256:e2df9cb4611021527ff8a675b1c320b610a2ec397acc8d6fe6e91df2d9b33c29", size = 83121, upload-time = "2026-06-25T14:16:57.491Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/4a/129b2e21b90ac2985d3928d96792bccc39bc6dfe796c5eee2d8ec06d4105/httpx2-2.7.0.tar.gz", hash = "sha256:8b30709aed5c8465b0dd3b95c09ce301c8f79e7e7a2d00ab0af551e0d0375b07", size = 94487, upload-time = "2026-07-14T20:40:02.318Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/22/859d8252dad9bc9adee34b52e62cde621ece07b042ccb2ab4da1be46695f/httpx2-2.5.0-py3-none-any.whl", hash = "sha256:3d2d4d9cf4b61f1a1f46a95947cfdb47e80cb56a2f91c6256ac8f58e4891df41", size = 76652, upload-time = "2026-06-25T14:16:55.23Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b8/c341bba6411bdfda786020343c47a75ef472f6085caf82391b142b1a3ad9/httpx2-2.7.0-py3-none-any.whl", hash = "sha256:ed2a2719c696789e09493bd8e2bec3d8bd925cc6e26b68389ec25ade132f7bf4", size = 90234, upload-time = "2026-07-14T20:39:59.531Z" }, ] [[package]] name = "huggingface-hub" -version = "1.21.0" +version = "1.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -1265,12 +1277,11 @@ dependencies = [ { name = "packaging" }, { name = "pyyaml" }, { name = "tqdm" }, - { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8f/77/ce3331f40cb2d021fe9b24c46c41e72faf74493621138e5eddac12bf5e1c/huggingface_hub-1.21.0.tar.gz", hash = "sha256:a44f222cd8f2f7c2eade30b5e7a04cac984a3235fa61ea87a0a5a31db77d561f", size = 861572, upload-time = "2026-06-25T13:09:26.356Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/8f/999e4dda11c6187c78f090eac00895a47e11a0049308f07579bcb7aa3aa2/huggingface_hub-1.23.0.tar.gz", hash = "sha256:c04997fb8bbdace1e57b7703d30ed7678af51f70d00d241819ff411b92ae9a88", size = 919163, upload-time = "2026-07-09T14:49:32.315Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/85/b505a99a133d9f99d21af182af416e9baef70bdeef019983479651e494c2/huggingface_hub-1.21.0-py3-none-any.whl", hash = "sha256:eadaa3678c512c82aea69e8675d90a184861e68de32f1105668628b4dce0e7cd", size = 721078, upload-time = "2026-06-25T13:09:24.402Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ce/13b2ba57838b8db1e6bd033c1b21ce0b9f6153b87d4e4939f77074e41eb0/huggingface_hub-1.23.0-py3-none-any.whl", hash = "sha256:b1d604788f5adc7f0eb246e03e0ec19011ca06e38400218c347dccc3dffa64a2", size = 770336, upload-time = "2026-07-09T14:49:30.597Z" }, ] [[package]] @@ -1346,14 +1357,14 @@ wheels = [ [[package]] name = "jaraco-functools" -version = "4.5.0" +version = "4.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "more-itertools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/cf/ea4ef2920830dea3f5ab2ea4da6fb67724e6dca80ee2553788c3607243d0/jaraco_functools-4.5.0.tar.gz", hash = "sha256:3bb5665ea4a020cf78a7040e89154c77edadb3ca74f366479669c5999aa70b03", size = 20272, upload-time = "2026-05-15T21:34:10.025Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/1f/c23395957d41ccf27c4e535c3d334c4051e5395b3752057ba4cbaec35c56/jaraco_functools-4.6.0.tar.gz", hash = "sha256:880c577ec9720b3a052d5bc611fb9f2269b3d87902ef42440df443b88e443280", size = 20837, upload-time = "2026-07-14T01:28:02.544Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/9a/982e48afcffcd727a9144506720ffd4224b6b7e355c98641866f38b7c043/jaraco_functools-4.5.0-py3-none-any.whl", hash = "sha256:79ce39246eddbde4b3a03b77ea5f0f7878dc669b166a66cf3fa8e266aa3fa2f4", size = 10594, upload-time = "2026-05-15T21:34:08.595Z" }, + { url = "https://files.pythonhosted.org/packages/02/36/ecc85bc96c273dc8a11273ed4782272975e6338d4a3e9228621175edf0e3/jaraco_functools-4.6.0-py3-none-any.whl", hash = "sha256:99e3dc0060c5cbe8fcd1cdb36258e2a65ca40f1566b2033b12abb1bb44dd3c30", size = 11677, upload-time = "2026-07-14T01:28:01.59Z" }, ] [[package]] @@ -1379,29 +1390,24 @@ wheels = [ [[package]] name = "jiter" -version = "0.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/b5/55f06bb281d92fb3cc86d14e1def2bd908bb77693183e7cb1f5a3c388b0c/jiter-0.15.0.tar.gz", hash = "sha256:4251acc80e2b7c9b7b8823456ea0fceeb0734dac2df7636d3c711b38476b5a76", size = 166640, upload-time = "2026-05-19T10:09:48.361Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/f4/f708c900ecee41b2025ef8413d5351e5649eb2125c506f6720cc69b06f5c/jiter-0.15.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1c11465f97e2abf45a014b83b730222f8f1c5335e802c7055a67d50de6f1f4e3", size = 307829, upload-time = "2026-05-19T10:07:59.704Z" }, - { url = "https://files.pythonhosted.org/packages/86/59/db537c0949e83668c38481d426b9f2fd5ab758c4ee53a811dd0a510626a0/jiter-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e7b1776f0797956c509e123d0952d10d293a9492dea9f288ab9570ec01d1a5", size = 308445, upload-time = "2026-05-19T10:08:01.184Z" }, - { url = "https://files.pythonhosted.org/packages/37/38/ea0e13b18c30ef951da0d47d39e7fa9edb82a93a62990ffbd7cea9b622d4/jiter-0.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:351a341c2105aa430b7047e30f1bf7975f6313b00165d3fc07be2edaf741f279", size = 336181, upload-time = "2026-05-19T10:08:02.688Z" }, - { url = "https://files.pythonhosted.org/packages/58/fc/2303901b16c4ba05865588990a420c0b4156270b44379c20931544a1d962/jiter-0.15.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ab395feec8d249ec4044e228e98a7033f043426a265df439dc3698823f0a4e4", size = 362985, upload-time = "2026-05-19T10:08:04.394Z" }, - { url = "https://files.pythonhosted.org/packages/5b/6f/11bace093c52e7d4d26c8e606ccd7ae8c972189622469ec0d9e28161e28b/jiter-0.15.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2a438005b6f22d0273413484d6094d7c2c5d10ec1b3a3bf128e0d1d3ba53258", size = 453292, upload-time = "2026-05-19T10:08:05.967Z" }, - { url = "https://files.pythonhosted.org/packages/22/db/987f2f086ca4d7a6582eb4ccd513f9b26b42d9e4243a087609a3137a8fc7/jiter-0.15.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f18f85e4218d1b40f000f42a92239a7a61a902cd42c65e6c360dbd17dcb20894", size = 373501, upload-time = "2026-05-19T10:08:07.857Z" }, - { url = "https://files.pythonhosted.org/packages/8f/7c/89fbcabb2739b7a5b8dc959a1b6c5761f6484f5fed3486854b3c789bb1de/jiter-0.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1aa62e277fc1cbd80e6deacae6f4d983b41b3d7728e0645c5d741a6149bba45", size = 344683, upload-time = "2026-05-19T10:08:09.431Z" }, - { url = "https://files.pythonhosted.org/packages/30/6f/6cca7692e7dddfec6d8d76c54dc97f2af2a41df4ac0674b999df1f09a5f3/jiter-0.15.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:6550fa135c7deb8ead6af49ed7ff648532ea8334a1447fe34a36315ef79c5c29", size = 350892, upload-time = "2026-05-19T10:08:11.352Z" }, - { url = "https://files.pythonhosted.org/packages/39/14/0338d6190cb8e6d22e677ab1d4eabd4117f67cca70c54cd04b82ff64e068/jiter-0.15.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:066f8f33f18b2419cd8213b2436fa7fbc9c499f315971cfa3ce1f9820c001b1b", size = 388723, upload-time = "2026-05-19T10:08:12.912Z" }, - { url = "https://files.pythonhosted.org/packages/90/31/cc19f4a1bdb6afb09ce6a2f2615aa8d44d994eba0d8e6105ed1af920e736/jiter-0.15.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:75e8a04e91432dde9f1838373cf93d23726c79d3e908d319acf0e796f85592e7", size = 516648, upload-time = "2026-05-19T10:08:14.808Z" }, - { url = "https://files.pythonhosted.org/packages/49/9f/833c541512cd091b63c10c0381973dfe11bc7a503a818c16384417e0c81e/jiter-0.15.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a97261f1fccb8e50ecd2890a96e46efdc3f57c80a197324c6777827231eca712", size = 547382, upload-time = "2026-05-19T10:08:16.927Z" }, - { url = "https://files.pythonhosted.org/packages/d2/11/e7b70e91f90bc4477e8eee9e8a5f7cf3cb41b4525d6394dc98a714eb8f7f/jiter-0.15.0-cp313-cp313-win32.whl", hash = "sha256:c77496cb10bd7549690fbbab3e5ec05857b83e49276f4a9423a766ddd2afcd4c", size = 205845, upload-time = "2026-05-19T10:08:18.401Z" }, - { url = "https://files.pythonhosted.org/packages/4b/23/5c20d9ad6f02c493e4023e5d2d09e1c1f15fe2753c9102c544aff068a88e/jiter-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b15741f501469009ae0ae90b7147958a664a7dede40aa7ff174a8a4645f546d0", size = 196842, upload-time = "2026-05-19T10:08:20.131Z" }, - { url = "https://files.pythonhosted.org/packages/6b/11/1eb400ef248e8c925fd883fbe325daf5e42cd1b0d308539dd332bd4f7ffc/jiter-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d6a60072b44c3c2b797a7ddcbcbbf2b34ea3cfd4721580fbfd2a09d9d9b84ba", size = 192212, upload-time = "2026-05-19T10:08:21.807Z" }, - { url = "https://files.pythonhosted.org/packages/8a/60/2fd8d7c79da8acf9b7b277c7616847773779356b92acfc9bb158452174da/jiter-0.15.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ef1fd24d9413f6209e00d3d5a453e67acfe004a25cc6c8e8484faed4311ab9e8", size = 315065, upload-time = "2026-05-19T10:08:23.218Z" }, - { url = "https://files.pythonhosted.org/packages/46/f4/008fb7d65e8ac2abf00811651a661e025c4ba80bbc6f378450384ddd3aed/jiter-0.15.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:144f8e72cb53dab146347b91cceac01f5481237f2b93b4a339a1ee8f8878b67c", size = 339444, upload-time = "2026-05-19T10:08:24.701Z" }, - { url = "https://files.pythonhosted.org/packages/00/55/90b0c7b9c6896c0f2a591dd36d36b71d22e09674bfef178fa03ba3f81499/jiter-0.15.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553fcac2ef2cb990877f9fc0833b8b629a3e6a5670b6b5fd58219b41a653ddc4", size = 347779, upload-time = "2026-05-19T10:08:26.408Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/69666cec5000fd57734c118437394516c749ae8dbeea9fb66d6fef9c4775/jiter-0.15.0-cp313-cp313t-win_amd64.whl", hash = "sha256:774f93f65031856bf14ad9f59bdcab8b8cad501e5ceabd51ba3525f76937a25b", size = 200395, upload-time = "2026-05-19T10:08:28.055Z" }, - { url = "https://files.pythonhosted.org/packages/39/04/a6aa62cd27e8149b0d28df5561f10f6cceaf7935a9ccf3f1c5a05f9a0cd8/jiter-0.15.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f1e1754960f38ec40613a07e5e372df67acb3b890fb383b6fb3de3e49ddbf3c7", size = 190516, upload-time = "2026-05-19T10:08:29.35Z" }, +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/1f/10936e16d8860c70698a1aa939a46aa0224813b782bce4e000e637da0b2d/jiter-0.16.0.tar.gz", hash = "sha256:7b24c3492c5f4f84a37946ad9cf504910cf6a782d6a4e0689b6673c5894b4a1c", size = 176431, upload-time = "2026-06-29T13:05:13.657Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/c0/555fc60473d30d66894ba825e63615e3be7524fac23858356afa7a38906c/jiter-0.16.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:41977aa5654023948c2dae2a81cbf9c43343954bef1cd59a154dd15a4d84c195", size = 306203, upload-time = "2026-06-29T13:03:36.243Z" }, + { url = "https://files.pythonhosted.org/packages/d0/2b/c3eaf16f5d7c9bad66ea32f40a95bd169b29a91217fcc7f081375157e99c/jiter-0.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d28bb3c26762358dadf3e5bf0bccd29ae987d65e6988d2e6f49829c76b003c09", size = 306489, upload-time = "2026-06-29T13:03:37.846Z" }, + { url = "https://files.pythonhosted.org/packages/96/3f/02fdfc6705cad96127d883af5c34e4867f554f29ec7705ec1a46156400a9/jiter-0.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0542a7189c26920778658fc8fcf2af8bae05bae9924577f71804acef37996536", size = 335453, upload-time = "2026-06-29T13:03:39.221Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a6/e4bda5920d4b0d7c5dfb7174ce4a6b2e4d3e11c9162c452ef0eab4cdbdbd/jiter-0.16.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8fb8de1e23a0cb2a7f53c335049c7b72b6db41aa6227cdcc0972a1de5cb39450", size = 361625, upload-time = "2026-06-29T13:03:40.597Z" }, + { url = "https://files.pythonhosted.org/packages/b7/97/4e6b59b2c6e55cbb3e183595f81ad65dcfb21c915fee5e19e335df21bc55/jiter-0.16.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b72d0b2990ca754a9102779ac98d8597b7cb31678958562214a007f909eab78e", size = 456958, upload-time = "2026-06-29T13:03:42.074Z" }, + { url = "https://files.pythonhosted.org/packages/15/e0/97e9557686d2f94f4b93786eccb7eed28e9228ad132ea8237f44727314a7/jiter-0.16.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f91b1c27fc22a57993d5a5cb8a627cb8ed4b10502716fac1ffbfe1d19d84e8", size = 372017, upload-time = "2026-06-29T13:03:43.658Z" }, + { url = "https://files.pythonhosted.org/packages/0f/94/db768b6938e0df35c86beeba3dfbbb025c9ee5c19e1aa271f2396e50864d/jiter-0.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c682bea068a90b764577bdb78a60a4c1d1606daf9cd4c893832a37c7cc9d9026", size = 343320, upload-time = "2026-06-29T13:03:45.226Z" }, + { url = "https://files.pythonhosted.org/packages/c1/d6/5a59d938244a30735fe62d9433fd325f9021ea29d89780ea4596ea93bc89/jiter-0.16.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:8d031aabecc4f1b6276adfb42e3aabb77c89d468bf616600e8d3a11328929053", size = 350520, upload-time = "2026-06-29T13:03:46.671Z" }, + { url = "https://files.pythonhosted.org/packages/67/f8/c4a857f49c9af125f6bbcac7e3eee7f7978ed89682833062e2dbf62576b1/jiter-0.16.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eab2cd170150e70153de16896a1774e3a1dca80154c56b54d7a812c479a7165e", size = 387550, upload-time = "2026-06-29T13:03:48.361Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d6/5fbc2f7d6b67b754caa61a993a2e626e815dec47ffc2f9e35f01adfebec7/jiter-0.16.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6edb63a46e65a82c26800a868e49b2cac30dd5a4218b88d74bc2c848c8ad60bb", size = 515424, upload-time = "2026-06-29T13:03:49.881Z" }, + { url = "https://files.pythonhosted.org/packages/ed/54/284f0164b64a5fed915fea6ba7e9ba9b3d8d37c67d59cf2e3bb99d45cdfe/jiter-0.16.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:659039cc50b5addcc35fcc87ae2c1833b7c0a8e5326ef631a75e4478447bcf84", size = 546981, upload-time = "2026-06-29T13:03:51.363Z" }, + { url = "https://files.pythonhosted.org/packages/13/c5/2a467585a576594384e1d2c43e1224deaafc085f24e243529cf98beef8e1/jiter-0.16.0-cp313-cp313-win32.whl", hash = "sha256:c9c53be232c2e206ef9cdbad81a48bfa74c3d3f08bcf8124630a8a748aad993e", size = 202853, upload-time = "2026-06-29T13:03:53.015Z" }, + { url = "https://files.pythonhosted.org/packages/88/6a/de61d04b9eec69c71719968d2f716532a3bc121170c44a39e14979c6be81/jiter-0.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:baad945ed47f163ad833314f8e3288c396118934f94e7bbb9e243ce4b341a4fd", size = 196160, upload-time = "2026-06-29T13:03:54.447Z" }, + { url = "https://files.pythonhosted.org/packages/19/4b/b390ed59bafb3f31d008d1218578f10327714484b334439947f7e5b11e7f/jiter-0.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:3c1fd2dbe1b0af19e987f03fe66c5f5bd105a2229c1aff4ab14890b24f41d21a", size = 189862, upload-time = "2026-06-29T13:03:55.754Z" }, ] [[package]] @@ -1415,14 +1421,14 @@ wheels = [ [[package]] name = "joserfc" -version = "1.7.1" +version = "1.7.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/44/90/25cb27518750218e4f850be63d8bbb2343efaad1c01c3571aaa4b3c33bd7/joserfc-1.7.1.tar.gz", hash = "sha256:77d0b76514879c68c6f433bc5b7357a4ab72008ff1e33d8379fd11d72bd8ca81", size = 233181, upload-time = "2026-06-08T07:21:33.412Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/c6/b1cac0280f8efc57626ea8804866b37099f23cae11b1485a42b213245e31/joserfc-1.7.3.tar.gz", hash = "sha256:116955c2587139dba20621fd0bd7fc9255fa960c9fe7f43c43ebef2e801dcfcf", size = 233821, upload-time = "2026-07-08T12:41:42.66Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/00/fa62404c3e347f946faa13aa21085205f9cc06ad17671e37f81a51662ae8/joserfc-1.7.1-py3-none-any.whl", hash = "sha256:b3e3d655612e2e1ef67b2600f2f420e12e537b020208fab1761fad647319c164", size = 70423, upload-time = "2026-06-08T07:21:32.001Z" }, + { url = "https://files.pythonhosted.org/packages/7d/f5/650b59d1b74f5befb7a7a7e7d7c92a26b94256df3541e2b4914152cd177a/joserfc-1.7.3-py3-none-any.whl", hash = "sha256:7c39f3f2c943dbc03122747fa8ebbd8e156e54904cf25651b452f4d2634a6075", size = 70982, upload-time = "2026-07-08T12:41:41.521Z" }, ] [[package]] @@ -1546,23 +1552,24 @@ wheels = [ [[package]] name = "librt" -version = "0.11.0" +version = "0.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/08/9e7f6b5d2b5bed6ad055cdd5925f192bb403a51280f86b56554d9d0699a2/librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1", size = 200139, upload-time = "2026-05-10T18:17:25.138Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/2f/3908645ddddab7120b46295e541ead308109fa48dbec7d67d7a778870d60/librt-0.13.0.tar.gz", hash = "sha256:1d2a610c14ac0d0750ee0a3ab8548e83155258387891caaca04def4bf7289781", size = 211402, upload-time = "2026-07-08T12:26:29.834Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/61/e59168d4d0bf2bf90f4f0caf7a001bfc60254c3af4586013b04dc3ef517b/librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894", size = 144119, upload-time = "2026-05-10T18:16:11.771Z" }, - { url = "https://files.pythonhosted.org/packages/61/fd/caa1d60b12f7dd79ccea23054e06eeaebe266a5f52c40a6b651069200ce5/librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c", size = 143565, upload-time = "2026-05-10T18:16:13.334Z" }, - { url = "https://files.pythonhosted.org/packages/b8/a9/dc744f5c2b4978d48db970be29f22716d3413d28b14ad99740817315cf2c/librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea", size = 485395, upload-time = "2026-05-10T18:16:14.729Z" }, - { url = "https://files.pythonhosted.org/packages/8f/21/7f8e97a1e4dae952a5a95948f6f8507a173bc1e669f54340bba6ca1ca31b/librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230", size = 479383, upload-time = "2026-05-10T18:16:16.321Z" }, - { url = "https://files.pythonhosted.org/packages/a6/6d/d8ee9c114bebf2c50e29ec2aa940826fccb62a645c3e4c18760987d0e16d/librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2", size = 513010, upload-time = "2026-05-10T18:16:17.647Z" }, - { url = "https://files.pythonhosted.org/packages/f0/43/0b5708af2bd30a46400e72ba6bdaa8f066f15fb9a688527e34220e8d6c06/librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3", size = 508433, upload-time = "2026-05-10T18:16:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/4a/50/356187247d09013490481033183b3532b58acf8028bcb34b2b56a375c9b2/librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21", size = 522595, upload-time = "2026-05-10T18:16:20.642Z" }, - { url = "https://files.pythonhosted.org/packages/40/e7/c6ac4240899c7f3248079d5a9900debe0dadb3fdeaf856684c987105ba47/librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930", size = 527255, upload-time = "2026-05-10T18:16:22.352Z" }, - { url = "https://files.pythonhosted.org/packages/eb/b5/a81322dbeedeeaf9c1ee6f001734d28a09d8383ac9e6779bc24bbd0743c6/librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be", size = 516847, upload-time = "2026-05-10T18:16:23.627Z" }, - { url = "https://files.pythonhosted.org/packages/ae/66/6e6323787d592b55204a42595ff1102da5115601b53a7e9ddebc889a6da5/librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e", size = 553920, upload-time = "2026-05-10T18:16:25.025Z" }, - { url = "https://files.pythonhosted.org/packages/9c/21/623f8ca230857102066d9ca8c6c1734995908c4d0d1bee7bb2ef0021cb33/librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e", size = 101898, upload-time = "2026-05-10T18:16:26.649Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1d/b4ebd44dd723f768469007515cb92251e0ae286c94c140f374801140fa74/librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47", size = 119812, upload-time = "2026-05-10T18:16:27.859Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e4/b2f4ca7965ca373b491cdb4bc25cdb30c1649ca81a8782056a83850292a9/librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44", size = 103448, upload-time = "2026-05-10T18:16:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/67/3b/18e7b63255297a2bdc9c25c8d6d4ca8eca9f63aceb1252c0f7427ac7099e/librt-0.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a468951af16155824e88bdd8326ebe5bdb371f3ec0ac04642994b98201d914f3", size = 151027, upload-time = "2026-07-08T12:25:19.638Z" }, + { url = "https://files.pythonhosted.org/packages/4d/68/e2248452c00d1a03b45fee1752cdc8f790a476efd2402b75181da88a9e61/librt-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ae01d8512cc17079e53425635327dbf3f7ff57a42c00dec348bf79791c56444c", size = 155152, upload-time = "2026-07-08T12:25:20.851Z" }, + { url = "https://files.pythonhosted.org/packages/0e/16/52b1c99bf19057a062aac39c900cbb81499f6f75d6c537c14463d247ba78/librt-0.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32c26893cd085c1efe83219e78d866da23fb20a066101b8f68210004361d224c", size = 502499, upload-time = "2026-07-08T12:25:22.055Z" }, + { url = "https://files.pythonhosted.org/packages/9f/54/b811151805c795f55e0dedee6ec687b75f9982a8105d240ea3910737a77b/librt-0.13.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5929da1981a46bcf4b28b1b9499905f0ff58e2419da402a048234e9783acbc4b", size = 496108, upload-time = "2026-07-08T12:25:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/8f/f8/094d6b2bd93f3fdaa54db54cc788c4a365333bddad65ab02e04da0b1d004/librt-0.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b85d664d777bab6c0d709416cb42938251fda9e221b79e3a2215d85df5f4f9", size = 531576, upload-time = "2026-07-08T12:25:24.648Z" }, + { url = "https://files.pythonhosted.org/packages/2e/40/541733d5755824f968f7ec39d78ffbd75d145964157ae5e69a09ec6d7326/librt-0.13.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:531b2df3e9fe96b1fcf73a6d165921e4656be5f58d631d384ebce344298368db", size = 524390, upload-time = "2026-07-08T12:25:25.898Z" }, + { url = "https://files.pythonhosted.org/packages/c6/b5/255673cfdbf5ba663339d36cd863c897289ab4337577e19f9405ce059f36/librt-0.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:109b84a9edf69ad89dc1f66358659e14a031baca95e3e5b0060bd903ede8efd6", size = 543053, upload-time = "2026-07-08T12:25:27.436Z" }, + { url = "https://files.pythonhosted.org/packages/9e/11/ab5005e9c9850710f21e354201bf090646349d3fabf5f951eaf70235729e/librt-0.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1304368a3e7ffc3e9db986796cc5326fdb5943a3567ecc137cff318e4240c0e7", size = 546387, upload-time = "2026-07-08T12:25:28.65Z" }, + { url = "https://files.pythonhosted.org/packages/a2/04/a5d7ce1d1df1afd15ca283dcdf7530ac073e12d69ae8c40879dda96f7868/librt-0.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e4f9b472e7d308d94b62c801982065661158c6ed02790d6c7ddb4337cea0f9c1", size = 535970, upload-time = "2026-07-08T12:25:30.171Z" }, + { url = "https://files.pythonhosted.org/packages/5a/76/927e267a6daa290174ac281b23c9804c8829b042ade9c6f24a065f540958/librt-0.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f836c37478f167a81200d8c8b2c920a22224564bed2c23d7aeec760965c367a", size = 573582, upload-time = "2026-07-08T12:25:31.507Z" }, + { url = "https://files.pythonhosted.org/packages/10/24/b6c5213efe39c19f9e13605644d0cf063b4ddaa33ac2e45b088e23a70e2e/librt-0.13.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:4000d961ff9598ac6ea603c6c836a5ed49bc205ade5fc378b998dfe1e2c36628", size = 82189, upload-time = "2026-07-08T12:25:32.675Z" }, + { url = "https://files.pythonhosted.org/packages/4c/00/d29736be177a906ac0b84a5b04b4fbfa22c776dc2f366de4172b0f968c08/librt-0.13.0-cp313-cp313-win32.whl", hash = "sha256:79e44cff71750d299d61a678e49995b0d5935a9cda238c2574daeca3ba536927", size = 106193, upload-time = "2026-07-08T12:25:33.692Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ac/aff6fb45393cb8912f39dfb156ef6b2d1cadb207ff465fc8f66141054be8/librt-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:54dab44a847d5ad1acd05c8a83fe518ae685516ecf4d3f7cc6e3df2a66767650", size = 126962, upload-time = "2026-07-08T12:25:34.769Z" }, + { url = "https://files.pythonhosted.org/packages/d9/3a/d68cb2b334d53fd30fac81d3a489ce4ba0d9506f4df43fcf676b68352b19/librt-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:d4cb6fbfdf874340ab5e51450753c0f817b6958a3621125ee695bbc3de866566", size = 112127, upload-time = "2026-07-08T12:25:35.981Z" }, ] [[package]] @@ -1596,14 +1603,14 @@ wheels = [ [[package]] name = "llvmlite" -version = "0.47.0" +version = "0.48.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/88/a8952b6d5c21e74cbf158515b779666f692846502623e9e3c39d8e8ba25f/llvmlite-0.47.0.tar.gz", hash = "sha256:62031ce968ec74e95092184d4b0e857e444f8fdff0b8f9213707699570c33ccc", size = 193614, upload-time = "2026-03-31T18:29:53.497Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/a0/acc8ffcd5bdc63df0097e22c719bfcd61b604358343089313a8aebbb24ab/llvmlite-0.48.0.tar.gz", hash = "sha256:543b19f9ef8f3c7c60d1468191e4ee1b1537bf9f8a3d56f64c0ddd98de92edd2", size = 184016, upload-time = "2026-07-02T20:20:05.308Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/6f/4615353e016799f80fa52ccb270a843c413b22361fadda2589b2922fb9b0/llvmlite-0.47.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a3c6a735d4e1041808434f9d440faa3d78d9b4af2ee64d05a66f351883b6ceec", size = 37232771, upload-time = "2026-03-31T18:29:01.324Z" }, - { url = "https://files.pythonhosted.org/packages/31/b8/69f5565f1a280d032525878a86511eebed0645818492feeb169dfb20ae8e/llvmlite-0.47.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2699a74321189e812d476a43d6d7f652f51811e7b5aad9d9bba842a1c7927acb", size = 56275178, upload-time = "2026-03-31T18:29:05.748Z" }, - { url = "https://files.pythonhosted.org/packages/d6/da/b32cafcb926fb0ce2aa25553bf32cb8764af31438f40e2481df08884c947/llvmlite-0.47.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c6951e2b29930227963e53ee152441f0e14be92e9d4231852102d986c761e40", size = 55128632, upload-time = "2026-03-31T18:29:11.235Z" }, - { url = "https://files.pythonhosted.org/packages/46/9f/4898b44e4042c60fafcb1162dfb7014f6f15b1ec19bf29cfea6bf26df90d/llvmlite-0.47.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2e9adf8698d813a9a5efb2d4370caf344dbc1e145019851fee6a6f319ba760e", size = 38138695, upload-time = "2026-03-31T18:29:15.43Z" }, + { url = "https://files.pythonhosted.org/packages/9c/23/fe9316d14626b42c73ef0b502e724705a6ee9450afe53759c0a99c37c2d7/llvmlite-0.48.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a83a99ef0c05b4ccddf9b6218ed9fe84b653a0caf7c1d9dbe148d6d16c67f518", size = 40480652, upload-time = "2026-07-01T18:41:52.216Z" }, + { url = "https://files.pythonhosted.org/packages/1b/4a/90715fa12006d681270b08d881195b6fab3ec39572e048764a1f7f59fed7/llvmlite-0.48.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8761b9e522f55207e24424fcd98370289eec2710bf8e915c82d1053f642450dc", size = 59890120, upload-time = "2026-07-01T18:42:00.748Z" }, + { url = "https://files.pythonhosted.org/packages/70/5e/7b3e20d64650ca3c80af0cdb664ec4b575ec83d9d4dd05bea8bd31f9bbb6/llvmlite-0.48.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2fe5cb59b2063bfa039dcb8ca6481c0181bf552f340d10dcf61d7996a665556e", size = 58343457, upload-time = "2026-07-01T18:41:56.41Z" }, + { url = "https://files.pythonhosted.org/packages/17/97/5a430055d1838cf1fb7a01cfa943300f5e4c026fc6333a522c5e4a03b0c1/llvmlite-0.48.0-cp313-cp313-win_amd64.whl", hash = "sha256:91c7e24e74cde3f02b88aa5acca678373f9e069f3b98531b3dbb3a142d9d10bb", size = 41865022, upload-time = "2026-07-01T18:42:04.57Z" }, ] [[package]] @@ -1818,7 +1825,7 @@ wheels = [ [[package]] name = "modal" -version = "1.5.1" +version = "1.5.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -1835,9 +1842,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "watchfiles" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/56/a8d1a1dd705044e0c3534642361e8586db98908b683f8231b9fd39a86209/modal-1.5.1.tar.gz", hash = "sha256:f8fb7f4d3ccc5b774370704b1aabb79e629ea7e6681a7f9671af5cf77161be12", size = 801962, upload-time = "2026-06-23T15:44:18.853Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b8/e6/44f609084472d16a3acf7748c184e23d3009944eed5860e855aaafc61065/modal-1.5.2.tar.gz", hash = "sha256:ac0d2983ecccacb80c54385906404d75f37a3751f1374c750974cd7853c0008a", size = 818040, upload-time = "2026-07-10T18:12:55.164Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/67/c91cb2ee6cbae523ae051f5ecfebd9fc24518fb84b9e92859d32fbbe3a71/modal-1.5.1-py3-none-any.whl", hash = "sha256:49aeda1a4fafc71994c3aa63d3e2321419b586de0303113fa5bfc68f31ad5c95", size = 915761, upload-time = "2026-06-23T15:44:16.48Z" }, + { url = "https://files.pythonhosted.org/packages/e3/fe/7e284d889eb3ea2c307e48828eccdd9a984beb2e096bce5577ef796592e9/modal-1.5.2-py3-none-any.whl", hash = "sha256:7508a44f8742c6f26be4d28da71f46092ef3bcc07d0f27cbd0ea8c9d6d9fc873", size = 932258, upload-time = "2026-07-10T18:12:52.219Z" }, ] [[package]] @@ -1924,7 +1931,7 @@ wheels = [ [[package]] name = "mypy" -version = "2.1.0" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ast-serialize" }, @@ -1933,16 +1940,16 @@ dependencies = [ { name = "pathspec" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/15/cca9d88503549ed6fedeaa1d448cdddd542ee8a490232d732e278036fbf2/mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633", size = 3898359, upload-time = "2026-05-11T18:37:36.237Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/af/4e516a05d3ca2eb9283e9ec45b2c02225c1514dd6da49fd3c9eaa6639370/mypy-2.3.0.tar.gz", hash = "sha256:465965d41cd9a2726694e983e8ce7113259327bec798115d1e1dfa2a52fb666e", size = 3988104, upload-time = "2026-07-13T11:34:53.387Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/dd/c7191469c777f07689c032a8f7326e393ea34c92d6d76eb7ce5ba57ea66d/mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5", size = 14852174, upload-time = "2026-05-11T18:31:38.929Z" }, - { url = "https://files.pythonhosted.org/packages/55/8c/aed55408879043d72bb9135f4d0d19a02b886dd569631e113e3d2706cb8d/mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e", size = 13651542, upload-time = "2026-05-11T18:36:04.636Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8e/f371a824b1f1fa8ea6e3dbb8703d232977d572be2329554a3bc4d960302f/mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e", size = 14033929, upload-time = "2026-05-11T18:35:55.742Z" }, - { url = "https://files.pythonhosted.org/packages/94/21/f54be870d6dd53a82c674407e0f8eed7174b05ec78d42e5abd7b42e84fd5/mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285", size = 15039200, upload-time = "2026-05-11T18:33:10.281Z" }, - { url = "https://files.pythonhosted.org/packages/17/99/bf21748626a40ce59fd29a39386ab46afec88b7bd2f0fa6c3a97c995523f/mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5", size = 15272690, upload-time = "2026-05-11T18:32:07.205Z" }, - { url = "https://files.pythonhosted.org/packages/d6/d7/9e90d2cf47100bea550ed2bc7b0d4de3a62181d84d5e37da0003e8462637/mypy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65", size = 11147435, upload-time = "2026-05-11T18:33:56.477Z" }, - { url = "https://files.pythonhosted.org/packages/ec/46/e5c449e858798e35ffc90946282a27c62a77be743fe17480e4977374eb91/mypy-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d", size = 10035052, upload-time = "2026-05-11T18:32:30.049Z" }, - { url = "https://files.pythonhosted.org/packages/0d/2a/13ca1f292f6db1b98ff495ef3467736b331621c5917cad984b7043e7348d/mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289", size = 2693302, upload-time = "2026-05-11T18:31:29.246Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ae/f7d056eb0294586a572d0d0d89580ec633c064db520f11d37d5a2fb833bd/mypy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:91ad22a52ae2c7e621c2f67c94d5a17f66b3209a4cff5cf8a573579835c69e97", size = 14947298, upload-time = "2026-07-13T11:27:47.734Z" }, + { url = "https://files.pythonhosted.org/packages/32/d5/db3e7af01e7844d21662c6ddc1f7825ec7cb4053f0391ac02faf3638396f/mypy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:99ac767cc5d3b64c8d0ae226ead10c96694f94e4e7da1668642225dcd4e75aac", size = 13950768, upload-time = "2026-07-13T11:27:57.726Z" }, + { url = "https://files.pythonhosted.org/packages/d9/fb/43c031f0190513d1ec248ed037eceb742ddd2a4d74bbf406658a28173837/mypy-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de6d2c484742a4d7b0ed6d07b143375624d3b899c5749c7b3c947f56261f48a6", size = 14151586, upload-time = "2026-07-13T11:29:18.615Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c3/f8b2ffc60883084da91be51af58e88a7ffd4ff9795acb7d902ff88d31eb1/mypy-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7da939dd335cfd2ad788bdfd081c9f4e47634ab995e5a45eb15fd1e5bc052f8b", size = 15227411, upload-time = "2026-07-13T11:30:29.904Z" }, + { url = "https://files.pythonhosted.org/packages/83/2e/16b917fc7adcf03f1aadddfc93aab804ffb234b1ab09c0ffd6d92a5d34a2/mypy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7247eb2824f996722a949530183394921ca71deb9680052a338cf53cff7925c2", size = 15478790, upload-time = "2026-07-13T11:33:14.686Z" }, + { url = "https://files.pythonhosted.org/packages/c0/88/aaa65a93c73d0cdae7e42f8adb302bf6885bb281302084f99d0290a35347/mypy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:75b0984bb3cbd76bb5c9291a8671f7ae66ca3b51c7584c358fc2e923259f0757", size = 11234919, upload-time = "2026-07-13T11:33:39.28Z" }, + { url = "https://files.pythonhosted.org/packages/35/19/b40de63f1a80e63bc2d40f0679a6a8dbd34e95176c8122119bdf406aa552/mypy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:d78fcf900b59cb7e82cb7e3a235e31b462d9333d92285bd1e4952d355b8ffba1", size = 10201510, upload-time = "2026-07-13T11:31:52.619Z" }, + { url = "https://files.pythonhosted.org/packages/2c/fa/fdc54fe583ba3cafbcedfb70eeeaf03849f75b1827a07096c7bd996f582d/mypy-2.3.0-py3-none-any.whl", hash = "sha256:6b1cdb579446b60432432b2b2403a6201b4b475a004d7f488511c9ba177c9e88", size = 2753292, upload-time = "2026-07-13T11:33:18.48Z" }, ] [[package]] @@ -1956,11 +1963,11 @@ wheels = [ [[package]] name = "narwhals" -version = "2.22.1" +version = "2.24.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/62/3c/c4ef2164a71c1a63d7f1ae411c4082c5fa872405106db60a4b7114989ad7/narwhals-2.22.1.tar.gz", hash = "sha256:d62920805a0a43b7ff8b54b0c0d3142d796f8a9301836ada37e573d6a33cbcd9", size = 647493, upload-time = "2026-06-05T12:34:34.051Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/1d/58946e5aab18393e793bd4add6985b95d0e01c3a2d832f38f54468b10dcd/narwhals-2.24.0.tar.gz", hash = "sha256:b5c0f684ccd9d7475b564111e319a4964abcf2baf79d3cf6b1003d06ac9b828d", size = 661143, upload-time = "2026-07-13T10:49:19.086Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/ca/36339329c4604adbcc99c899b7eb1ce1a555c499b6a6860757dc9bfed36d/narwhals-2.22.1-py3-none-any.whl", hash = "sha256:60567d774edf77db53906f89d9fbd164e66e56d66d388e1e6990f17ac33cfb53", size = 454815, upload-time = "2026-06-05T12:34:32.289Z" }, + { url = "https://files.pythonhosted.org/packages/7e/85/a5bfaebfd305ac18b57b0854d74e37e586809061a91fda62f0bd50c8518e/narwhals-2.24.0-py3-none-any.whl", hash = "sha256:42fdedf44e5b2ca7505630d45b4ac3058f38d8485cba9fe1652ca23152df7489", size = 461030, upload-time = "2026-07-13T10:49:17.571Z" }, ] [[package]] @@ -1983,18 +1990,18 @@ wheels = [ [[package]] name = "numba" -version = "0.65.1" +version = "0.66.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "llvmlite" }, { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f6/c5/db2ac3685833d626c0dcae6bd2330cd68433e1fd248d15f70998160d3ad7/numba-0.65.1.tar.gz", hash = "sha256:19357146c32fe9ed25059ab915e8465fb13951cf6b0aace3826b76886373ab23", size = 2765600, upload-time = "2026-04-24T02:02:56.551Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/a0/570e3dc53e5602b49108f62a13e529f1eec8bfc7ef37d49c825924dcf546/numba-0.66.0.tar.gz", hash = "sha256:b900e63a0e26c05ea9a6d5a3a5a0a177cb64c5011887bf43edb8c3ed2c38d363", size = 2806181, upload-time = "2026-07-01T23:12:46.36Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/37/14a4579049c1eb673afd0de0cb4842982acd55b9ce2643e763db858bcea0/numba-0.65.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:1735c15c1134a5108b4d6a5c77fc0947924ea066a738dc09a52008c13df9cad3", size = 2681344, upload-time = "2026-04-24T02:02:33.65Z" }, - { url = "https://files.pythonhosted.org/packages/a0/22/b8d873f6466b20aa563fc9b33acd48dec89a07803ddaa2f1c8ca1cd33126/numba-0.65.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c09f49117ef255e1f1c6dad0c7a1ed39868243862a73be5706793241a3755f1b", size = 3810619, upload-time = "2026-04-24T02:02:36.041Z" }, - { url = "https://files.pythonhosted.org/packages/62/08/e16a8b5d9a018962ebb5c66be662317cde32b9f5dab08441f90bed5522fb/numba-0.65.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:594a8680b3fadac99e97e489b1fd89007177e5336713745c3b769528c635a464", size = 3509783, upload-time = "2026-04-24T02:02:38.245Z" }, - { url = "https://files.pythonhosted.org/packages/fd/a5/03c970d57f4c1741354837353ce39fb5206952ae1dba8922d29c86f64805/numba-0.65.1-cp313-cp313-win_amd64.whl", hash = "sha256:85be74c0d036842699a30058f82fb88fc5ffdc59f7615cab5792ea92914c9b62", size = 2750534, upload-time = "2026-04-24T02:02:39.903Z" }, + { url = "https://files.pythonhosted.org/packages/03/52/176c02d005c5c5143cde10a85bbcdcb6236d9e34c3aac089380e0506cd1d/numba-0.66.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:380b2556a2019ccd1e956ae77dd257eaa39403f7520768b626d44b755112785e", size = 2727084, upload-time = "2026-07-01T23:12:25.434Z" }, + { url = "https://files.pythonhosted.org/packages/44/b5/e930010965568fe7f2c6c962fd2849d458cb9f62c3ab7584af8a19a2b40a/numba-0.66.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:939316d5d8619751207b8972a67852b5a7646665298cb4de693cd6bf135152f4", size = 3873663, upload-time = "2026-07-01T23:12:27.308Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ec/5b51457cbe96e4831141d83e892e65191b23a1b78728456c62909d231ace/numba-0.66.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cdf506775d9f02eb92a87bf5c5b1e0d25506fd18cafd769f4ed914a8feac73e7", size = 3573529, upload-time = "2026-07-01T23:12:28.944Z" }, + { url = "https://files.pythonhosted.org/packages/83/7e/cea7710e96913d3c7f2999f16db1b28e6c5be5171cbf40f77f98333a7243/numba-0.66.0-cp313-cp313-win_amd64.whl", hash = "sha256:c5bfe5350284509ab0474390321454c3a8627a188af5b68c910e83df3e2db4a7", size = 2797247, upload-time = "2026-07-01T23:12:30.774Z" }, ] [[package]] @@ -2153,11 +2160,11 @@ wheels = [ [[package]] name = "nvidia-nvjitlink" -version = "13.0.88" +version = "13.3.33" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" }, - { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ee/580ca6f29dcab0221db8706badca1bbbb084f1975c4d4e83329c3a7e31f0/nvidia_nvjitlink-13.3.33-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:26a6de7fb4c8fdaa7703d3dad720d6d427ddfea5c48a528fd97c11733ad830e5", size = 40742423, upload-time = "2026-05-26T16:54:51.613Z" }, + { url = "https://files.pythonhosted.org/packages/69/30/45414e35ff2eee7db3da037e5707037ccf9d2b5218ffbdb055ea4d5aa98a/nvidia_nvjitlink-13.3.33-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ce48b37dfeb3cb1eae4cf85adacb47d7a6539ea2272870c9a3628ce275c2037e", size = 39168635, upload-time = "2026-05-26T16:54:13.906Z" }, ] [[package]] @@ -2180,7 +2187,7 @@ wheels = [ [[package]] name = "openai" -version = "2.44.0" +version = "2.45.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2192,9 +2199,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/f5/7c7cb955305cb41f7f3c5fd7e0e38bf6bbf2658468863d4b7b868a5cb8df/openai-2.44.0.tar.gz", hash = "sha256:68a5a5ffad82b8ff7d451c437529fb64f7c3b8123aaf0c021966a882d9e3947d", size = 988753, upload-time = "2026-06-24T20:56:02.293Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/60/d4219875289b11d2c2f7da93c36283da224a2e55865ed865ab64e0ce9217/openai-2.45.0.tar.gz", hash = "sha256:10d34ca9c5643bce775852fddbfc172505cb1d4de1ccd101696c3ecff358765d", size = 1109653, upload-time = "2026-07-09T18:02:44.091Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/f4/561ed79fd94876160018a5e75254cfcb9b0e62d4dded9dcb20072e86d623/openai-2.44.0-py3-none-any.whl", hash = "sha256:0a2a3ab2e29aeda368700f662ff9ba0f9df17ba4c54577a64e08b8115a3cc0ad", size = 1366216, upload-time = "2026-06-24T20:55:58.882Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b0/2291689e3ec4723fbf5bbf3b54afcd7b160f9ddc98ca7aedfd0132af5677/openai-2.45.0-py3-none-any.whl", hash = "sha256:5df105f5f8c9b711fcb9d06d2d3888cebc82506db216484c14a4e53cdf651777", size = 1629470, upload-time = "2026-07-09T18:02:42.21Z" }, ] [[package]] @@ -2366,47 +2373,31 @@ wheels = [ [[package]] name = "pgvector" -version = "0.4.2" +version = "0.5.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/25/6c/6d8b4b03b958c02fa8687ec6063c49d952a189f8c91ebbe51e877dfab8f7/pgvector-0.4.2.tar.gz", hash = "sha256:322cac0c1dc5d41c9ecf782bd9991b7966685dee3a00bc873631391ed949513a", size = 31354, upload-time = "2025-12-05T01:07:17.87Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/ec/6eb80aebc728200f95229219882994c1b0585b956ca47da5edb9d062627a/pgvector-0.5.0.tar.gz", hash = "sha256:07a9dcf735696879406983afc6eba9a787cef7c0cf6c367ca1a5779f036dee74", size = 35170, upload-time = "2026-07-06T18:27:27.767Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/26/6cee8a1ce8c43625ec561aff19df07f9776b7525d9002c86bceb3e0ac970/pgvector-0.4.2-py3-none-any.whl", hash = "sha256:549d45f7a18593783d5eec609ea1684a724ba8405c4cb182a0b2b08aeff04e08", size = 27441, upload-time = "2025-12-05T01:07:16.536Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e4/a5573f2c579ca9ad133293bfb624148ba0893674ca4a6eeec85ced9a6a09/pgvector-0.5.0-py3-none-any.whl", hash = "sha256:fedc9800894e6da2be51358d7b7c574bf34f247ca741a5a09513622135f5964f", size = 30958, upload-time = "2026-07-06T18:27:26.797Z" }, ] [[package]] name = "pillow" -version = "12.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, - { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, - { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, - { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, - { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, - { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, - { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, - { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, - { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, - { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, - { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, - { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, - { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, - { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, - { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, - { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, - { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, - { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, - { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, - { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, +version = "12.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/ac/31fb64e1e7efb5a4b50cd3d92049ba89ac6e4d8d3bb6a74e15048ca3353e/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89", size = 4161684, upload-time = "2026-07-01T11:54:25.934Z" }, + { url = "https://files.pythonhosted.org/packages/87/b4/9805e23d2b4d77842b468513841fda254ee42f0289d25088340e4ff46e2d/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace", size = 4255487, upload-time = "2026-07-01T11:54:27.935Z" }, + { url = "https://files.pythonhosted.org/packages/df/39/ecf519435a200c693fe053a6ee4d835b41cf963a4dfc2551c4e637cb2a71/pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec", size = 3696433, upload-time = "2026-07-01T11:54:29.813Z" }, + { url = "https://files.pythonhosted.org/packages/42/92/2fc3ffad878ae8dd5469ec1bc8eb83b71f48e13efdf68f02709003982a32/pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66", size = 5345889, upload-time = "2026-07-01T11:54:31.97Z" }, + { url = "https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35", size = 4780109, upload-time = "2026-07-01T11:54:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/e18aff37cb0b4aac47ac90f016d347a49aca667ef97f190b06ac2aabc928/pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65", size = 6263736, upload-time = "2026-07-01T11:54:36.131Z" }, + { url = "https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3", size = 6937129, upload-time = "2026-07-01T11:54:38.216Z" }, + { url = "https://files.pythonhosted.org/packages/70/4d/105627a13300c5e0df1d174230b32fd1273062c96f7745fd552b945d1e1d/pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a", size = 6339562, upload-time = "2026-07-01T11:54:40.354Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1d/f13de01a553988ab895ba1c722e06cf3144d4f57656fd5b81b6d881f1179/pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e", size = 7049439, upload-time = "2026-07-01T11:54:42.489Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f9/066794cca041b969964f779ee5fa66a9498bbf34248ac39c5d7954e4198f/pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f", size = 6473287, upload-time = "2026-07-01T11:54:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/7a58e61d62be561da3a356fe2384d4059a6345fc130e23ef1c36a5b81d24/pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8", size = 7239691, upload-time = "2026-07-01T11:54:47.141Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b0/c4ed4f0ef8f8fa5ee8351537db6650bb8189f7e118842978dd6589065692/pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b", size = 2568185, upload-time = "2026-07-01T11:54:49.137Z" }, ] [[package]] @@ -2618,11 +2609,11 @@ memory = [ [[package]] name = "pyasn1" -version = "0.6.3" +version = "0.6.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a4/9a/23310166d960def5897e91fe20e5b724601b02a22e84ba1f94232c0b7f67/pyasn1-0.6.4.tar.gz", hash = "sha256:9c447d8431c947fe4c8febc4ed9e760bc29011a5b01e5c74b67025bd9fb8ce81", size = 151262, upload-time = "2026-07-09T01:12:33.988Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3b/6163796d69c3977d1e4287bea4a6979161cbbdd170ebb430511e8e1999ce/pyasn1-0.6.4-py3-none-any.whl", hash = "sha256:deda9277cfd454080ec40b207fb6df82206a3a2688735233cdcd8d3d565f088b", size = 84410, upload-time = "2026-07-09T01:12:32.92Z" }, ] [[package]] @@ -2668,19 +2659,19 @@ email = [ [[package]] name = "pydantic-ai" -version = "2.0.0" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic-ai-slim", extra = ["anthropic", "cli", "evals", "google", "logfire", "mcp", "openai", "retries", "web"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a7/a2/e8a7aa6c58726262d355dfb0f89aedfa332ad193461ebbb0d4daad1f539f/pydantic_ai-2.0.0.tar.gz", hash = "sha256:c1a6420eab2ac7031ffac1e3c2aab70aac2850a44826a1a466e82c33a2d9df41", size = 18248, upload-time = "2026-06-23T15:48:56.503Z" } +sdist = { url = "https://files.pythonhosted.org/packages/70/cb/a71c04cbf94c4adb17c5d512f0d7afd87971594b1fa2b2e19d02b855d71c/pydantic_ai-2.11.0.tar.gz", hash = "sha256:202bcd30ab3b82dd370674519067ee4c88522e954a9e278ed46c4fe52133b598", size = 18552, upload-time = "2026-07-16T02:59:30.972Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/4c/275e567eb516c4efe281807db1c6a6e7b0ffe24232c2d20f8f0252197215/pydantic_ai-2.0.0-py3-none-any.whl", hash = "sha256:587d3074850514b45890f751c26a9ffdbe8e84e85ffca6ccf89a713ca06ddf9a", size = 7701, upload-time = "2026-06-23T15:48:48.695Z" }, + { url = "https://files.pythonhosted.org/packages/b0/00/0525eabd801130f66415123f430df6533ad0ca43330ea491ec70119d0e6a/pydantic_ai-2.11.0-py3-none-any.whl", hash = "sha256:b84bb291986f102389a01eb5e02761ad80731ae5d0b8dc2bacd5ccf34e8a9838", size = 7730, upload-time = "2026-07-16T02:59:22.35Z" }, ] [[package]] name = "pydantic-ai-slim" -version = "2.0.0" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "genai-prices" }, @@ -2691,9 +2682,9 @@ dependencies = [ { name = "pydantic-graph" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/81/5b/c930c2c645677d1069172a71be9a22ea37b2aa170dcf3c6051aa1c105e72/pydantic_ai_slim-2.0.0.tar.gz", hash = "sha256:056ea466d67b47a832736ac0f33172264b09da2110dbcce09d26b82772173218", size = 734369, upload-time = "2026-06-23T15:48:58.379Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/c2/c399c833d88c48a3f5c3dc8a2d9f92644d22efc33bd8345605ae87fa373b/pydantic_ai_slim-2.11.0.tar.gz", hash = "sha256:d174372ee7e70e763b92aaac841d0f1728aa5e80c6373dd3704eee1c799a1194", size = 824048, upload-time = "2026-07-16T02:59:33.085Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/e6/814efd5693a97589bfd15c51c9324f77758b82a04f172f3fec9723c7777b/pydantic_ai_slim-2.0.0-py3-none-any.whl", hash = "sha256:39979b459a7bc73ae5294c071a7e474f123858a9f7922e9cbb662018d6431198", size = 905291, upload-time = "2026-06-23T15:48:51.628Z" }, + { url = "https://files.pythonhosted.org/packages/16/d9/edcc186a9f10bd0f73e2ee4dec8130aa0f9b5428f9e7bd2bc2b27a7b96cd/pydantic_ai_slim-2.11.0-py3-none-any.whl", hash = "sha256:1a797a9c1c6d192f3b2e19f7d833aa9ab92ec1de000693a139a2320a6863581b", size = 1001962, upload-time = "2026-07-16T02:59:25.256Z" }, ] [package.optional-dependencies] @@ -2760,7 +2751,7 @@ wheels = [ [[package]] name = "pydantic-evals" -version = "2.0.0" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2770,9 +2761,9 @@ dependencies = [ { name = "pyyaml" }, { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/79/f6/0e376b882163bfb3d65fffc29af85f2b8819c97d4afde6aea2ea27e524ca/pydantic_evals-2.0.0.tar.gz", hash = "sha256:eeb5b260d63d276e64eeaf7b2542134b55af82d5b968fb8cd0eb64fde23eee94", size = 75553, upload-time = "2026-06-23T15:48:59.571Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/d5/16ff85ceed2481cc6492358f21774c08ff625e37e6558f55a237fc66abc1/pydantic_evals-2.11.0.tar.gz", hash = "sha256:6036a946468a7724897fe93bf006f014c1645f2473600f95efdcad94bde77472", size = 85043, upload-time = "2026-07-16T02:59:34.213Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/20/bc3f7ed680830b0736adc2a7634977386ee1da618f350b69128af02ba65d/pydantic_evals-2.0.0-py3-none-any.whl", hash = "sha256:0ae78e988ae4be4354212c8d3ab7ee0deb8daab93eef99690ec281290fae4f46", size = 90109, upload-time = "2026-06-23T15:48:53.334Z" }, + { url = "https://files.pythonhosted.org/packages/ca/7c/c90fcb51729bdaa1d91e4c7cbf7c00f9689d7b22ca09c2dd0e0df43d5d84/pydantic_evals-2.11.0-py3-none-any.whl", hash = "sha256:c56ec8dad9fa2b9d91932936167d8159670baed7025e686421f2b1e6694711a8", size = 100360, upload-time = "2026-07-16T02:59:27.179Z" }, ] [[package]] @@ -2790,7 +2781,7 @@ wheels = [ [[package]] name = "pydantic-graph" -version = "2.0.0" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -2798,9 +2789,9 @@ dependencies = [ { name = "pydantic" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/9d/81b09921d360614f65bb2d4f936d5cae856b4ebe827fcf43ae7abb81383a/pydantic_graph-2.0.0.tar.gz", hash = "sha256:f0bffe84a46a5118bce0824de63d08f3f32ba4dfc1064674f449b07e15128287", size = 43054, upload-time = "2026-06-23T15:49:00.488Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/4f/1b10ff0e166a346a3967fca1f7c5cd7d7d9f8209848c9bd5d89ed9b85eef/pydantic_graph-2.11.0.tar.gz", hash = "sha256:25a40a815ef1dcf6e08bad4264c4c09d6426b34126570e72a356d50e358520fd", size = 43937, upload-time = "2026-07-16T02:59:35.355Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/2c/ff64a971e26bb2d0d67a70ef436f2cfc778341a4cf89d5aca35cbc1f071c/pydantic_graph-2.0.0-py3-none-any.whl", hash = "sha256:36d69fa01cd316be8584b90eef58bd21675c11c0a081b500a5c4ebe9b68310a5", size = 50773, upload-time = "2026-06-23T15:48:54.562Z" }, + { url = "https://files.pythonhosted.org/packages/2a/5e/7e51421f9a70e24907768556e21aa0272cae429bf78d79b5eae8175bd151/pydantic_graph-2.11.0-py3-none-any.whl", hash = "sha256:e40dbf2dd51a0c7a7fb306b41c0af7f3acdc892cb5a60324733ad8eb51f441f3", size = 51655, upload-time = "2026-07-16T02:59:28.721Z" }, ] [[package]] @@ -2888,15 +2879,15 @@ wheels = [ [[package]] name = "python-discovery" -version = "1.4.2" +version = "1.4.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0b/1a/cbbaf13b730abb0a16b964d984e19f2fe520c21a4dc664051359a3f5a9e7/python_discovery-1.4.2.tar.gz", hash = "sha256:8f3746c4b4968d22afbb97d36e1a0e5b66e6c0f297290f2e95f05b9b8bf18690", size = 70277, upload-time = "2026-06-11T16:10:42.383Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/81/58c70036dffeccb7fe7d79d6260c69f7a28272bbd3909c29a01ea9422744/python_discovery-1.4.4.tar.gz", hash = "sha256:5cad33982d412c1f3ffb8f9ca4ea292c9680bca3942451d30b69c37fce53a4a3", size = 72212, upload-time = "2026-07-08T23:06:50.691Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/82/a70006589557f267f15bd384c0642ad49f0d97b690c3a05b166b9dcbad3b/python_discovery-1.4.2-py3-none-any.whl", hash = "sha256:475803f53b7b2ed6e490e27373f9d8340f7d2eebf9acdaf645d7d714c97bb500", size = 33886, upload-time = "2026-06-11T16:10:41.192Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ae/84bc0d2440c95772272bb6f4b3d09ccf08b2898fce89b3d4f969a9fc74e9/python_discovery-1.4.4-py3-none-any.whl", hash = "sha256:abebe9120b43453b68c908acfb1e72a19d1a959ed2cb620ad38fc57d08056dbe", size = 34181, upload-time = "2026-07-08T23:06:49.402Z" }, ] [[package]] @@ -2978,42 +2969,42 @@ wheels = [ [[package]] name = "regex" -version = "2026.5.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/0e/49aee608ad09480e7fd276898c99ec6192985fa331abe4eb3a986094490b/regex-2026.5.9.tar.gz", hash = "sha256:a8234aa23ec39894bfe4a3f1b85616a7032481964a13ac6fc9f10de4f6fca270", size = 416074, upload-time = "2026-05-09T23:15:19.37Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/da/797e91ecec6f84135da778ddce78c20e0af5d2a15c26f87a81bc3eadb6db/regex-2026.5.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d626b84406444b165fc0ba981604edea39f0588ff1f92baa23fe50799ea9afdb", size = 490303, upload-time = "2026-05-09T23:13:04.382Z" }, - { url = "https://files.pythonhosted.org/packages/44/da/bf30abaaa737b58f4a4b8c4a03659e02fd92092c822e0197ed9e0daab917/regex-2026.5.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d7bdc0ab8f3dd7e1b4f9ab88634e13374669db86bb3c72e8292f07ae313f539f", size = 292019, upload-time = "2026-05-09T23:13:06.022Z" }, - { url = "https://files.pythonhosted.org/packages/2d/e7/d0eaf5713828417b9e5648cf81fa9bacd4961f6ab98c380c2034f8716e35/regex-2026.5.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a8820737949116ffff55fe18f9fc644530063ba6ebfcb8314239416e78f1347c", size = 289468, upload-time = "2026-05-09T23:13:08.214Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9b/b3fdd62b003baa1a9b593cd8c8699c9651c2e80cc21a5c715707983c42d7/regex-2026.5.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0fbdbac82cb3e4450d0ccde7d7a35607f4cb2dd9fba4b8b69bfaf8c9fa6aed", size = 796749, upload-time = "2026-05-09T23:13:10.573Z" }, - { url = "https://files.pythonhosted.org/packages/d4/30/66ab84588765f5b4b271a9ca09ef7ce2b87caa95176ec3d2ad65d7bc4902/regex-2026.5.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57e8915c7986aa33d25e4d3629cef711cd2863f2961b10409f0c04cb8b7d9020", size = 865445, upload-time = "2026-05-09T23:13:12.523Z" }, - { url = "https://files.pythonhosted.org/packages/1a/89/f05169e8588aac365f35ffc7f3bc3184f095ef4cfded7cfaa3c7fd5dbd89/regex-2026.5.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508f56a89ba9cb26e4168cbc37dbd60a28d82430a9e18ad1d25fe0883c314ca2", size = 912322, upload-time = "2026-05-09T23:13:14.281Z" }, - { url = "https://files.pythonhosted.org/packages/30/e1/c93444052cf41581f3c884ab3fb5823daf0992f11cd4388d4275ca610558/regex-2026.5.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d189041f15691cfa2b6c4290448ec221244d225b3f5fe9e7771b34ffcdf6e2", size = 801269, upload-time = "2026-05-09T23:13:16.569Z" }, - { url = "https://files.pythonhosted.org/packages/50/fe/0cf96b882f540e62e8b9956599798203d599c44cf4c77917ca27400ff69b/regex-2026.5.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e82db382b44d0111b22601c509c89f64434816c9e0eef9d1989cda8cc6ff1c04", size = 777085, upload-time = "2026-05-09T23:13:18.675Z" }, - { url = "https://files.pythonhosted.org/packages/23/5c/d78d4924e7fc875557b9e9b768423925fdfaac5549d06da7810019a9bd26/regex-2026.5.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2acfb48634f64996b57f90f39afa692ff362162722581921fe92239a59960f3c", size = 785153, upload-time = "2026-05-09T23:13:20.525Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e0/5214774090e7b4524dcea3e3c4aa74141d43043f8beb49c1599db1c8b53a/regex-2026.5.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d29eebfc9525db68cad3c97eedd7f754fa265aa5cd0cf4f863b2421e1b48fc9f", size = 860164, upload-time = "2026-05-09T23:13:22.263Z" }, - { url = "https://files.pythonhosted.org/packages/6e/e1/4a57a83350319b1271f0d7a249b8672513ed928b237a741631270de6caea/regex-2026.5.9-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:debb893095e944091c16e641a6e33c1b0f4cb61ab945ec5afbf53ce7068834d8", size = 765731, upload-time = "2026-05-09T23:13:24.277Z" }, - { url = "https://files.pythonhosted.org/packages/12/f4/499e74a20c156fc75836ee04a72a38d1a063978f600937f9760467beb1b0/regex-2026.5.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d659eee77986549c9ea45b861c7567e44d6287c3dc9a4565478853f7b9fe2ff6", size = 852062, upload-time = "2026-05-09T23:13:26.125Z" }, - { url = "https://files.pythonhosted.org/packages/5b/92/7eebc0d0a01e78629695f342ba17e0deaff8fb45e79cc0d7b98287da6e3e/regex-2026.5.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2efa205e6d98b24d1f3ab395c11aa15cdf10935bca283d0285e0499c284fba21", size = 789577, upload-time = "2026-05-09T23:13:27.814Z" }, - { url = "https://files.pythonhosted.org/packages/05/a4/018e71f7d2ad48c1ebe6d3ae0026f9b7cb4802fd15c7cc02fdf724355102/regex-2026.5.9-cp313-cp313-win32.whl", hash = "sha256:f3844f134e834076677dd369976e9f5068679fcb8e50102fdf6b7ac96a3ec127", size = 266691, upload-time = "2026-05-09T23:13:29.549Z" }, - { url = "https://files.pythonhosted.org/packages/e6/1d/861a93719fb9ee7dbfc3761b3797b7a3e112a5d42c6129459d2d741be9b5/regex-2026.5.9-cp313-cp313-win_amd64.whl", hash = "sha256:3527bb4942d2c14552155406cdedd906567456821848aed1cb4933a391bf5eca", size = 277747, upload-time = "2026-05-09T23:13:31.859Z" }, - { url = "https://files.pythonhosted.org/packages/d9/c6/0a2436ae4da1ba76e51cb98943c6838a9a721faa40ebe2dce07694ae34e3/regex-2026.5.9-cp313-cp313-win_arm64.whl", hash = "sha256:56a33f191f17d8c417f99945ebdc1e691d3af9605d86ec68c7e54a57e3e17af6", size = 270500, upload-time = "2026-05-09T23:13:33.525Z" }, - { url = "https://files.pythonhosted.org/packages/e8/e9/d21346f7b60ed58789371358ed66b09d00f832e1bd7c06e55d9da5679882/regex-2026.5.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:01f28d868834624c934b8d2e0aa1c8341337e37831f4a012f18a5afcba4cbaf3", size = 494172, upload-time = "2026-05-09T23:13:35.935Z" }, - { url = "https://files.pythonhosted.org/packages/c4/43/fd1177a2032037c681baecdb3422ee4e1424aec4e4f470ef47793d325274/regex-2026.5.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:48036f6374aaa79eb3b754ec29c61d1c6b1606749d705a13f8854fa2539671f6", size = 293952, upload-time = "2026-05-09T23:13:38.307Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7d/9fbf919768368d3f8a4f6c692cf2aa61e482b2b81ec6a298ace4cbf02480/regex-2026.5.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b96350aa424e79d4fd6b567b344dcbe2b2d6bfc48dfe7717587e1fa6d43da6ff", size = 292314, upload-time = "2026-05-09T23:13:40.353Z" }, - { url = "https://files.pythonhosted.org/packages/e2/6c/e41bfeecb589716843e7c4df09ba46ff2a42961457afece19059d85caeef/regex-2026.5.9-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f3af7a4903c5c04a11a196a5aa75cdd7dd3f8508132f9fb3259d9f5908e3b88", size = 811681, upload-time = "2026-05-09T23:13:42.543Z" }, - { url = "https://files.pythonhosted.org/packages/87/83/a5c1c525fba0aa656e88ad0face0b1829788ef4c2fb6b26df58aa1151b84/regex-2026.5.9-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e87577720152d2caae19fe2baaf1f8d5ca12091e9e229f03915c37d1e4b9178", size = 871135, upload-time = "2026-05-09T23:13:44.326Z" }, - { url = "https://files.pythonhosted.org/packages/18/d4/80882e799e440dd878b0979cbebf8fa4d54624a332c83037c7a701649e3f/regex-2026.5.9-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c8b9b9d294cfea3cd19c718ade7cc93492b2c4991abd9a68d0b3477ae6d8e100", size = 917265, upload-time = "2026-05-09T23:13:47.295Z" }, - { url = "https://files.pythonhosted.org/packages/ae/ff/8db60211e2286e396aad7dc7725356c502bff0901ea05bd6cdc2e1a042b9/regex-2026.5.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:728d8bfd28a8845c8b6bc5dc7ce010453d206396786c0765c2740cb65f37791e", size = 816311, upload-time = "2026-05-09T23:13:49.885Z" }, - { url = "https://files.pythonhosted.org/packages/4c/47/742ef579c61730f8d268e5cf1f9ce0e37e2ea041ad0f5644724f2378e463/regex-2026.5.9-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7e30b874d341fac767d7df5a0870540541c2c054b80cfaac116e8d367a8a7ff2", size = 785498, upload-time = "2026-05-09T23:13:52.25Z" }, - { url = "https://files.pythonhosted.org/packages/7f/ab/cb0999802dcb0fb95b1ab005e8d4163d8afdd67efc2cb6b6630ac13f8cb1/regex-2026.5.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fd190e88a895a8901325fad284a3f74ea52b1da8525b76cc811fa9b1edf0ce2b", size = 801348, upload-time = "2026-05-09T23:13:54.127Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/8ca59a24c55bc34d166eefaf3717bd77772f329fdbf984d86581e0a3571c/regex-2026.5.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:8e76e8161ad00694cfce6767d5dea860c6391ac5b83e5c3a39661e696f11fc7e", size = 866493, upload-time = "2026-05-09T23:13:56.067Z" }, - { url = "https://files.pythonhosted.org/packages/8d/3d/30f2ae62cef3278bb5bb821f467277a55fb73f01032cf85997e15e8289a8/regex-2026.5.9-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ddda5340e6c01a293027dd46232fa79eaff1b48058ce7a98f572b6445b088041", size = 772811, upload-time = "2026-05-09T23:13:57.867Z" }, - { url = "https://files.pythonhosted.org/packages/d8/ae/7d2089bcd78ad0c0161bc684339df50032acb438a7bd3305e7ddb1193cec/regex-2026.5.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:205109e96b3cf5adf8f4cd62bedde9487feb282b9497a3535451e5a24cd706a0", size = 856584, upload-time = "2026-05-09T23:13:59.679Z" }, - { url = "https://files.pythonhosted.org/packages/a9/29/92ff47f75990131ea4f24ba17819e5a9d141e10819807e09addd73409af6/regex-2026.5.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbe4579b9f08036aa7d101d1835437a20783574ac66327e6b29b4018a138081", size = 803453, upload-time = "2026-05-09T23:14:01.978Z" }, - { url = "https://files.pythonhosted.org/packages/04/99/eff29f1037dcab36702c9ee5d6858cf1ce2336ea8ea2987f64245b99ea5e/regex-2026.5.9-cp313-cp313t-win32.whl", hash = "sha256:ed2c9e8068b614c574d8d30e543d617cf5379b0535d46f97ef00e904745a08b5", size = 269951, upload-time = "2026-05-09T23:14:03.661Z" }, - { url = "https://files.pythonhosted.org/packages/0e/9d/8870b8981d27b22cda77bb26a5ac7ebfa9c7d9e0dea195a834a82380e748/regex-2026.5.9-cp313-cp313t-win_amd64.whl", hash = "sha256:b46b0f094dc1d3b90356c85a0bd2c9bafc4a6a190b9d6f8ddd5a033b6e088ed4", size = 281240, upload-time = "2026-05-09T23:14:05.56Z" }, - { url = "https://files.pythonhosted.org/packages/72/b1/3379415e8f135c13ac551353397cc4fe97b4978f3cac73c5fcbcded548b8/regex-2026.5.9-cp313-cp313t-win_arm64.whl", hash = "sha256:872acc074bd29ffc9913ecdfedf6ea77502312ca44a4aa0d3779089c6069d8de", size = 272383, upload-time = "2026-05-09T23:14:07.843Z" }, +version = "2026.7.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/37/451aaddbf50922f34d744ad5ca919ae1fcfac112123885d9728f52a484b3/regex-2026.7.10.tar.gz", hash = "sha256:1050fedf0a8a92e843971120c2f57c3a99bea86c0dfa1d63a9fac053fe54b135", size = 416282, upload-time = "2026-07-10T19:49:46.267Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/88/0c977b9f3ba9b08645516eca236388c340f56f7a87054d41a187a04e134c/regex-2026.7.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:4db009b4fc533d79af3e841d6c8538730423f82ea8508e353a3713725de7901c", size = 496868, upload-time = "2026-07-10T19:47:41.675Z" }, + { url = "https://files.pythonhosted.org/packages/f6/51/600882cd5d9a3cf083fd66a4064f5b7f243ba2a7de2437d42823e286edaf/regex-2026.7.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b96341cb29a3faa5db05aff29c77d141d827414f145330e5d8846892119351c1", size = 297306, upload-time = "2026-07-10T19:47:43.521Z" }, + { url = "https://files.pythonhosted.org/packages/52/6f/48a912054ffcb756e374207bb8f4430c5c3e0ffa9627b3c7b6661844b30a/regex-2026.7.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:14d27f6bd04beb01f6a25a1153d73e58c290fd45d92ba56af1bb44199fd1010d", size = 291950, upload-time = "2026-07-10T19:47:45.267Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c8/8e1c3c86ebcee7effccbd1f7fc54fe3af22aa0e9204503e2baea4a6ff001/regex-2026.7.10-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6b6a11bf898cca3ce7bfaa17b646901107f3975677fbd5097f36e5eb5641983", size = 796817, upload-time = "2026-07-10T19:47:48.054Z" }, + { url = "https://files.pythonhosted.org/packages/65/39/3e49d9ff0e0737eb8180a00569b47aabb59b84611f48392eba4d998d91a0/regex-2026.7.10-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:234f8e0d65cf1df9becadae98648f74030ee85a8f12edcb5eb0f60a22a602197", size = 865513, upload-time = "2026-07-10T19:47:49.855Z" }, + { url = "https://files.pythonhosted.org/packages/70/57/6511ad809bb3122c65bbeeffa5b750652bb03d273d29f3acb0754109b183/regex-2026.7.10-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:91b916d495db3e1b473c7c8e68733beec4dce8e487442db61764fff94f59740e", size = 912391, upload-time = "2026-07-10T19:47:51.776Z" }, + { url = "https://files.pythonhosted.org/packages/cc/29/a1b0c109c9e878cb04b931bfe4c54332d692b93c322e127b5ae9f25b0d9e/regex-2026.7.10-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f0d4ccf70b1d13711242de0ba78967db5c35d12ac408378c70e06295c3f6644", size = 801338, upload-time = "2026-07-10T19:47:53.38Z" }, + { url = "https://files.pythonhosted.org/packages/33/be/171c3dad4d77000e1befeff2883ca88734696dfd97b2951e5e074f32e4dd/regex-2026.7.10-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c622f4c638a725c39abcb2e680b1bd592663c83b672a4ed350a17f806d75618e", size = 777149, upload-time = "2026-07-10T19:47:54.944Z" }, + { url = "https://files.pythonhosted.org/packages/33/61/41ab0de0e4574da1071c151f67d1eb9db3d92c43e31d64d2e6863c3d89bf/regex-2026.7.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:41a47c2b28d9421e2509a4583a22510dc31d83212fcf38e1508a7013140f71a8", size = 785216, upload-time = "2026-07-10T19:47:56.56Z" }, + { url = "https://files.pythonhosted.org/packages/66/28/372859ea693736f07cf7023247c7eca8f221d9c6df8697ff9f93371cca08/regex-2026.7.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:13fba679fe035037e9d5286620f88bbfd105df4d5fcd975942edd282ab986775", size = 860229, upload-time = "2026-07-10T19:47:58.278Z" }, + { url = "https://files.pythonhosted.org/packages/50/b1/e1d32cd944b599534ae655d35e8640d0ec790c0fa12e1fb29bf434d50f55/regex-2026.7.10-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8e26a075fa9945b9e44a3d02cc83d776c3b76bb1ff4b133bbfa620d5650131da", size = 765797, upload-time = "2026-07-10T19:48:00.291Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/79a2cd9556a3329351e370929743ef4f0ccc0aaff6b3dc414ae5fa4a1302/regex-2026.7.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d0834c84ae8750ae1c4cede59b0afd4d2f775be958e11b18a3eea24ed9d0d9f1", size = 852130, upload-time = "2026-07-10T19:48:01.972Z" }, + { url = "https://files.pythonhosted.org/packages/66/58/76fec29898cf5d359ab63face50f9d4f7135cc2eca3477139227b1d09952/regex-2026.7.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64722a5031aeace7f6c8d5ea9a9b22d9368af0d6e8fa532585da8158549ea963", size = 789644, upload-time = "2026-07-10T19:48:03.748Z" }, + { url = "https://files.pythonhosted.org/packages/f6/06/3c7cec7817bda293e13c8f88aed227bbcf8b37e5990936ff6442a8fdf11a/regex-2026.7.10-cp313-cp313-win32.whl", hash = "sha256:74ae61d8573ecd51b5eeee7be2218e4c56e99c14fa8fcf97cf7519611d4be92e", size = 267130, upload-time = "2026-07-10T19:48:05.677Z" }, + { url = "https://files.pythonhosted.org/packages/88/6c/e2a6f9a6a905f923cfc912298a5949737e9504b1ca24f29eda8d04d05ece/regex-2026.7.10-cp313-cp313-win_amd64.whl", hash = "sha256:5e792367e5f9b4ffb8cad93f1beaa91837056b94da98aa5c65a0db0c1b474927", size = 277722, upload-time = "2026-07-10T19:48:07.318Z" }, + { url = "https://files.pythonhosted.org/packages/00/a6/9d8935aaa940c388496aa1a0c82669cc4b5d06291c2712d595e3f0cf16d3/regex-2026.7.10-cp313-cp313-win_arm64.whl", hash = "sha256:82ab8330e7e2e416c2d42fcec67f02c242393b8681014750d4b70b3f158e1f08", size = 277059, upload-time = "2026-07-10T19:48:08.977Z" }, + { url = "https://files.pythonhosted.org/packages/7d/e9/26decfd3e85c09e42ff7b0d23a6f51085ca4c268db15f084928ca33459c6/regex-2026.7.10-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:2b93eafd92c4128bab2f93500e8912cc9ecb3d3765f6685b902c6820d0909b6b", size = 501508, upload-time = "2026-07-10T19:48:10.668Z" }, + { url = "https://files.pythonhosted.org/packages/38/a5/5b167cebde101945690219bf34361481c9f07e858a4f46d9996b80ec1490/regex-2026.7.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3f03b92fb6ec739df042e45b06423fc717ecf0063e07ffe2897f7b2d5735e1e8", size = 299705, upload-time = "2026-07-10T19:48:12.544Z" }, + { url = "https://files.pythonhosted.org/packages/f6/20/7909be4b9f449f8c282c14b6762d59aa722aeaeebe7ee4f9bb623eeaa5e0/regex-2026.7.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bb5aab464a0c5e03a97abad5bdf54517061ebbf72340d576e99ff661a42575cc", size = 294605, upload-time = "2026-07-10T19:48:14.495Z" }, + { url = "https://files.pythonhosted.org/packages/82/88/e52550185d6fda68f549b01239698697de47320fd599f5e880b1986b7673/regex-2026.7.10-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fadb07dbe36a541283ff454b1a268afd54b077d917043f2e1e5615372cb5f200", size = 811747, upload-time = "2026-07-10T19:48:16.197Z" }, + { url = "https://files.pythonhosted.org/packages/06/98/16c255c909714de1ee04da6ae30f3ee04170f300cdc0dcf57a314ee4816a/regex-2026.7.10-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:21150500b970b12202879dfd82e7fd809d8e853140fff84d08e57a90cf1e154e", size = 871203, upload-time = "2026-07-10T19:48:18.12Z" }, + { url = "https://files.pythonhosted.org/packages/3b/32/423ed27c9bae2092a453e853da2b6628a658d08bb5a6117db8d591183d85/regex-2026.7.10-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a68b637451d64ba30ed8ae125c973fa834cc2d37dfa7f154c2b479015d477ba8", size = 917334, upload-time = "2026-07-10T19:48:19.952Z" }, + { url = "https://files.pythonhosted.org/packages/73/87/74dac8efb500db31cb000fda6bae2be45fc2fbf1fa9412f445fbb8acbe37/regex-2026.7.10-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e23458d8903e33e7d27196d7a311523dc4e2f4137a5f34e4dbd30c8d37ff33e", size = 816379, upload-time = "2026-07-10T19:48:21.616Z" }, + { url = "https://files.pythonhosted.org/packages/a8/9f/1859403654e3e030b288f06d49233c6a4f889d62b84c4ef3f3a28653173d/regex-2026.7.10-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae27622c094558e519abf3242cf4272db961d12c5c9a9ffb7a1b44b2627d5c6", size = 785563, upload-time = "2026-07-10T19:48:23.643Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d8/35d30d6bdf1ef6a5430e8982607b3a6db4df1ddedbe001e43435585d88ba/regex-2026.7.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ee877b6d78f9dff1da94fef51ae8cf9cce0967e043fdcc864c40b85cf293c192", size = 801415, upload-time = "2026-07-10T19:48:25.499Z" }, + { url = "https://files.pythonhosted.org/packages/f7/22/630f31f5ea4826167b2b064d9cac2093a5b3222af380aa432cfe1a5dabcd/regex-2026.7.10-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:2c66a8a1969cfd506d1e203c0005fd0fc3fe6efc83c945606566b6f9611d4851", size = 866560, upload-time = "2026-07-10T19:48:27.789Z" }, + { url = "https://files.pythonhosted.org/packages/8d/14/f5914a6d9c5bc63b9bed8c9a1169fb0be35dbe05cdc460e17d953031a366/regex-2026.7.10-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:2bc350e1c5fa250f30ab0c3e38e5cfdffcd82cb8af224df69955cab4e3003812", size = 772877, upload-time = "2026-07-10T19:48:29.563Z" }, + { url = "https://files.pythonhosted.org/packages/c1/0f/7c13999eef3e4186f7c79d4950fa56f041bf4de107682fb82c80db605ff9/regex-2026.7.10-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:53f54993b462f3f91fea0f2076b46deb6619a5f45d70dbd1f543f789d8b900ef", size = 856648, upload-time = "2026-07-10T19:48:31.282Z" }, + { url = "https://files.pythonhosted.org/packages/a4/71/a48e43909b6450fb48fa94e783bef2d9a37179258bc32ef2283955df7be7/regex-2026.7.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cfcec18f7da682c4e2d82112829ce906569cb8d69fa6c26f3a50dfbed5ceb682", size = 803520, upload-time = "2026-07-10T19:48:33.275Z" }, + { url = "https://files.pythonhosted.org/packages/e0/b8/f037d1bf2c133cb24ceb6e7d81d08417080390eddab6ddfd701aa7091874/regex-2026.7.10-cp313-cp313t-win32.whl", hash = "sha256:a2d6d30be35ddd70ce0f8ee259a4c25f24d6d689a45a5ac440f03e6bcc5a21d1", size = 269168, upload-time = "2026-07-10T19:48:35.353Z" }, + { url = "https://files.pythonhosted.org/packages/b6/9c/eaac34f8452a838956e7e89852ad049678cdc1af5d14f72d3b3b658b1ea5/regex-2026.7.10-cp313-cp313t-win_amd64.whl", hash = "sha256:c57b6ad3f7a1bdd101b2966f29dc161adf49727b1e8d3e1e89db2eda8a75c344", size = 280004, upload-time = "2026-07-10T19:48:37.106Z" }, + { url = "https://files.pythonhosted.org/packages/cd/a9/e22e997587bc1d588b0b2cd0572027d39dd3a006216e40bbf0361688c51c/regex-2026.7.10-cp313-cp313t-win_arm64.whl", hash = "sha256:3d8ef9df02c8083c7b4b855e3cb87c8e0ebbcfea088d98c7a886aaefdf88d837", size = 279308, upload-time = "2026-07-10T19:48:38.907Z" }, ] [[package]] @@ -3046,16 +3037,16 @@ wheels = [ [[package]] name = "rich-toolkit" -version = "0.20.1" +version = "0.20.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/29/63/3e427c62f1992945c997d4ec31e2fcb37d26aadbe5aa44ae5b29f7f64d26/rich_toolkit-0.20.1.tar.gz", hash = "sha256:c7336ae281f435c785acecaedc4b71d4b663dc73d9c8079fea96372527e822a4", size = 203473, upload-time = "2026-06-05T08:56:57.679Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/3a/a258c2fbc6c6bdf428611388f5698ba5d57ffdf0755e1cab474d9cc47813/rich_toolkit-0.20.3.tar.gz", hash = "sha256:223dd2cfba325ed55e94933b9e53f3aca13e9fdf76622bd564c18109a2273c1b", size = 205355, upload-time = "2026-07-13T14:38:06.837Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/88/309f07d08155da2ba1d5ceb42d270fb42fbe34a807684543e3ffc10fe713/rich_toolkit-0.20.1-py3-none-any.whl", hash = "sha256:2a6d5f8e15759b9eba5a9ee63da10b275359ead20e5a0fc92bd5b4dbae8ce4bf", size = 35525, upload-time = "2026-06-05T08:56:58.586Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ce/639d0d0ce3d25c5edbd1afecd308bb35dc04883a45ac9f0855c8aee4e919/rich_toolkit-0.20.3-py3-none-any.whl", hash = "sha256:419aa87516d5f3849cca553c6dcf707c02a36d508fcf996946606725d34a3002", size = 36195, upload-time = "2026-07-13T14:38:05.687Z" }, ] [[package]] @@ -3096,64 +3087,50 @@ wheels = [ [[package]] name = "rpds-py" -version = "2026.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/43/25a8dcd3feedd735039a8f0b5b7e3b118232b5eae288c4fd9ab200d41094/rpds_py-2026.5.1.tar.gz", hash = "sha256:07b24fea40541e28570e5b795a4a38fbdcd12550c06bd0748005ecc8116ca256", size = 64459, upload-time = "2026-05-28T12:02:13.232Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/32/14c961ad295f490eb0849ada8b79683e93a59b9de3afdd983eaf55fa6867/rpds_py-2026.5.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:efef4ac29c6ff495531eb17ee705b62841ecaa291b7c7077e848ea03e237164d", size = 352787, upload-time = "2026-05-28T11:59:33.655Z" }, - { url = "https://files.pythonhosted.org/packages/ca/bb/d1b85117967c11191441a7274ae616c65d93901d082c588f89a50a8da5ae/rpds_py-2026.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c39f5b67a8a2e67179ada2a954227d670fe65fa9098457f698f56ddf248709b3", size = 345179, upload-time = "2026-05-28T11:59:35Z" }, - { url = "https://files.pythonhosted.org/packages/7c/46/d84105f062e626a1b233f863907288a4708c2d833b8b4c6fb2764bc080c0/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5c30f3f04eef4fbd362226a6f31d7c8895ca4fbb6e0b790f6890a98d8da8559", size = 376173, upload-time = "2026-05-28T11:59:36.43Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ae/469d7959ce5b1201e1de135dc735b86db3b35dd0d1734f6a44246d5f061c/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:277f6c82f0580848796c7ecc8a7173aa3bfb928e4ff831261c2f60a81dc270db", size = 383162, upload-time = "2026-05-28T11:59:37.995Z" }, - { url = "https://files.pythonhosted.org/packages/dc/a2/57853d31a1116a561aa072794602ad3f6341e18d70a8523f1bd5b9fc1e5a/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63c2c4c213f1a4e3f3de28ecab029dbdee976324e729c0d7a55211be72576b02", size = 495093, upload-time = "2026-05-28T11:59:39.453Z" }, - { url = "https://files.pythonhosted.org/packages/99/63/3a8eabcad9314b7daf5c65f451d2c33d989235cd8a5762186cf2c3f5a4f8/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3350ec808fb538fe71a1f94dfaa0e29c598dfad805ce49f0caec5ae3183c652b", size = 389829, upload-time = "2026-05-28T11:59:40.896Z" }, - { url = "https://files.pythonhosted.org/packages/4b/25/05678d97fc25e2622df14dc530fb82023174ecfff6733991ed0d78f167bd/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1b964e3ab599e718dc46c018d104b1ebc007cbc6567d827c94a687fca56d77e", size = 374786, upload-time = "2026-05-28T11:59:42.626Z" }, - { url = "https://files.pythonhosted.org/packages/88/d1/8c90b6431e80a3b91b284a5c7c8c0c4f9c006444d90477a740d6e0f9c694/rpds_py-2026.5.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:19cb09fab7b7fc96b2a6e28f2e34b72a3705ff27b37edb77455316e5d3f3dc9b", size = 386920, upload-time = "2026-05-28T11:59:44.124Z" }, - { url = "https://files.pythonhosted.org/packages/ff/99/4638f672ab356682d633ee0da9255f5b67ce6efd0b85eb94ad3e255e65a5/rpds_py-2026.5.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abe76bcdba31e576cb83eeb8797aa0d882b738fef6dc65d0601fc753806a5b46", size = 405059, upload-time = "2026-05-28T11:59:47.177Z" }, - { url = "https://files.pythonhosted.org/packages/66/3f/3546524b6eb4cc2e1f363a3d638fa52f6c24faae3500c25fb488b02f1740/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8bff7073db3899158fff55ebf57b113a67030af26f80a18978f9f0aa60250ddf", size = 553030, upload-time = "2026-05-28T11:59:48.603Z" }, - { url = "https://files.pythonhosted.org/packages/c6/c3/7b3388c796fcf471bd17194242d4dc1a7608567c0fa422bcc1c5e79f9c1e/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8ba264fa49be666cd9cc56bf34ec7002fb3d27a4aee5bcb4d43d0d18feb1bb6f", size = 618975, upload-time = "2026-05-28T11:59:50.314Z" }, - { url = "https://files.pythonhosted.org/packages/61/1e/a3cb07f2795075d1d88efddae2f541359fde5f08c81ee114c29c2949c90a/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4860b603ddda0475a8885499b3729e90229d480105b42651962a5397d995fa89", size = 581178, upload-time = "2026-05-28T11:59:51.673Z" }, - { url = "https://files.pythonhosted.org/packages/a1/74/e758c03a5ef46f04c37f2651a2893db846d569ba8a7bca469d4b58939bcd/rpds_py-2026.5.1-cp313-cp313-win32.whl", hash = "sha256:7944270ae71383f6e2657dd7d5ce4eeb4ac2d0059a6738f0510583d462ab4842", size = 212481, upload-time = "2026-05-28T11:59:53.148Z" }, - { url = "https://files.pythonhosted.org/packages/70/ec/a2aca432db9c7359b40fa393eeeaa0d166c2f70175be956e75fa24197c44/rpds_py-2026.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:88647f43a73c4e01be19b04ceef0c8d3a1958153604d13c773becd8016f2a0cf", size = 228519, upload-time = "2026-05-28T11:59:54.505Z" }, - { url = "https://files.pythonhosted.org/packages/29/60/a73bfdd45b096574556acf303bbd9fa9eed36ca8a818b514e2a5d5fe2b9d/rpds_py-2026.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:453895624ecf7db7063b1004e44037522bbaef9ff6a945e59bc71662d7a03abd", size = 223446, upload-time = "2026-05-28T11:59:56.081Z" }, - { url = "https://files.pythonhosted.org/packages/18/e2/408105fd611823f00882aea810f3989a30d26b1bab8b6beb20f98c724e0e/rpds_py-2026.5.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:b4e4bc98639ec915f512fde3aa7a95e0041d95d9c3cc86eea841fa63cb1e8600", size = 355287, upload-time = "2026-05-28T11:59:57.448Z" }, - { url = "https://files.pythonhosted.org/packages/8d/58/5c4a43436843c90d0f6d19f82c200c80e3843ca9fa07b237623327f6d384/rpds_py-2026.5.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cacedb7a6e167680acba45ad5716e89067d225dc80da0d7040cae8c81d4572fa", size = 347033, upload-time = "2026-05-28T11:59:58.881Z" }, - { url = "https://files.pythonhosted.org/packages/fb/c2/1a71acdacaf4e259b10278fb87b039ded3cf80041bcd89dd8a3ea702ded6/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68700371c5d7ae1412862ddfa719090925c93ecf351c566d66f09d04b136ea00", size = 376891, upload-time = "2026-05-28T12:00:00.516Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c8/535f3d9b65addd8e28aa87b83c6e526799c3717a88273db8ea795beeef7a/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:296c799becfa849c779c8725494fe9ed94959ed886787df4364b058465bad7f0", size = 385646, upload-time = "2026-05-28T12:00:02.394Z" }, - { url = "https://files.pythonhosted.org/packages/1c/91/dc033f313345c354ade914dbe73cdb90b615a4409ea02430d5356794f3d8/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3858b908218ee108d0bbfb2095ccc237648053c9bf98affad7cb079acaf1d97", size = 498830, upload-time = "2026-05-28T12:00:04.189Z" }, - { url = "https://files.pythonhosted.org/packages/27/fc/90fcbea459dbb8ddc18a2e0fd1de9412b48bc84ffff2db771cf714bacfd6/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4fb8d2e7cb2f850b169806d61d1b991738acec96500a75c30f49caf064ce7cef", size = 392830, upload-time = "2026-05-28T12:00:05.797Z" }, - { url = "https://files.pythonhosted.org/packages/b2/1d/46cd11a228c9750684a798d98f878be6f614aa762438da7378f035e79e35/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27b74c10ed6a8f190f4287f53bcfea348b92a84a9c9f70d30183d1e6172d580d", size = 379613, upload-time = "2026-05-28T12:00:07.433Z" }, - { url = "https://files.pythonhosted.org/packages/24/4a/d9b0c6af3a1de03eb93741bbe8be2bdce84d8fda8224f3005451d86df389/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b9a6528956191c48c52294a592dbd4a8386d7048bdb25c0efcb6b966466c6d83", size = 388183, upload-time = "2026-05-28T12:00:09.227Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b4/db7aaabdda6d020afc87d981bcc2f57a434c7dec60ecfc2ab3dd50b20351/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:af03e34e860047bc7a352b842856fcf78798fbb81132cc98bd2f907ab4eb9cd2", size = 408578, upload-time = "2026-05-28T12:00:10.779Z" }, - { url = "https://files.pythonhosted.org/packages/08/d6/070f6a41cbb343e2ac4171859bf3f3623e0ab002f72619d6d505313ec2de/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fea6e836d10abbe191d557d33bd58bd5987725fe63aa1eefe557d230209855bd", size = 553573, upload-time = "2026-05-28T12:00:12.443Z" }, - { url = "https://files.pythonhosted.org/packages/75/ab/1a71ea3589c4345dac0a0518f0e6a031cb42689277851b683c46d27463a5/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:fc0c0f878ea770a0a8a462456c5ad36fc9fe6358e6b76fdadc7f17575e0b8bf1", size = 620861, upload-time = "2026-05-28T12:00:14.09Z" }, - { url = "https://files.pythonhosted.org/packages/8a/22/9bf80a56069c0c443fcfefac639a86a744550a2898817a6dfd3e26654924/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e0b360f316d966b048b085857630b3cc51f3db2f07b06f440eac8f695374d1e3", size = 585633, upload-time = "2026-05-28T12:00:15.66Z" }, - { url = "https://files.pythonhosted.org/packages/da/68/3b2c0a75c9e04125696f84ebdbbf304acf5a40b58ba4481cdb98a922c3ba/rpds_py-2026.5.1-cp313-cp313t-win32.whl", hash = "sha256:a2999883eedf72fdfb7520b92c7d4ec2572a71ff40239377aa604cc529eecafc", size = 210074, upload-time = "2026-05-28T12:00:17.291Z" }, - { url = "https://files.pythonhosted.org/packages/e7/8b/609157d5a25d37d4f29f92840ba531f416907c34ae5c5739dd21fc2bef98/rpds_py-2026.5.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e07be2a9d7122bd6e82dea89814ef8dc893feb1aae97fec1630f3263bbb30e55", size = 228635, upload-time = "2026-05-28T12:00:18.73Z" }, +version = "2026.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/2a/9618a122aeb2a169a28b03889a2995fe297588964333d4a7d67bdf46e147/rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4", size = 64051, upload-time = "2026-06-30T07:17:53.009Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/9e/b818ee580026ec578138e961027a68820c40afeb1ec8f6819b54fb99e196/rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223", size = 343012, upload-time = "2026-06-30T07:15:36.005Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6b/686d9dc4359a8f163cfbbf89ee0b4e586431de22fe8248edb63a8cf50d49/rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f", size = 338203, upload-time = "2026-06-30T07:15:37.462Z" }, + { url = "https://files.pythonhosted.org/packages/9e/9b/069aa329940f8207615e091f5eedbbd40e1e15eac68a0790fd05ccdf796c/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f", size = 367984, upload-time = "2026-06-30T07:15:39.008Z" }, + { url = "https://files.pythonhosted.org/packages/14/db/34c203e4becff3703e4d3bc121842c00b8689197f398161203a880052f4e/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7", size = 374815, upload-time = "2026-06-30T07:15:40.253Z" }, + { url = "https://files.pythonhosted.org/packages/ee/7d/8071067d2cc453d916ad836e828c943f575e8a44612537759002a1e07381/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6", size = 490545, upload-time = "2026-06-30T07:15:41.729Z" }, + { url = "https://files.pythonhosted.org/packages/a3/42/da06c5aa8f0484ff07f270787434204d9f4535e2f8c3b51ed402267e63c3/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af", size = 382828, upload-time = "2026-06-30T07:15:43.327Z" }, + { url = "https://files.pythonhosted.org/packages/57/d7/fe978efc2ae50abe48eb7464668ea99f53c010c60aeebb7b35ad27f23661/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf", size = 365678, upload-time = "2026-06-30T07:15:44.992Z" }, + { url = "https://files.pythonhosted.org/packages/69/9d/1d8922e1990b2a6eb532b6ff53d3e73d2b3bbffc84116c75826bee73dfc6/rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885", size = 377811, upload-time = "2026-06-30T07:15:46.523Z" }, + { url = "https://files.pythonhosted.org/packages/b1/3d/198dceafb4fb034a6a47347e1b0735d34e0bd4a50be4e898d408ee66cb14/rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4", size = 395382, upload-time = "2026-06-30T07:15:47.955Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f1/13968e49655d40b6b19d8b9140296bbc6f1d86b3f0f6c346cf9f1adddf4b/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7", size = 543832, upload-time = "2026-06-30T07:15:49.33Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ab/289bcb1b90bd3e40a2900c561fa0e2087345ecbb094f0b870f2345142b7c/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d", size = 611011, upload-time = "2026-06-30T07:15:50.847Z" }, + { url = "https://files.pythonhosted.org/packages/1e/16/5043105e679436ccfbc8e5e0dd2d663ed18a8b8113515fd06a5e5d77c83e/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97", size = 572431, upload-time = "2026-06-30T07:15:52.394Z" }, + { url = "https://files.pythonhosted.org/packages/85/ed/adab103321c0a6565d5ae1c2998349bc3ee175b82ccc5ae8fc04cc413075/rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0", size = 201710, upload-time = "2026-06-30T07:15:53.894Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ed/a03b09668e74e5dabbf2e211f6468e1820c0552f7b0500082da31841bf7b/rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80", size = 219454, upload-time = "2026-06-30T07:15:55.25Z" }, + { url = "https://files.pythonhosted.org/packages/27/17/b8642c12930b71bc2b25831f6708ccf0f75abcd11883932ec9ce54ba3a78/rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb", size = 215063, upload-time = "2026-06-30T07:15:56.573Z" }, ] [[package]] name = "ruff" -version = "0.15.20" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/dc/35b341fc554ba02f217fc10da57d1a75168cfbcf75b0ef2202176d4c4f2d/ruff-0.15.20.tar.gz", hash = "sha256:1416eb04349192646b54de98f146c4f59afe37d0decfc02c3cbbf396f3a28566", size = 4755489, upload-time = "2026-06-25T17:20:37.578Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/d9/2d5014f0253ba541d2061d9fa7193f48e941c8b21bb88a7ff9bbe0bd0596/ruff-0.15.20-py3-none-linux_armv6l.whl", hash = "sha256:00e188c53e499c3c1637f73c91dcf2fb56d576cab76ce1be50a27c4e80e37078", size = 10839665, upload-time = "2026-06-25T17:19:44.702Z" }, - { url = "https://files.pythonhosted.org/packages/c6/d3/ac1798ba64f670698867fcfc591d50e7e421bef137db564858f619a30fcf/ruff-0.15.20-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9ebd1fd9b9c95fc0bd7b2761aebec1f030013d2e193a2901b224af68fe47251b", size = 11208649, upload-time = "2026-06-25T17:19:48.787Z" }, - { url = "https://files.pythonhosted.org/packages/47/47/d3ac899991202095dfcf3d5176be4272642be3cf981a2f1a30f72a2afb95/ruff-0.15.20-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5b16cdd67ca108185cd36dce98c576350c03b1660a751de725fb049193a0632", size = 10622638, upload-time = "2026-06-25T17:19:51.354Z" }, - { url = "https://files.pythonhosted.org/packages/33/13/4e043fe30aa94d4ff5213a9881fc296d12960f5971b234a5263fdc225312/ruff-0.15.20-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3413bb3c3d2ca6a8208f1f4809cd2dca3c6de6d0b491c0e70847672bde6e6efd", size = 10984227, upload-time = "2026-06-25T17:19:54.044Z" }, - { url = "https://files.pythonhosted.org/packages/76/e6/92e7bf40388bc5800073b96564f56264f7e48bfd1a498f5ced6ae6d5a769/ruff-0.15.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd7ec42b3bb3da066488db093308a69c4ac5ee6d2af333a86ba6e2eb2e7dd44b", size = 10622882, upload-time = "2026-06-25T17:19:57.037Z" }, - { url = "https://files.pythonhosted.org/packages/13/7a/43460be3f24495a3aa46d4b16873e2c4941b3b5f0b00cf88c03b7b94b339/ruff-0.15.20-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1a36ad0eb77fba9aabfb69ede54de6f376d04ac18ebea022847046d340a8267", size = 11474808, upload-time = "2026-06-25T17:20:00.357Z" }, - { url = "https://files.pythonhosted.org/packages/27/a0/f37077884873221c6b33b4ab49eb18f9f88e54a16a25a5bca59bef46dd66/ruff-0.15.20-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6df3b1e4610432f0386dba04d853b5f08cbbc903410c6fcc02f620f05aff53c", size = 12293094, upload-time = "2026-06-25T17:20:03.446Z" }, - { url = "https://files.pythonhosted.org/packages/a6/74/165545b60256a9704c21ac0ec4a0d07933b320812f9584836c9f4aca4292/ruff-0.15.20-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e89f198a1ea6ef0d727c1cf16088bc91a6cb0ab947dedc966715691647186eae", size = 11526176, upload-time = "2026-06-25T17:20:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/86/b1/a976a136d40ade83ce743578399865f57001003a409acadc0ecbb3051082/ruff-0.15.20-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309809086c2acb67624950a3c8133e80f32d0d3e27106c0cd60ff26657c9f24b", size = 11520767, upload-time = "2026-06-25T17:20:09.191Z" }, - { url = "https://files.pythonhosted.org/packages/19/0f/f032696cb01c9b54c0263fa393474d7758f1cdc021a01b04e3cbc2500999/ruff-0.15.20-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2d2374caa2f2c2f9e2b7da0a50802cfb8b79f55a9b5e49379f564544fbf56487", size = 11500132, upload-time = "2026-06-25T17:20:13.602Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f4/51b1a14bc69e8c224b15dab9cce8e99b425e0455d462caa2b3c9be2b6a8e/ruff-0.15.20-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a1ed17b65293e0c2f22fc387bc13198a5de94bf4429589b0ff6946b0feaf21a3", size = 10943828, upload-time = "2026-06-25T17:20:16.635Z" }, - { url = "https://files.pythonhosted.org/packages/71/4b/fe267640783cd02bf6c5cc290b1df1051be2ec294c678b5c15fe19e52343/ruff-0.15.20-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f701305e66b38ea6c91882490eb73459796808e4c6362a1b765255e0cdcd4053", size = 10645418, upload-time = "2026-06-25T17:20:19.4Z" }, - { url = "https://files.pythonhosted.org/packages/b0/c0/a65aa4ec2f5e87a1df32dc3ec1fede434fe3dfd5cbcf3b503cafc676ab54/ruff-0.15.20-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b9c0c367ad8e5d0d5b5b8537864c469a0a0e55417aadfbeca41fa61333be9f4", size = 11211770, upload-time = "2026-06-25T17:20:22.033Z" }, - { url = "https://files.pythonhosted.org/packages/5a/a4/0caa331d954ae2723d729d351c989cb4ca8b6077d5c6c2cb6de75e98c041/ruff-0.15.20-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:01cc00dd58f0df339d0e902219dd53990ea99996a0344e5d9cc8d45d5307e460", size = 11618698, upload-time = "2026-06-25T17:20:25.259Z" }, - { url = "https://files.pythonhosted.org/packages/10/9b/5f14927848d2fd4aa891fd88d883788c5a7baba561c7874732364045708c/ruff-0.15.20-py3-none-win32.whl", hash = "sha256:ed65ef510e43a137207e0f01cfcf998aeddb1aeeda5c9d35023e910284d7cf21", size = 10857322, upload-time = "2026-06-25T17:20:28.612Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f0/fe47c501f9dea92a26d788ff98bb5d92ed4cb4c88792c5c88af6b697dc8e/ruff-0.15.20-py3-none-win_amd64.whl", hash = "sha256:a525c81c70fb0380344dd1d8745d8cc1c890b7fc94a58d5a07bd8eb9557b8415", size = 11993274, upload-time = "2026-06-25T17:20:31.871Z" }, - { url = "https://files.pythonhosted.org/packages/d7/2b/9555445e1201d92b3195f45cdb153a0b68f24e0a4273f6e3d5ab46e212bb/ruff-0.15.20-py3-none-win_arm64.whl", hash = "sha256:2f5b2a6d614e8700388806a14996c40fab2c47b819ef57d790a34878858ed9ca", size = 11343498, upload-time = "2026-06-25T17:20:35.03Z" }, +version = "0.15.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/06/ae069393fc66e8ff33036d4b368003833bf6e88ccf182e17e7a2f1c754fd/ruff-0.15.22.tar.gz", hash = "sha256:3f15175b1fb580126f58285a5dae6b2ea89000136d980c64499211f116b54809", size = 4785063, upload-time = "2026-07-16T15:14:13.244Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/18/ee54b7ae1e121be7a28ea6da4b67564ebb0530e183a54415ab7e3bcd2c4e/ruff-0.15.22-py3-none-linux_armv6l.whl", hash = "sha256:44423e73493737f5e7c5b41d475483898ff37afcdae38bc3da5085e29af1c2d8", size = 10781258, upload-time = "2026-07-16T15:13:19.452Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d2/2520cb14761ddbeaf57642a76942fc36adcbdbe53b4532241995f6fc485c/ruff-0.15.22-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b82c6482946e9eda7ff2e091d25b8bad3f718684e1916d41bd56873cee05b697", size = 10999477, upload-time = "2026-07-16T15:13:23.318Z" }, + { url = "https://files.pythonhosted.org/packages/c9/10/74e53572aa758dfaa678c2a2646b5c5515d884b7ca56be4d2ce03ca4b560/ruff-0.15.22-py3-none-macosx_11_0_arm64.whl", hash = "sha256:11c1c715af53a09f714e011106bffc419751ec8232fcb5da42173284ea3fec6f", size = 10466716, upload-time = "2026-07-16T15:13:26.162Z" }, + { url = "https://files.pythonhosted.org/packages/1e/cc/44eaaf0844e028182f2d0a8f2190d0f359159aed0a9e5ab861d892f1ae2a/ruff-0.15.22-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:742a29cf29bddb7c8327895d6a10e0e6c5b38a96dd407af9b5d0857f809c0576", size = 10892644, upload-time = "2026-07-16T15:13:29.229Z" }, + { url = "https://files.pythonhosted.org/packages/9f/21/8edf559014d2b0f82beea19cfb713993ad802ccda16868769979c6090a84/ruff-0.15.22-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72af58b951b0ae395935ae79763dc349bc0eb706319d28f7a33ad2cfb3cfc178", size = 10576719, upload-time = "2026-07-16T15:13:32.35Z" }, + { url = "https://files.pythonhosted.org/packages/bf/1e/3a13abd392a3b50b62e5938a831f9ab6e588358cacad5c18545b716d2182/ruff-0.15.22-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d425005c1835eb24e2ee4161cb90e8db263415f4a71c8c72c33abaa6c0c224", size = 11376494, upload-time = "2026-07-16T15:13:35.958Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3e/422d3d95bcf04dd78e1aeac22184d4f9a8fb2c01865d39d44618484a0317/ruff-0.15.22-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8b9b3f8779a4f08c969defc3c8c35abffaa757e601ed5ae66d6d1db6519969a", size = 12208370, upload-time = "2026-07-16T15:13:39.185Z" }, + { url = "https://files.pythonhosted.org/packages/1e/91/5d065a0e0a02bf4813f5119ad278462eed081d2b832eb7c021ade0ec9e65/ruff-0.15.22-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e0dd1b2e4d3d585f897a0d137cbf4eaf6223bef4e8ce34d6bb12556c5f9249e", size = 11581098, upload-time = "2026-07-16T15:13:42.132Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f9/a0d4871d12fae702eb1f41b686caf05f1f8b124dc6db6f784f53d74918fa/ruff-0.15.22-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365523eb91d9224e1bcb03b022fbf0facb8f9e23792a2c53d9d4b3924bdbdebb", size = 11399422, upload-time = "2026-07-16T15:13:45.2Z" }, + { url = "https://files.pythonhosted.org/packages/18/80/c843a5176cddbceb0b7e8dd41cf9993490796c1c469348d384f5a5c13c56/ruff-0.15.22-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:fabfd168afdf29fee5be98b831efa9683c94d7c5a3b58b9ce5a2e38444589a74", size = 11381683, upload-time = "2026-07-16T15:13:48.46Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/8485de0ae92239438a36cfc51350db9b9e85c9ebdfaea91b18e422706662/ruff-0.15.22-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:225dbf095a87f1d9f90f5fd7924d2613ee452a75a4308c63a8f50f761787aa7c", size = 10850295, upload-time = "2026-07-16T15:13:51.655Z" }, + { url = "https://files.pythonhosted.org/packages/fa/91/24977ec2ec72eaf15e4394ace2959fdff2dd1e14f03e005e838023407169/ruff-0.15.22-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1877d63b9d24ed278744f1523fd11b85540566d54641f97c566d7d9dc5ca5296", size = 10579640, upload-time = "2026-07-16T15:13:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/9c/47/9b51216951974df1f263ac19da550d34252e0ed7218c25f10c5ef9ed7517/ruff-0.15.22-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a1606c510bd7215680d32efab38965f7cdec3ef69f5170a3f4791404ffdd5262", size = 11105077, upload-time = "2026-07-16T15:13:57.915Z" }, + { url = "https://files.pythonhosted.org/packages/c2/47/20e9d4a3b8016778acea5fc32bb50d35d207500a17ddb529ffa6996feef8/ruff-0.15.22-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:630479b18625f5ffc373f77603a22a9f8ac0acd7ff0501178b5db28ec71e9c64", size = 11490980, upload-time = "2026-07-16T15:14:01.032Z" }, + { url = "https://files.pythonhosted.org/packages/4d/76/3f72d8fc38c1cb77b38c56a70da9d0c17700cc1cc50f9649c9d3c8f5ba71/ruff-0.15.22-py3-none-win32.whl", hash = "sha256:e5ba0e4a13fd14abbed2a77b517a3911290c6c6c59ef67784328d1668fab76cf", size = 10789165, upload-time = "2026-07-16T15:14:04.16Z" }, + { url = "https://files.pythonhosted.org/packages/cb/46/4965251734c2b6fcdca1b1b187d20bcac3af0ee5b083b89c910bb961ce3a/ruff-0.15.22-py3-none-win_amd64.whl", hash = "sha256:9be63ba1eb936acd2d1342fb8337c356353706fce233b2a15a09a97037e6acde", size = 11938297, upload-time = "2026-07-16T15:14:07.316Z" }, + { url = "https://files.pythonhosted.org/packages/57/c9/e69b1ff4c8b69093ef08b8919ab767af0569666865b39c30a8795d88d3c6/ruff-0.15.22-py3-none-win_arm64.whl", hash = "sha256:e1168075b72158510839f250027659cdd78476f40507dd517892304c41318661", size = 11298172, upload-time = "2026-07-16T15:14:10.51Z" }, ] [[package]] @@ -3237,24 +3214,24 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.63.0" +version = "2.66.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/c8/b3c970a5b186722d276cd40a05b3254e03bccc0208560aff20f612e018e8/sentry_sdk-2.63.0.tar.gz", hash = "sha256:2a1502bf864769275dbc8c2c9fc7a0f7f5e18358180b615d262d13a31ffba216", size = 912449, upload-time = "2026-06-16T12:45:57.553Z" } +sdist = { url = "https://files.pythonhosted.org/packages/48/ff/670abe04c5072719b5060ed93851d0d69525d60f8f2c5810f8becd58f9c1/sentry_sdk-2.66.0.tar.gz", hash = "sha256:9727d35aa83c56cd53294676fe65b96296a334c9ce107fa2142bd70f47acb265", size = 935745, upload-time = "2026-07-16T12:42:04.663Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/57/cb205f7d93373120f666b9c5736dc0815524d96a9b278e7a728f018dc22a/sentry_sdk-2.63.0-py3-none-any.whl", hash = "sha256:3a9b5ddd403f79eb73bd670f75f04485819db53d28f76ced7bc09041cb0dfd6a", size = 495950, upload-time = "2026-06-16T12:45:55.819Z" }, + { url = "https://files.pythonhosted.org/packages/c7/bb/49b10783f29067da2eec179320617e94faf63196609de47aeab3c26c3325/sentry_sdk-2.66.0-py3-none-any.whl", hash = "sha256:096136c214c602be2b323524d30755dc5b30ec5a218a206207f33b12c05c6f11", size = 504769, upload-time = "2026-07-16T12:42:02.919Z" }, ] [[package]] name = "setuptools" -version = "81.0.0" +version = "83.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, + { url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" }, ] [[package]] @@ -3482,7 +3459,7 @@ wheels = [ [[package]] name = "tensorboard" -version = "2.20.0" +version = "2.21.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "absl-py" }, @@ -3497,7 +3474,7 @@ dependencies = [ { name = "werkzeug" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/d9/a5db55f88f258ac669a92858b70a714bbbd5acd993820b41ec4a96a4d77f/tensorboard-2.20.0-py3-none-any.whl", hash = "sha256:9dc9f978cb84c0723acf9a345d96c184f0293d18f166bb8d59ee098e6cfaaba6", size = 5525680, upload-time = "2025-07-17T19:20:49.638Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/cd2eec9642781a8f5b2fb9994e3933a7b259ab18e9d49aeede9b5acf6311/tensorboard-2.21.0-py3-none-any.whl", hash = "sha256:7279316dcb6bd5bc391d623dea841531299cde1887310e8133bc34a996d32255", size = 5516204, upload-time = "2026-06-29T20:48:04.472Z" }, ] [[package]] @@ -3582,16 +3559,15 @@ wheels = [ [[package]] name = "torch" -version = "2.12.1" +version = "2.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, - { name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, { name = "filelock" }, { name = "fsspec" }, { name = "jinja2" }, { name = "networkx" }, - { name = "nvidia-cublas", marker = "sys_platform == 'linux'" }, { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, @@ -3602,23 +3578,23 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/4a/0300261818e1560d72cc160ac826005507e8b7ca0a35788b591436d05b4a/torch-2.12.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:c75e93173c700bccd6bfcc4a9d19ce242ab6dacd1f1781483027a16239b9e650", size = 87992358, upload-time = "2026-06-17T21:07:40.299Z" }, - { url = "https://files.pythonhosted.org/packages/30/a7/874a5ca05e8f159211dca7921060f7057acc1adb26431e119fd150623efc/torch-2.12.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:fcb61ccd20784b62bdd78ec84238a5cfb383b4994902e03bac95505ab360884c", size = 426386134, upload-time = "2026-06-17T21:07:31.481Z" }, - { url = "https://files.pythonhosted.org/packages/e1/75/20bb8fe9c1ad6538cce8cd0391b51927ae5af0b17ed1eab44b8824465dc1/torch-2.12.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:f4afc8083dff08719edbea346644476e3cec0cf40ebe256be0ee5d5b7c7e8c0d", size = 532268019, upload-time = "2026-06-17T21:05:37.925Z" }, - { url = "https://files.pythonhosted.org/packages/d1/fa/824ddb662af55b2eabc0dbb7b57c7c0b1bcd93693754a2b8509ec4d16490/torch-2.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:f92609e3b3ce72f25e2eb780d043ced2480c1a86c47c852604fc7a9108648386", size = 122987777, upload-time = "2026-06-17T21:07:09.49Z" }, + { url = "https://files.pythonhosted.org/packages/0d/fa/c1c10b7aff4a9a3e8956d4f0a5f468fa6db7abc3208805719076772b4833/torch-2.13.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:33449899ce5496c1b84b4853179d94fd102028ae1407314d9fb956bb79e70d09", size = 111213743, upload-time = "2026-07-08T16:03:28.579Z" }, + { url = "https://files.pythonhosted.org/packages/11/18/9ecb37b56293a0be8d80f810bf672a72fe7e02f8b475d5ef1b9bf8a0d748/torch-2.13.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:1e09d6a722504957c694faceca843acde562786df1144ebcc5a74075ec7f6005", size = 427213008, upload-time = "2026-07-08T16:03:44.106Z" }, + { url = "https://files.pythonhosted.org/packages/d4/5a/7c50ba1b7b713d71d34669c6d13dab0a11531a3eceb0307a5162dbfec0f7/torch-2.13.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:a3a9a21312872af8a26950b2c15680335a386a1f56ed03e780653d78b9607e9e", size = 526602329, upload-time = "2026-07-08T16:03:12.649Z" }, + { url = "https://files.pythonhosted.org/packages/91/3d/e7adcc6aaf36961cd18f56cf8ad0f3058c3a5c84ccf391762176c94581b8/torch-2.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:49b58f1e2c52440abb6f17c28f0335fe6c6d01ad1a7f55b0183b81e4b34d64e6", size = 122057920, upload-time = "2026-07-08T16:03:01.808Z" }, ] [[package]] name = "torch-einops-utils" -version = "0.1.5" +version = "0.1.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "einops" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/a4/41bc12d041974f84f5a9d32778af649629f8938eb8de1c48b4cf9ed4b4dd/torch_einops_utils-0.1.5.tar.gz", hash = "sha256:3b3726f0d8dd2a889a8ab8f4cc07b927d118dadf69b425a77c83b0e204e4d949", size = 11366, upload-time = "2026-06-26T18:00:38.403Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/af/937085aebe5186c3a2650400c56518fe9044446768286995831e0a414023/torch_einops_utils-0.1.7.tar.gz", hash = "sha256:4fc601d59615590d493b1377800e3bef52aa98efbfa9fb740c0a75d8c24e5d2b", size = 11897, upload-time = "2026-07-10T17:03:03.727Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/fc/024b57f74cf98efe42e35d36046502bea8adecbadce08c869c0417020ae6/torch_einops_utils-0.1.5-py3-none-any.whl", hash = "sha256:c19c0ed528f881eca75190585d0576340d5bcce3327a86432b37023826806996", size = 8462, upload-time = "2026-06-26T18:00:37.505Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d3/b43efc8ee1a210a09290d44a12764237713d93aafab788e5a0b5e1d3c4bf/torch_einops_utils-0.1.7-py3-none-any.whl", hash = "sha256:c46a650ca8ac7ef30513a9cd2c9d0d778c6309204757456e99781f9ed0bae19a", size = 8758, upload-time = "2026-07-10T17:03:02.901Z" }, ] [[package]] @@ -3647,19 +3623,19 @@ wheels = [ [[package]] name = "tqdm" -version = "4.68.3" +version = "4.68.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/87/d7/0535a28b1f5f24f6612fb3ff1e89fb1a8d160fee0f976e0aa6803862134b/tqdm-4.68.3.tar.gz", hash = "sha256:00dfa48452b6b6cfae3dd9885636c23d3422d1ec97c66d96818cbd5e0821d482", size = 170596, upload-time = "2026-06-17T07:36:52.105Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/5f/57ff8b434839e70dab45601284ea413e947a63799891b7553e5960a793a8/tqdm-4.68.4.tar.gz", hash = "sha256:19829c9673638f2a0b8617da4cdcb927e831cd88bcfcb6e78d42a4d1af131520", size = 792418, upload-time = "2026-07-07T09:58:18.369Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d8/8e/bb97bb0c71802080bfc8952937d174e49cfc50de5c951dd47b2496f0dcdb/tqdm-4.68.3-py3-none-any.whl", hash = "sha256:39832cc2def2789a6f29df83f172db7416cea70052c0907a57801c5f2fdccb03", size = 78337, upload-time = "2026-06-17T07:36:50.132Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/5e5e750890ada51017d18d0d4c30da696e5b5bd3180947729927628fc3cb/tqdm-4.68.4-py3-none-any.whl", hash = "sha256:5168118b2368f48c561afda8020fd79195b1bdb0bdf8086b88442c267a315dc2", size = 676612, upload-time = "2026-07-07T09:58:16.256Z" }, ] [[package]] name = "transformers" -version = "5.12.1" +version = "5.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, @@ -3672,9 +3648,9 @@ dependencies = [ { name = "tqdm" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/7c/8240f612819718100a9346dc28dea6a11370c3ca9c8c6eabadd3dea4ef29/transformers-5.12.1.tar.gz", hash = "sha256:679ee731c8225347889ad4fb3b2c926a62e9da3b7d284e9d12c791da7272466b", size = 8924054, upload-time = "2026-06-15T17:27:50.604Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/fb/2a2ba88f325e68a921d8b69ff63b477830b2e73ade9a3c8c8cab2f06d741/transformers-5.14.1.tar.gz", hash = "sha256:60d196c27781eacf8637e2b533f517582907ad6f9ae142046d6b69431a5b2173", size = 9295927, upload-time = "2026-07-16T09:41:57.773Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/df/56/bbd60dd8668055803bf8ba55a81f9b8a8b31497f620109a9671d26a2076d/transformers-5.12.1-py3-none-any.whl", hash = "sha256:2a5e109d2021265df7098ffbb738295acaf5ad256f12cbc586db2ea4dcbb1a8a", size = 11150587, upload-time = "2026-06-15T17:27:46.679Z" }, + { url = "https://files.pythonhosted.org/packages/6f/67/8d85ca2323233ae3c0365a659c4e52ee1f587b440e4bc577e7d8e4416d0f/transformers-5.14.1-py3-none-any.whl", hash = "sha256:9db974c4079ede2d1a3ea7ca5a240df33f2cc26fc2b36ba64c5f2a4f43b6e725", size = 11625234, upload-time = "2026-07-16T09:41:54.143Z" }, ] [[package]] @@ -3697,17 +3673,17 @@ wheels = [ [[package]] name = "typer" -version = "0.25.1" +version = "0.27.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, - { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "rich" }, { name = "shellingham" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/51/9aed62104cea109b820bbd6c14245af756112017d309da813ef107d42e7e/typer-0.25.1.tar.gz", hash = "sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc", size = 122276, upload-time = "2026-04-30T19:32:16.964Z" } +sdist = { url = "https://files.pythonhosted.org/packages/37/78/fda3361b56efc27944f24225f6ecd13d96d6fcfe37bd0eb34e2f4c63f9fc/typer-0.27.0.tar.gz", hash = "sha256:629bd12ea5d13a17148125d9a264f949eb171fb3f120f9b04d85873cab054fa5", size = 203430, upload-time = "2026-07-15T19:21:07.007Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409, upload-time = "2026-04-30T19:32:18.271Z" }, + { url = "https://files.pythonhosted.org/packages/40/03/26a383c9e58c213199d1aad1c3d353cfc22d4444ec6d2c0bf8ad02523843/typer-0.27.0-py3-none-any.whl", hash = "sha256:6f4b27631e47f077871b7dc30e933ec0131c1390fbe0e387ea5574b5bac9ccf1", size = 122716, upload-time = "2026-07-15T19:21:05.553Z" }, ] [[package]] @@ -3730,11 +3706,11 @@ wheels = [ [[package]] name = "typing-extensions" -version = "4.15.0" +version = "4.16.0" source = { registry = "https://pypi.org/simple" } -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" } +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/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" }, + { 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]] @@ -3751,11 +3727,11 @@ wheels = [ [[package]] name = "tzdata" -version = "2026.2" +version = "2026.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ff/5a28bdfd8c3ebec42564ac7d0e54ca3db65044a9314a97f9564fa7a1e926/tzdata-2026.3.tar.gz", hash = "sha256:4a1518b8993086a7982523e071643f3c0e5f213e75b21318e78bcabfff9d1415", size = 198674, upload-time = "2026-07-10T08:50:37.887Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, + { url = "https://files.pythonhosted.org/packages/e5/6d/b53b99a9f2766d095985947a5782f1702cabb129a34f7a802d7197af832f/tzdata-2026.3-py2.py3-none-any.whl", hash = "sha256:dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931", size = 348168, upload-time = "2026-07-10T08:50:36.46Z" }, ] [[package]] @@ -3769,20 +3745,19 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.49.0" +version = "0.51.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/1f/fa18009dea8469069cca78a4e877a008ab78f08b064bfc9ab891579077ff/uvicorn-0.49.0.tar.gz", hash = "sha256:ebf4271aa580d9de97f93192d4595176df6e91f9aae919ca73e4fc07df1e66a3", size = 91284, upload-time = "2026-06-03T22:01:30.448Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/65/b7c6c443ccc58678c91e1e973bbe2a878591538655d6e1d47f24ba1c51f3/uvicorn-0.51.0.tar.gz", hash = "sha256:f6f4b69b657c312f516dd2d268ab9ae6f254b11e4bac504f37b2ab58b24dd0b0", size = 94412, upload-time = "2026-07-08T10:59:05.962Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/fa/e1388bbcf24ef3274f45c0c1c7b501fd14971037c1b6ee23610553307497/uvicorn-0.49.0-py3-none-any.whl", hash = "sha256:ba3d14c3ee7e41c6c654c46c9eb489d33213cdd30aa1696eab1374337c13f68f", size = 71376, upload-time = "2026-06-03T22:01:29.037Z" }, + { url = "https://files.pythonhosted.org/packages/45/ec/dbb7e5a6b91f86bfb9eb7d2988a2730907b6a729875b949c7f022e8b88fa/uvicorn-0.51.0-py3-none-any.whl", hash = "sha256:5d38af6cd620f2ae3849fb44fd4879e0890aa1febe8d47eb355fb45d93fe6a5b", size = 73219, upload-time = "2026-07-08T10:59:04.44Z" }, ] [package.optional-dependencies] standard = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "httptools" }, { name = "python-dotenv" }, { name = "pyyaml" }, @@ -3807,7 +3782,7 @@ wheels = [ [[package]] name = "virtualenv" -version = "21.5.1" +version = "21.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -3815,9 +3790,9 @@ dependencies = [ { name = "platformdirs" }, { name = "python-discovery" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/a5/81f987504738e6defeed61ec1c47e2aefab3c35d8eeb87e1b3f38cf28254/virtualenv-21.5.1.tar.gz", hash = "sha256:dca3bf98275a59c652b69d68e73433e597d977c2da9198882479d1a7188009c8", size = 4578798, upload-time = "2026-06-16T16:23:58.603Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/d9/b477fddb68840b570af8b22afe9b035cbc277b5fb7b33dea390617a8b10f/virtualenv-21.6.1.tar.gz", hash = "sha256:15f978b7cd329f24855ff4a0c4b4899cc7678589f49adbdcbbb4d3232e641128", size = 5526620, upload-time = "2026-07-10T19:33:53.312Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/02/3623e6169bed617ed1e2d372f7c69f92ec28d54c4dfc997055c8578ec148/virtualenv-21.5.1-py3-none-any.whl", hash = "sha256:55aa670b67bbfb991b03fda39bd3276d92c419d702376e98c5df1c9989a26783", size = 4558820, upload-time = "2026-06-16T16:23:56.963Z" }, + { url = "https://files.pythonhosted.org/packages/c1/7c/4e7225d46d634a0d8d534dd8a6ce0c319d09b4d0cf0337eb314ca4789d8c/virtualenv-21.6.1-py3-none-any.whl", hash = "sha256:afe991df855715a2b2f60edfcc0107ef95a79fdfd8cb4cdaa71603d1c12e463b", size = 5506392, upload-time = "2026-07-10T19:33:51.629Z" }, ] [[package]] @@ -3858,29 +3833,37 @@ wheels = [ [[package]] name = "wcwidth" -version = "0.8.1" +version = "0.8.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/b4/51fe890511f0f242d07cb1ebe6a5b6db417262b9d2568b460347c57d95cc/wcwidth-0.8.1.tar.gz", hash = "sha256:faf5b4a5366a72dc49cad48cdf21f52bdf63bdda995178e483ba247ff79089b9", size = 1466072, upload-time = "2026-06-08T05:57:23.146Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/74/c6428f875774288bec1396f5bfcbc2d925700a4dad61727fd5f2b12f249d/wcwidth-0.8.2.tar.gz", hash = "sha256:91fbef97204b96a3d4d421609b80340b760cf33e26da123ff243d76b1fda8dda", size = 1466253, upload-time = "2026-06-29T18:11:11.601Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/6e/95b0e537de1f4d4301f76f944642c6da50d1511cc7b3d64dc418a66c7509/wcwidth-0.8.1-py3-none-any.whl", hash = "sha256:f453740b1e4a4f3291faa37944c555d71056c4da08d59809b307ef4feba695c8", size = 323092, upload-time = "2026-06-08T05:57:21.413Z" }, + { url = "https://files.pythonhosted.org/packages/96/42/3e5985a0a7e57de470b320c6d6a1a67c844f6737a587f3d44dd13d1819e7/wcwidth-0.8.2-py3-none-any.whl", hash = "sha256:d63947694a0539a1d51e01eda7caf800c291020e6cdd7e28ad7b14dd33ad4f85", size = 323166, upload-time = "2026-06-29T18:11:09.888Z" }, ] [[package]] name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, +version = "16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/02/b9a097e1e16fee4e2fd1ec8c39f6a9c5d6257bae8fa12640caf869f54436/websockets-16.1.tar.gz", hash = "sha256:299468cbe42e2b9981134c7c51d99387d8a7bf562b00183b3eec53f882846dad", size = 182530, upload-time = "2026-07-10T06:32:57.734Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/63/df158b155420b566f025e75613424ad9649a24bcb0e9f259321ab3d58bea/websockets-16.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:b0232ed141cec3df2af5a3959a071c51f40036336b0d37e17faf9ef52fc73e47", size = 179791, upload-time = "2026-07-10T06:31:33.108Z" }, + { url = "https://files.pythonhosted.org/packages/74/cf/00fe9414dfeafa6fe54eae9f5716c8c8e9ac59d192be3b893c096d395846/websockets-16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a71b73d143991714144e159f767b698f03c4a70b8a65ae1733b650cff488045b", size = 177472, upload-time = "2026-07-10T06:31:34.522Z" }, + { url = "https://files.pythonhosted.org/packages/8b/76/b10633424d40681b4e892ffd08ca5226322b2426e62d4ab71eae484c3a32/websockets-16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:187323204c3b2fc465e8fc2609e60437c521790cb9c1acb49c4c452a33e57f37", size = 177737, upload-time = "2026-07-10T06:31:35.964Z" }, + { url = "https://files.pythonhosted.org/packages/dc/61/d3bb03b2229bb1afd72008742d586cf1ea240dce64dd48c71c8c7fd3294c/websockets-16.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9dba74233c8c3ce368850818c98354dad2570f57231b3fd3bd00d7aa57628881", size = 187403, upload-time = "2026-07-10T06:31:37.496Z" }, + { url = "https://files.pythonhosted.org/packages/26/16/cc2e80478f688fc3c39c67dc1fac6a0783858058914ebc2489917462cb42/websockets-16.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63339bc8c63c86a463177775cb7c677691f5bcfac7b3b2f01b286d42acd41600", size = 188639, upload-time = "2026-07-10T06:31:38.86Z" }, + { url = "https://files.pythonhosted.org/packages/15/d6/ad87b2507e57de1cbf897a56c963f2925962ed5e85fbe06aaa83ced27acd/websockets-16.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:23e545ea8ae4263e37cdfd4e22a217f519e48e432728bc461185bbf585f38a83", size = 190078, upload-time = "2026-07-10T06:31:40.218Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1a/5b37b3fd335d5811f29fc829f2646a3e6d1463a4bf09c3100708684c766e/websockets-16.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2237081454846fb40403a80ba86d82e2038b9c45865ab96af0abe7d002a91045", size = 189267, upload-time = "2026-07-10T06:31:41.523Z" }, + { url = "https://files.pythonhosted.org/packages/42/98/06afc33e9450d4230f94c664db78875d90f5f6a5fb77f0bc6ec15ae74e1c/websockets-16.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5f5218de1ed047385ca53744caba9435d65f75d008364970a3fae95a05812cf9", size = 188022, upload-time = "2026-07-10T06:31:42.838Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/42fef5d5887c18cf2d148b02debf56cecb9cfbffc68027cde9b12c8f432c/websockets-16.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75c98e3920039d0edff03b74478ada504b7ce3a1bc406db2cabfca84320f7baf", size = 185435, upload-time = "2026-07-10T06:31:44.219Z" }, + { url = "https://files.pythonhosted.org/packages/a0/9b/8021c133add5fe40ed40312553a6cd1408c069d7efe3444ad483d4973ed3/websockets-16.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1facd189d8190af30487a55b4c3688484dd50801628a3b5b2ccd26db08e67057", size = 188080, upload-time = "2026-07-10T06:31:45.986Z" }, + { url = "https://files.pythonhosted.org/packages/69/54/1e37384f395eaa127383aab15c1c45e200890a7d7b99db5c312233d193e0/websockets-16.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cc0c6a6eef613c7da32d4fb068f82ef834b58134f6a16b54e6c1e5bf9529ab3d", size = 186678, upload-time = "2026-07-10T06:31:47.449Z" }, + { url = "https://files.pythonhosted.org/packages/68/79/1caeacab5bc2081e4519288d248bc8bd2de30652e6eaa94be6be09a1fe5b/websockets-16.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ad9411eded8988b879be6038206698bf7106c85a78f642c004485bcb95be17eb", size = 188554, upload-time = "2026-07-10T06:31:48.886Z" }, + { url = "https://files.pythonhosted.org/packages/ee/83/b3dca5fad71487b726e31cb0acf56f226792c1cc34e6ab18cbf146bd2d74/websockets-16.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:cd68f0914f3b64694895bc5e9b14e8b447e41d7bf5ffaf989bb8dcb5e2dfdce7", size = 186109, upload-time = "2026-07-10T06:31:50.508Z" }, + { url = "https://files.pythonhosted.org/packages/5b/0b/8f246c3712f07f207b52ea5fb47f3b2b66fafec7303162644c74aed51c6a/websockets-16.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fef2debfe7f7ebdda12176f26166f95b7af17af05ba06150fcf889032e0213e9", size = 187061, upload-time = "2026-07-10T06:31:51.861Z" }, + { url = "https://files.pythonhosted.org/packages/47/eb/27d6c92a01696b6495386af4fc941d7d0a13f2eab2bf9c336111d7321491/websockets-16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3cd6c9b798218798f4bb7b2e71c38f0e744bb94ca537b13376f88019d46384d", size = 187347, upload-time = "2026-07-10T06:31:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/6b/d5/eeee439921f55d5eaeabcea18d0f7ce32cdc39cb8fc1e185431a094c5c7b/websockets-16.1-cp313-cp313-win32.whl", hash = "sha256:84c170c6869633536921e4474b1cce7254c0c9b0053ef5725f966cee47e718e4", size = 180149, upload-time = "2026-07-10T06:31:55.058Z" }, + { url = "https://files.pythonhosted.org/packages/a3/03/971e98d4a4864cf263f9e94c5b2b7c9a9b7682d77bfbba4e732c55ee85a9/websockets-16.1-cp313-cp313-win_amd64.whl", hash = "sha256:bef52d327d70fa75dad93ee61ea2cb1d1489aca9f35c188833563f5a3b4df0a5", size = 180458, upload-time = "2026-07-10T06:31:56.767Z" }, + { url = "https://files.pythonhosted.org/packages/66/58/bd83247f39ddc26ffc2c24eb05087a3b749e00cb4509fc6d19daa23c8495/websockets-16.1-py3-none-any.whl", hash = "sha256:c5149dfe490ec7e5ee5dbf624c642fb725f93a5575c7f00ab594ca9eddb8dd81", size = 174031, upload-time = "2026-07-10T06:32:56.079Z" }, ] [[package]] diff --git a/docs/pydantic-ai-llms-full.txt b/docs/pydantic-ai-llms-full.txt index e1f4e8a..85b384e 100644 --- a/docs/pydantic-ai-llms-full.txt +++ b/docs/pydantic-ai-llms-full.txt @@ -174,7 +174,7 @@ Unsupported `AudioUrl`, `DocumentUrl` -`VideoUrl`. `DocumentUrl` [not supported with `AzureProvider`](/docs/ai/models/openai#using-azure-with-the-responses-api) +`VideoUrl`. `DocumentUrl` [not supported with `AzureProvider`](/docs/ai/models/openai#using-azure-with-the-responses-api) or [`AlibabaProvider`](/docs/ai/models/openai#alibaba-cloud-model-studio-dashscope) [`OpenAIResponsesModel`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIResponsesModel) @@ -261,7 +261,7 @@ When URLs are forwarded to the provider, the provider fetches them under its own Don't construct [`ImageUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ImageUrl), [`AudioUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.AudioUrl), [`VideoUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.VideoUrl), or [`DocumentUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.DocumentUrl) from untrusted user input without validating the scheme and scope. For frontend-initiated uploads to cloud storage, convert references like `s3://bucket/key` into pre-signed `https://` URLs server-side before constructing the file URL part. `force_download=True` only works for `http(s)://` URLs (it routes through the library's HTTP client and applies SSRF protection); cloud-storage schemes like `s3://` and `gs://` aren't supported by the local download path and are forwarded to the provider as-is. Only use `force_download='allow-local'` for server-authored URLs, since it allows local network access. -The [UI adapters](/docs/ai/integrations/ui/overview) apply this sanitization automatically to client-submitted messages via [`UIAdapter.allowed_file_url_schemes`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allowed_file_url_schemes) and [`UIAdapter.allowed_file_url_force_download`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allowed_file_url_force_download). +The [UI adapters](/docs/ai/integrations/ui/overview) apply this sanitization automatically to client-submitted messages via [`UIAdapter.allowed_file_url_schemes`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allowed_file_url_schemes) and [`UIAdapter.allowed_file_url_force_download`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allowed_file_url_force_download). If you accept serialized `message_history` through a custom client API, use [`sanitize_messages`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.sanitize_messages) before passing that history to the agent. ## Uploaded Files @@ -334,14 +334,14 @@ Follow the [Anthropic Files API docs](https://docs.anthropic.com/en/docs/build-w Beta Feature -The Anthropic Files API is currently in beta. You need to include the beta header `anthropic-beta: files-api-2025-04-14` when making requests. +The Anthropic Files API is currently in beta. `AnthropicModel` automatically adds the required `anthropic-beta: files-api-2025-04-14` header when a request contains an Anthropic [`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile), so you don't need to set it yourself. uploaded\_file\_anthropic.py ```py import asyncio -from pydantic_ai import Agent, ModelSettings, UploadedFile +from pydantic_ai import Agent, UploadedFile from pydantic_ai.models.anthropic import AnthropicModel from pydantic_ai.providers.anthropic import AnthropicProvider @@ -354,14 +354,13 @@ async def main(): with open('document.pdf', 'rb') as f: uploaded_file = await provider.client.beta.files.upload(file=f) - # Reference the uploaded file, including the required beta header + # Reference the uploaded file; the beta header is added automatically agent = Agent(model) result = await agent.run( [ 'Summarize this document', UploadedFile(file_id=uploaded_file.id, provider_name=model.system), - ], - model_settings=ModelSettings(extra_headers={'anthropic-beta': 'files-api-2025-04-14'}), + ] ) print(result.output) #> The document discusses the main topics and key findings... @@ -411,7 +410,7 @@ Referencing uploaded images [`OpenAIChatModel`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIChatModel) can only reference uploaded _documents_ by `file_id`. Referencing an uploaded image (an `image/*` media type) raises a [`UserError`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.UserError), because the Chat Completions API doesn't accept a `file_id` for image parts. Use [`ImageUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ImageUrl) or [`BinaryContent`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.BinaryContent) for images, or use [`OpenAIResponsesModel`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIResponsesModel), which does support uploaded images. -With `OpenAIResponsesModel`, control the image [detail level](https://platform.openai.com/docs/guides/images-vision) by passing `vendor_metadata={'detail': 'high'}` (or `'low'`) to the `UploadedFile`; it defaults to `'auto'`. +With `OpenAIResponsesModel`, control the image [detail level](https://platform.openai.com/docs/guides/images-vision) by passing `vendor_metadata={'detail': 'high'}` (or `'low'`) to the `UploadedFile`; it defaults to `'auto'`. With `OpenAIChatModel`, `GroqModel`, `MistralModel`, and `XaiModel`, control the image detail level by passing `vendor_metadata={'detail': 'high'}` (or `'low'`) to the [`ImageUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ImageUrl) or [`BinaryContent`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.BinaryContent); it defaults to `'auto'`. ### Google @@ -542,8 +541,8 @@ The retry functionality is built on top of the [tenacity](https://github.com/jd/ To use the retry transports, you need to install `tenacity`, which you can do via the `retries` dependency group: -- [pip](#tab-panel-0) -- [uv](#tab-panel-1) +- [pip](#tab-panel-699) +- [uv](#tab-panel-700) Terminal @@ -988,7 +987,7 @@ Anthropic (Opus 4.6+) `{type: 'adaptive'}` + `effort='high'` -Claude Opus 4.7 and 4.8 also support `effort='xhigh'` +Claude Opus 4.7, 4.8, and Sonnet 5 also support `effort='xhigh'` Anthropic (older) @@ -1018,11 +1017,11 @@ Google (Gemini 2.5) Groq -`reasoning_format='parsed'` +`reasoning_format='parsed'` (gpt-oss also `reasoning_effort='medium'`) -`reasoning_format='parsed'` +`reasoning_format='parsed'` (gpt-oss also `reasoning_effort='high'`) -qwen3: `thinking=False` → `reasoning_effort='none'` (true disable, via `extra_body`); other reasoning models → `'hidden'` (suppresses output only) +gpt-oss: unified effort → `reasoning_effort` (`low`/`medium`/`high`, via `extra_body`; always-on, so `thinking=False` is silently ignored); qwen3: `thinking=False` → `reasoning_effort='none'` (true disable, via `extra_body`); other reasoning models → `'hidden'` (suppresses output only) OpenRouter @@ -1046,7 +1045,7 @@ xAI `reasoning_effort='high'` -Grok 4.3 supports `'none'`, `'low'`, `'medium'`, and `'high'`, and `thinking=True` omits the parameter so the model applies its own default; Grok 3 Mini only supports `'low'` and `'high'` (so `thinking=True` → `'high'`) and silently ignores `thinking=False` +Grok 4.3 supports `'none'`, `'low'`, `'medium'`, and `'high'`, and `thinking=True` omits the parameter so the model applies its own default; Grok 3 Mini only supports `'low'` and `'high'` (so `thinking=True` → `'high'`) and silently ignores `thinking=False`; Grok 4.5 supports `'low'`, `'medium'`, and `'high'` but not `'none'`, so it reasons always-on (`thinking=True` → `'medium'`) and silently ignores `thinking=False` Bedrock (Claude 4.6+) @@ -1090,7 +1089,7 @@ If your provider recommends to send back these custom fields not changed, for ca ### OpenAI Responses -The [`OpenAIResponsesModel`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIResponsesModel) can generate native thinking parts. To enable this functionality, you need to set the `OpenAIResponsesModelSettings.openai_reasoning_effort` and [`OpenAIResponsesModelSettings.openai_reasoning_summary`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIResponsesModelSettings.openai_reasoning_summary) [model settings](/docs/ai/core-concepts/agent#model-run-settings). +The [`OpenAIResponsesModel`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIResponsesModel) can generate native thinking parts. To enable this functionality, you need to set the `OpenAIResponsesModelSettings.openai_reasoning_effort` and [`OpenAIResponsesModelSettings.openai_reasoning_summary`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIResponsesModelSettings.openai_reasoning_summary) [model settings](/docs/ai/core-concepts/agent#model-run-settings). Models that support it can additionally use a `pro` [reasoning mode](/docs/ai/models/openai#reasoning-mode), which is independent of the effort and never set by the unified `thinking` setting. By default, the unique IDs of reasoning, text, and function call parts from the message history are sent to the model, which can result in errors like `"Item 'rs_123' of type 'reasoning' was provided without its required following item."` if the message history you're sending does not match exactly what was received from the Responses API in a previous response, for example if you're using a [history processor](/docs/ai/core-concepts/message-history#processing-message-history). To disable this, you can disable the [`OpenAIResponsesModelSettings.openai_send_reasoning_ids`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIResponsesModelSettings.openai_send_reasoning_ids) [model setting](/docs/ai/core-concepts/agent#model-run-settings). @@ -1100,7 +1099,7 @@ openai\_thinking\_part.py from pydantic_ai import Agent from pydantic_ai.models.openai import OpenAIResponsesModel, OpenAIResponsesModelSettings -model = OpenAIResponsesModel('gpt-5.2') +model = OpenAIResponsesModel('gpt-5.6-sol') settings = OpenAIResponsesModelSettings( openai_reasoning_effort='low', openai_reasoning_summary='detailed', @@ -1119,7 +1118,7 @@ To enable thinking, use the [`AnthropicModelSettings.anthropic_thinking`](/docs/ Note -Extended thinking (`type: 'enabled'` with `budget_tokens`) is deprecated on `claude-opus-4-6` and removed on `claude-opus-4-7` and `claude-opus-4-8`. For those models, use [adaptive thinking](#adaptive-thinking--effort) instead. +Extended thinking (`type: 'enabled'` with `budget_tokens`) is deprecated on `claude-opus-4-6` and removed on `claude-opus-4-7`, `claude-opus-4-8`, and `claude-sonnet-5`. For those models, use [adaptive thinking](#adaptive-thinking--effort) instead. anthropic\_thinking\_part.py @@ -1156,7 +1155,7 @@ agent = Agent(model, model_settings=settings) ### Adaptive Thinking & Effort -Starting with `claude-opus-4-6`, Anthropic supports [adaptive thinking](https://docs.anthropic.com/en/docs/build-with-claude/adaptive-thinking), where the model dynamically decides when and how much to think based on the complexity of each request. This replaces extended thinking (`type: 'enabled'` with `budget_tokens`) which is deprecated on Opus 4.6 and removed on Opus 4.7 and 4.8. Claude Opus 4.7 and 4.8 also add the `xhigh` effort level. Adaptive thinking also automatically enables interleaved thinking. +Starting with `claude-opus-4-6`, Anthropic supports [adaptive thinking](https://docs.anthropic.com/en/docs/build-with-claude/adaptive-thinking), where the model dynamically decides when and how much to think based on the complexity of each request. This replaces extended thinking (`type: 'enabled'` with `budget_tokens`) which is deprecated on Opus 4.6 and removed on Opus 4.7, 4.8, and Sonnet 5. Claude Opus 4.7, 4.8, and Sonnet 5 also add the `xhigh` effort level. Adaptive thinking also automatically enables interleaved thinking. anthropic\_adaptive\_thinking.py @@ -1221,10 +1220,10 @@ For Claude Sonnet 4.6+ and Opus 4.6+, Pydantic AI's unified `thinking` setting t For older Claude models or to pin a specific `budget_tokens`, you can still use [`BedrockModelSettings.bedrock_additional_model_requests_fields`](/docs/ai/api/models/bedrock/#pydantic_ai.models.bedrock.BedrockModelSettings.bedrock_additional_model_requests_fields) [model setting](/docs/ai/core-concepts/agent#model-run-settings) to pass provider-specific configuration directly: -- [Claude](#tab-panel-6) -- [OpenAI](#tab-panel-7) -- [Qwen](#tab-panel-8) -- [Deepseek](#tab-panel-9) +- [Claude](#tab-panel-695) +- [OpenAI](#tab-panel-696) +- [Qwen](#tab-panel-697) +- [Deepseek](#tab-panel-698) bedrock\_claude\_thinking\_part.py @@ -1291,7 +1290,9 @@ Groq supports different formats to receive thinking parts: - `"hidden"`: The thinking part is not included in the text content. - `"parsed"`: The thinking part has its own structured part in the response which is converted into a [`ThinkingPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ThinkingPart) object. -Two composable [model settings](/docs/ai/core-concepts/agent#model-run-settings) control thinking: [`GroqModelSettings.groq_reasoning_format`](/docs/ai/api/models/groq/#pydantic_ai.models.groq.GroqModelSettings.groq_reasoning_format) selects how thinking parts are returned (the formats above), and [`GroqModelSettings.groq_reasoning_effort`](/docs/ai/api/models/groq/#pydantic_ai.models.groq.GroqModelSettings.groq_reasoning_effort) (sent to Groq as `reasoning_effort`) controls how much the model reasons: +The unified [`ModelSettings.thinking`](/docs/ai/api/pydantic-ai/settings/#pydantic_ai.settings.ModelSettings.thinking) setting works across providers: it selects `reasoning_format='parsed'` so thinking parts are returned, and for the gpt-oss family its effort level also drives Groq's `reasoning_effort` (`minimal`/`low` → `'low'`, `medium` → `'medium'`, `high`/`xhigh` → `'high'`, `True` → `'medium'`). + +Two composable [model settings](/docs/ai/core-concepts/agent#model-run-settings) give finer control: [`GroqModelSettings.groq_reasoning_format`](/docs/ai/api/models/groq/#pydantic_ai.models.groq.GroqModelSettings.groq_reasoning_format) selects how thinking parts are returned (the formats above), and [`GroqModelSettings.groq_reasoning_effort`](/docs/ai/api/models/groq/#pydantic_ai.models.groq.GroqModelSettings.groq_reasoning_effort) (sent to Groq as `reasoning_effort`) controls how much the model reasons, taking precedence over the unified `thinking` mapping: groq\_thinking\_part.py @@ -1307,11 +1308,11 @@ agent = Agent(model, model_settings=settings) Note -Most Groq reasoning models do not support truly disabling thinking: when `thinking=False` is set via the unified setting, Pydantic AI sends `reasoning_format='hidden'`, which suppresses reasoning output but the model may still reason internally. The exception is the qwen3 family, which truly disables reasoning via `reasoning_effort='none'` -- and when `thinking=False` is combined with an explicit `groq_reasoning_effort` on qwen3, the disable wins and `groq_reasoning_effort` is ignored (with a warning). +Most Groq reasoning models do not support truly disabling thinking. When `thinking=False` is set via the unified setting, the behavior is family-specific: the qwen3 family truly disables reasoning via `reasoning_effort='none'` (and when combined with an explicit `groq_reasoning_effort` on qwen3, the disable wins and `groq_reasoning_effort` is ignored, with a warning); the gpt-oss family reasons always-on and cannot be disabled, so `thinking=False` is silently ignored; other reasoning models send `reasoning_format='hidden'`, which suppresses reasoning output but the model may still reason internally. Note -The accepted `groq_reasoning_effort` values are family-specific: the qwen3 family accepts `'none'` and `'default'`, while the gpt-oss family accepts `'low'`, `'medium'`, and `'high'` (see the [Groq docs](https://console.groq.com/docs/reasoning#reasoning-effort)). The unified `thinking` setting controls `reasoning_format` and is deliberately not mapped to `reasoning_effort`, since the valid values differ per model family. +The accepted `reasoning_effort` values are family-specific (see the [Groq docs](https://console.groq.com/docs/reasoning#reasoning-effort)): the gpt-oss family accepts `'low'`, `'medium'`, and `'high'`, so unified `thinking` effort levels map onto those; the qwen3 family accepts only `'none'` and `'default'`, so unified enable-levels there control `reasoning_format` but send no `reasoning_effort` (there is no gradation to map). An explicit `groq_reasoning_effort` always takes precedence over the unified mapping. ## OpenRouter @@ -1335,6 +1336,22 @@ Truthy [`thinking`](/docs/ai/api/pydantic-ai/settings/#pydantic_ai.settings.Mode [`thinking=False`](/docs/ai/api/pydantic-ai/settings/#pydantic_ai.settings.ModelSettings.thinking) sends `reasoning={'effort': 'none'}` -- the [documented OpenRouter disable signal](https://openrouter.ai/docs/guides/best-practices/reasoning-tokens) -- on routes whose upstream can honor disable (e.g. `anthropic/claude-sonnet-4.5`, `z-ai/glm-4.6`). On routes whose upstream is always-on (e.g. `openai/o3`, `openai/gpt-5`, `mistralai/magistral-medium-*`, `deepseek/deepseek-r1`, `x-ai/grok-3-mini`), `thinking=False` is silently ignored at the model-profile gate, matching the same model's direct-route behavior. Set [`OpenRouterModelSettings.openrouter_reasoning`](/docs/ai/api/models/openrouter/#pydantic_ai.models.openrouter.OpenRouterModelSettings.openrouter_reasoning) directly when you want explicit per-route control. +## Z.AI + +To enable thinking, use the unified [`thinking`](/docs/ai/api/pydantic-ai/settings/#pydantic_ai.settings.ModelSettings.thinking) [model setting](/docs/ai/core-concepts/agent#model-run-settings). To preserve thinking content across multi-turn conversations, also set `ZaiModelSettings.zai_clear_thinking` to `False`. + +zai\_thinking\_part.py + +```python +from pydantic_ai import Agent +from pydantic_ai.models.zai import ZaiModel, ZaiModelSettings + +model = ZaiModel('glm-5') +settings = ZaiModelSettings(thinking=True, zai_clear_thinking=False) +agent = Agent(model, model_settings=settings) +... +``` + ## Mistral Thinking is supported by the `magistral` family of models. It does not need to be specifically enabled. @@ -1347,12 +1364,6 @@ Thinking is supported by the `command-a-reasoning-08-2025` model. It does not ne Text output inside `` tags is automatically converted to [`ThinkingPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ThinkingPart) objects. You can customize the tags using the [`thinking_tags`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile.thinking_tags) field on the [model profile](/docs/ai/models/openai#model-profile). ---- - -# [](https://pydantic.dev/docs/ai/api/builtin_tools/) - - - --- # [pydantic_ai.models.anthropic](https://pydantic.dev/docs/ai/api/models/anthropic/) @@ -1529,6 +1540,14 @@ The model provider. **Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) +##### profile + +The model profile. + +Anthropic web-tool availability depends on both model support and the client/platform, so the profile's `supported_native_tools` and `anthropic_supports_dynamic_filtering` are narrowed here for clients that don't support them (e.g. Bedrock, Vertex). + +**Type:** `AnthropicModelProfile` + #### Methods ##### \_\_init\_\_ @@ -1674,7 +1693,13 @@ Possible Anthropic model names. The installed Anthropic SDK exposes the current literal set and still allows arbitrary string model names. See [the Anthropic docs](https://docs.anthropic.com/en/docs/about-claude/models) for a full list. -**Default:** `LatestAnthropicModelNames` +**Default:** `LatestAnthropicModelNames | Literal['claude-sonnet-5']` + +### DEPRECATED\_ANTHROPIC\_MODELS + +Models that have been retired by Anthropic but are still present in the SDK's type definitions. + +**Type:** [`frozenset`](https://docs.python.org/3/library/stdtypes.html#frozenset)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `frozenset({'claude-3-haiku-20240307', 'claude-opus-4-0', 'claude-opus-4-20250514', 'claude-sonnet-4-0', 'claude-sonnet-4-20250514'})` ### AnthropicTaskBudget @@ -1946,6 +1971,36 @@ Make a request to the model and return a streaming response. [`AsyncGenerator`](https://docs.python.org/3/library/typing.html#typing.AsyncGenerator)\[`StreamedResponse`\] +##### cancel\_suspended\_response + +`@async` + +```python +def cancel_suspended_response(response: ModelResponse) -> None +``` + +Cancel a server-side suspended/background response (e.g. an OpenAI background job). + +Called when a continuation is abandoned via cancellation or error. No-op by default; model classes with cancellable server-side jobs override this. + +###### Returns + +[`None`](https://docs.python.org/3/library/constants.html#None) + +##### continuation\_delay + +```python +def continuation_delay(response: ModelResponse) -> float | None +``` + +Seconds to wait before continuing a suspended response, or `None` to continue immediately. + +Called between the segments of a suspended turn. `None` by default (e.g. Anthropic `pause_turn` continues immediately); a model that polls a server-side job (e.g. OpenAI background mode) overrides this to return a poll interval so the graph doesn't busy-poll. + +###### Returns + +[`float`](https://docs.python.org/3/library/functions.html#float) | [`None`](https://docs.python.org/3/library/constants.html#None) + ##### customize\_request\_parameters ```python @@ -2021,6 +2076,12 @@ Streamed response from an LLM when calling a tool. #### Attributes +##### state + +Lifecycle state of the response. + +**Type:** [`ModelResponseState`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponseState) **Default:** `field(default='complete', init=False)` + ##### usage Get the usage of the response so far. This will not be the final usage until the stream is exhausted. @@ -2131,6 +2192,20 @@ Build a [`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.message [`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse) +##### time\_to\_first\_chunk + +```python +def time_to_first_chunk(request_start: float) -> float | None +``` + +Seconds from `request_start` to the first chunk surfaced to the consumer, or `None` if nothing was yielded. + +`request_start` must be a `time.perf_counter()` reading taken when the request was issued. The first-chunk instant is stamped on the first `async for` pull, so the result reflects when the consumer _received_ the first event: it includes any consumer-side iteration delay (debouncing, batching, or awaiting other work) on top of the chunk's transit time, which for eager consumers is negligible. + +###### Returns + +[`float`](https://docs.python.org/3/library/functions.html#float) | [`None`](https://docs.python.org/3/library/constants.html#None) + ### known\_model\_names `@cached` @@ -2189,7 +2264,7 @@ Known model names that can be used with the `model` parameter of [`Agent`](/docs `KnownModelName` is provided as a concise way to specify a model. -**Default:** `TypeAliasType('KnownModelName', Literal['anthropic:claude-3-haiku-20240307', 'anthropic:claude-fable-5', 'anthropic:claude-haiku-4-5', 'anthropic:claude-haiku-4-5-20251001', 'anthropic:claude-mythos-5', 'anthropic:claude-mythos-preview', 'anthropic:claude-opus-4-0', 'anthropic:claude-opus-4-1', 'anthropic:claude-opus-4-1-20250805', 'anthropic:claude-opus-4-20250514', 'anthropic:claude-opus-4-5', 'anthropic:claude-opus-4-5-20251101', 'anthropic:claude-opus-4-6', 'anthropic:claude-opus-4-7', 'anthropic:claude-opus-4-8', 'anthropic:claude-sonnet-4-0', 'anthropic:claude-sonnet-4-20250514', 'anthropic:claude-sonnet-4-5', 'anthropic:claude-sonnet-4-5-20250929', 'anthropic:claude-sonnet-4-6', 'bedrock:amazon.titan-text-express-v1', 'bedrock:amazon.titan-text-lite-v1', 'bedrock:amazon.titan-tg1-large', 'bedrock:anthropic.claude-3-5-haiku-20241022-v1:0', 'bedrock:anthropic.claude-3-5-sonnet-20240620-v1:0', 'bedrock:anthropic.claude-3-5-sonnet-20241022-v2:0', 'bedrock:anthropic.claude-3-7-sonnet-20250219-v1:0', 'bedrock:anthropic.claude-3-haiku-20240307-v1:0', 'bedrock:anthropic.claude-3-opus-20240229-v1:0', 'bedrock:anthropic.claude-3-sonnet-20240229-v1:0', 'bedrock:anthropic.claude-haiku-4-5-20251001-v1:0', 'bedrock:anthropic.claude-instant-v1', 'bedrock:anthropic.claude-opus-4-20250514-v1:0', 'bedrock:anthropic.claude-sonnet-4-20250514-v1:0', 'bedrock:anthropic.claude-sonnet-4-5-20250929-v1:0', 'bedrock:anthropic.claude-sonnet-4-6', 'bedrock:anthropic.claude-v2', 'bedrock:anthropic.claude-v2:1', 'bedrock:cohere.command-light-text-v14', 'bedrock:cohere.command-r-plus-v1:0', 'bedrock:cohere.command-r-v1:0', 'bedrock:cohere.command-text-v14', 'bedrock:eu.anthropic.claude-haiku-4-5-20251001-v1:0', 'bedrock:eu.anthropic.claude-sonnet-4-20250514-v1:0', 'bedrock:eu.anthropic.claude-sonnet-4-5-20250929-v1:0', 'bedrock:eu.anthropic.claude-sonnet-4-6', 'bedrock:global.anthropic.claude-opus-4-5-20251101-v1:0', 'bedrock:meta.llama3-1-405b-instruct-v1:0', 'bedrock:meta.llama3-1-70b-instruct-v1:0', 'bedrock:meta.llama3-1-8b-instruct-v1:0', 'bedrock:meta.llama3-70b-instruct-v1:0', 'bedrock:meta.llama3-8b-instruct-v1:0', 'bedrock:mistral.mistral-7b-instruct-v0:2', 'bedrock:mistral.mistral-large-2402-v1:0', 'bedrock:mistral.mistral-large-2407-v1:0', 'bedrock:mistral.mixtral-8x7b-instruct-v0:1', 'bedrock:us.amazon.nova-2-lite-v1:0', 'bedrock:us.amazon.nova-lite-v1:0', 'bedrock:us.amazon.nova-micro-v1:0', 'bedrock:us.amazon.nova-pro-v1:0', 'bedrock:us.anthropic.claude-3-5-haiku-20241022-v1:0', 'bedrock:us.anthropic.claude-3-5-sonnet-20240620-v1:0', 'bedrock:us.anthropic.claude-3-5-sonnet-20241022-v2:0', 'bedrock:us.anthropic.claude-3-7-sonnet-20250219-v1:0', 'bedrock:us.anthropic.claude-3-haiku-20240307-v1:0', 'bedrock:us.anthropic.claude-3-opus-20240229-v1:0', 'bedrock:us.anthropic.claude-3-sonnet-20240229-v1:0', 'bedrock:us.anthropic.claude-haiku-4-5-20251001-v1:0', 'bedrock:us.anthropic.claude-opus-4-20250514-v1:0', 'bedrock:us.anthropic.claude-sonnet-4-20250514-v1:0', 'bedrock:us.anthropic.claude-sonnet-4-5-20250929-v1:0', 'bedrock:us.anthropic.claude-sonnet-4-6', 'bedrock:us.meta.llama3-1-70b-instruct-v1:0', 'bedrock:us.meta.llama3-1-8b-instruct-v1:0', 'bedrock:us.meta.llama3-2-11b-instruct-v1:0', 'bedrock:us.meta.llama3-2-1b-instruct-v1:0', 'bedrock:us.meta.llama3-2-3b-instruct-v1:0', 'bedrock:us.meta.llama3-2-90b-instruct-v1:0', 'bedrock:us.meta.llama3-3-70b-instruct-v1:0', 'cerebras:gpt-oss-120b', 'cerebras:llama3.1-8b', 'cerebras:qwen-3-235b-a22b-instruct-2507', 'cerebras:zai-glm-4.7', 'cohere:c4ai-aya-expanse-32b', 'cohere:c4ai-aya-expanse-8b', 'cohere:command-nightly', 'cohere:command-r-08-2024', 'cohere:command-r-plus-08-2024', 'cohere:command-r7b-12-2024', 'deepseek:deepseek-chat', 'deepseek:deepseek-reasoner', 'deepseek:deepseek-v4-flash', 'deepseek:deepseek-v4-pro', 'gateway/anthropic:claude-3-haiku-20240307', 'gateway/anthropic:claude-fable-5', 'gateway/anthropic:claude-haiku-4-5', 'gateway/anthropic:claude-haiku-4-5-20251001', 'gateway/anthropic:claude-mythos-5', 'gateway/anthropic:claude-mythos-preview', 'gateway/anthropic:claude-opus-4-0', 'gateway/anthropic:claude-opus-4-1', 'gateway/anthropic:claude-opus-4-1-20250805', 'gateway/anthropic:claude-opus-4-20250514', 'gateway/anthropic:claude-opus-4-5', 'gateway/anthropic:claude-opus-4-5-20251101', 'gateway/anthropic:claude-opus-4-6', 'gateway/anthropic:claude-opus-4-7', 'gateway/anthropic:claude-opus-4-8', 'gateway/anthropic:claude-sonnet-4-0', 'gateway/anthropic:claude-sonnet-4-20250514', 'gateway/anthropic:claude-sonnet-4-5', 'gateway/anthropic:claude-sonnet-4-5-20250929', 'gateway/anthropic:claude-sonnet-4-6', 'gateway/bedrock:anthropic.claude-3-5-sonnet-20240620-v1:0', 'gateway/bedrock:anthropic.claude-3-haiku-20240307-v1:0', 'gateway/bedrock:eu.anthropic.claude-haiku-4-5-20251001-v1:0', 'gateway/bedrock:eu.anthropic.claude-sonnet-4-20250514-v1:0', 'gateway/bedrock:eu.anthropic.claude-sonnet-4-5-20250929-v1:0', 'gateway/bedrock:eu.anthropic.claude-sonnet-4-6', 'gateway/bedrock:global.anthropic.claude-opus-4-5-20251101-v1:0', 'gateway/google-cloud:gemini-2.5-flash', 'gateway/google-cloud:gemini-2.5-flash-image', 'gateway/google-cloud:gemini-2.5-flash-lite', 'gateway/google-cloud:gemini-2.5-flash-lite-preview-09-2025', 'gateway/google-cloud:gemini-2.5-pro', 'gateway/google-cloud:gemini-3-flash-preview', 'gateway/google-cloud:gemini-3-pro-image-preview', 'gateway/google-cloud:gemini-3.1-flash-image-preview', 'gateway/google-cloud:gemini-3.1-flash-lite-preview', 'gateway/google-cloud:gemini-3.1-pro-preview', 'gateway/google-cloud:gemini-3.5-flash', 'gateway/google:gemini-2.5-flash', 'gateway/google:gemini-2.5-flash-image', 'gateway/google:gemini-2.5-flash-lite', 'gateway/google:gemini-2.5-flash-lite-preview-09-2025', 'gateway/google:gemini-2.5-pro', 'gateway/google:gemini-3-flash-preview', 'gateway/google:gemini-3-pro-image-preview', 'gateway/google:gemini-3.1-flash-image-preview', 'gateway/google:gemini-3.1-flash-lite-preview', 'gateway/google:gemini-3.1-pro-preview', 'gateway/google:gemini-3.5-flash', 'gateway/groq:llama-3.1-8b-instant', 'gateway/groq:llama-3.3-70b-versatile', 'gateway/groq:meta-llama/llama-4-scout-17b-16e-instruct', 'gateway/groq:moonshotai/kimi-k2-instruct-0905', 'gateway/groq:openai/gpt-oss-120b', 'gateway/groq:openai/gpt-oss-20b', 'gateway/groq:openai/gpt-oss-safeguard-20b', 'gateway/openai:gpt-3.5-turbo', 'gateway/openai:gpt-3.5-turbo-0125', 'gateway/openai:gpt-3.5-turbo-1106', 'gateway/openai:gpt-3.5-turbo-16k', 'gateway/openai:gpt-4', 'gateway/openai:gpt-4-0613', 'gateway/openai:gpt-4-turbo', 'gateway/openai:gpt-4-turbo-2024-04-09', 'gateway/openai:gpt-4.1', 'gateway/openai:gpt-4.1-2025-04-14', 'gateway/openai:gpt-4.1-mini', 'gateway/openai:gpt-4.1-mini-2025-04-14', 'gateway/openai:gpt-4.1-nano', 'gateway/openai:gpt-4.1-nano-2025-04-14', 'gateway/openai:gpt-4o', 'gateway/openai:gpt-4o-2024-05-13', 'gateway/openai:gpt-4o-2024-08-06', 'gateway/openai:gpt-4o-2024-11-20', 'gateway/openai:gpt-4o-mini', 'gateway/openai:gpt-4o-mini-2024-07-18', 'gateway/openai:gpt-4o-mini-search-preview', 'gateway/openai:gpt-4o-mini-search-preview-2025-03-11', 'gateway/openai:gpt-4o-search-preview', 'gateway/openai:gpt-4o-search-preview-2025-03-11', 'gateway/openai:gpt-5', 'gateway/openai:gpt-5-2025-08-07', 'gateway/openai:gpt-5-chat-latest', 'gateway/openai:gpt-5-mini', 'gateway/openai:gpt-5-mini-2025-08-07', 'gateway/openai:gpt-5-nano', 'gateway/openai:gpt-5-nano-2025-08-07', 'gateway/openai:gpt-5.1', 'gateway/openai:gpt-5.1-2025-11-13', 'gateway/openai:gpt-5.1-chat-latest', 'gateway/openai:gpt-5.2', 'gateway/openai:gpt-5.2-2025-12-11', 'gateway/openai:gpt-5.2-chat-latest', 'gateway/openai:gpt-5.4', 'gateway/openai:gpt-5.4-mini', 'gateway/openai:gpt-5.4-mini-2026-03-17', 'gateway/openai:gpt-5.4-nano', 'gateway/openai:gpt-5.4-nano-2026-03-17', 'gateway/openai:o1', 'gateway/openai:o1-2024-12-17', 'gateway/openai:o3', 'gateway/openai:o3-2025-04-16', 'gateway/openai:o3-mini', 'gateway/openai:o3-mini-2025-01-31', 'gateway/openai:o4-mini', 'gateway/openai:o4-mini-2025-04-16', 'google-cloud:gemini-2.0-flash', 'google-cloud:gemini-2.0-flash-lite', 'google-cloud:gemini-2.5-flash', 'google-cloud:gemini-2.5-flash-image', 'google-cloud:gemini-2.5-flash-lite', 'google-cloud:gemini-2.5-flash-lite-preview-09-2025', 'google-cloud:gemini-2.5-flash-preview-09-2025', 'google-cloud:gemini-2.5-pro', 'google-cloud:gemini-3-flash-preview', 'google-cloud:gemini-3-pro-image-preview', 'google-cloud:gemini-3-pro-preview', 'google-cloud:gemini-3.1-flash-image-preview', 'google-cloud:gemini-3.1-flash-lite-preview', 'google-cloud:gemini-3.1-pro-preview', 'google-cloud:gemini-3.5-flash', 'google-cloud:gemini-flash-latest', 'google-cloud:gemini-flash-lite-latest', 'google:gemini-2.0-flash', 'google:gemini-2.0-flash-lite', 'google:gemini-2.5-flash', 'google:gemini-2.5-flash-image', 'google:gemini-2.5-flash-lite', 'google:gemini-2.5-flash-lite-preview-09-2025', 'google:gemini-2.5-flash-preview-09-2025', 'google:gemini-2.5-pro', 'google:gemini-3-flash-preview', 'google:gemini-3-pro-image-preview', 'google:gemini-3-pro-preview', 'google:gemini-3.1-flash-image-preview', 'google:gemini-3.1-flash-lite-preview', 'google:gemini-3.1-pro-preview', 'google:gemini-3.5-flash', 'google:gemini-flash-latest', 'google:gemini-flash-lite-latest', 'groq:llama-3.1-8b-instant', 'groq:llama-3.3-70b-versatile', 'groq:meta-llama/llama-4-maverick-17b-128e-instruct', 'groq:meta-llama/llama-4-scout-17b-16e-instruct', 'groq:meta-llama/llama-guard-4-12b', 'groq:meta-llama/llama-prompt-guard-2-22m', 'groq:meta-llama/llama-prompt-guard-2-86m', 'groq:moonshotai/kimi-k2-instruct-0905', 'groq:openai/gpt-oss-120b', 'groq:openai/gpt-oss-20b', 'groq:openai/gpt-oss-safeguard-20b', 'groq:playai-tts', 'groq:playai-tts-arabic', 'groq:qwen/qwen-3-32b', 'groq:whisper-large-v3', 'groq:whisper-large-v3-turbo', 'heroku:claude-3-5-haiku', 'heroku:claude-3-5-sonnet-latest', 'heroku:claude-3-7-sonnet', 'heroku:claude-3-haiku', 'heroku:claude-4-5-haiku', 'heroku:claude-4-5-sonnet', 'heroku:claude-4-6-sonnet', 'heroku:claude-4-sonnet', 'heroku:claude-opus-4-5', 'heroku:claude-opus-4-6', 'heroku:deepseek-v3-2', 'heroku:glm-4-7', 'heroku:glm-4-7-flash', 'heroku:gpt-oss-120b', 'heroku:kimi-k2-5', 'heroku:kimi-k2-thinking', 'heroku:minimax-m2', 'heroku:minimax-m2-1', 'heroku:nova-2-lite', 'heroku:nova-lite', 'heroku:nova-pro', 'heroku:qwen3-235b', 'heroku:qwen3-coder-480b', 'huggingface:Qwen/QwQ-32B', 'huggingface:Qwen/Qwen2.5-72B-Instruct', 'huggingface:Qwen/Qwen3-235B-A22B', 'huggingface:Qwen/Qwen3-32B', 'huggingface:deepseek-ai/DeepSeek-R1', 'huggingface:meta-llama/Llama-3.3-70B-Instruct', 'huggingface:meta-llama/Llama-4-Maverick-17B-128E-Instruct', 'huggingface:meta-llama/Llama-4-Scout-17B-16E-Instruct', 'mistral:codestral-latest', 'mistral:mistral-large-latest', 'mistral:mistral-moderation-latest', 'mistral:mistral-small-latest', 'moonshotai:kimi-k2-0711-preview', 'moonshotai:kimi-latest', 'moonshotai:kimi-thinking-preview', 'moonshotai:moonshot-v1-128k', 'moonshotai:moonshot-v1-128k-vision-preview', 'moonshotai:moonshot-v1-32k', 'moonshotai:moonshot-v1-32k-vision-preview', 'moonshotai:moonshot-v1-8k', 'moonshotai:moonshot-v1-8k-vision-preview', 'openai-chat:computer-use-preview', 'openai-chat:computer-use-preview-2025-03-11', 'openai-chat:gpt-3.5-turbo', 'openai-chat:gpt-3.5-turbo-0125', 'openai-chat:gpt-3.5-turbo-0301', 'openai-chat:gpt-3.5-turbo-0613', 'openai-chat:gpt-3.5-turbo-1106', 'openai-chat:gpt-3.5-turbo-16k', 'openai-chat:gpt-3.5-turbo-16k-0613', 'openai-chat:gpt-4', 'openai-chat:gpt-4-0314', 'openai-chat:gpt-4-0613', 'openai-chat:gpt-4-turbo', 'openai-chat:gpt-4-turbo-2024-04-09', 'openai-chat:gpt-4.1', 'openai-chat:gpt-4.1-2025-04-14', 'openai-chat:gpt-4.1-mini', 'openai-chat:gpt-4.1-mini-2025-04-14', 'openai-chat:gpt-4.1-nano', 'openai-chat:gpt-4.1-nano-2025-04-14', 'openai-chat:gpt-4o', 'openai-chat:gpt-4o-2024-05-13', 'openai-chat:gpt-4o-2024-08-06', 'openai-chat:gpt-4o-2024-11-20', 'openai-chat:gpt-4o-audio-preview', 'openai-chat:gpt-4o-audio-preview-2024-12-17', 'openai-chat:gpt-4o-audio-preview-2025-06-03', 'openai-chat:gpt-4o-mini', 'openai-chat:gpt-4o-mini-2024-07-18', 'openai-chat:gpt-4o-mini-audio-preview', 'openai-chat:gpt-4o-mini-audio-preview-2024-12-17', 'openai-chat:gpt-4o-mini-search-preview', 'openai-chat:gpt-4o-mini-search-preview-2025-03-11', 'openai-chat:gpt-4o-search-preview', 'openai-chat:gpt-4o-search-preview-2025-03-11', 'openai-chat:gpt-5', 'openai-chat:gpt-5-2025-08-07', 'openai-chat:gpt-5-chat-latest', 'openai-chat:gpt-5-codex', 'openai-chat:gpt-5-mini', 'openai-chat:gpt-5-mini-2025-08-07', 'openai-chat:gpt-5-nano', 'openai-chat:gpt-5-nano-2025-08-07', 'openai-chat:gpt-5-pro', 'openai-chat:gpt-5-pro-2025-10-06', 'openai-chat:gpt-5.1', 'openai-chat:gpt-5.1-2025-11-13', 'openai-chat:gpt-5.1-chat-latest', 'openai-chat:gpt-5.1-codex', 'openai-chat:gpt-5.1-codex-max', 'openai-chat:gpt-5.2', 'openai-chat:gpt-5.2-2025-12-11', 'openai-chat:gpt-5.2-chat-latest', 'openai-chat:gpt-5.2-pro', 'openai-chat:gpt-5.2-pro-2025-12-11', 'openai-chat:gpt-5.3-chat-latest', 'openai-chat:gpt-5.4', 'openai-chat:gpt-5.4-mini', 'openai-chat:gpt-5.4-mini-2026-03-17', 'openai-chat:gpt-5.4-nano', 'openai-chat:gpt-5.4-nano-2026-03-17', 'openai-chat:o1', 'openai-chat:o1-2024-12-17', 'openai-chat:o1-pro', 'openai-chat:o1-pro-2025-03-19', 'openai-chat:o3', 'openai-chat:o3-2025-04-16', 'openai-chat:o3-deep-research', 'openai-chat:o3-deep-research-2025-06-26', 'openai-chat:o3-mini', 'openai-chat:o3-mini-2025-01-31', 'openai-chat:o3-pro', 'openai-chat:o3-pro-2025-06-10', 'openai-chat:o4-mini', 'openai-chat:o4-mini-2025-04-16', 'openai-chat:o4-mini-deep-research', 'openai-chat:o4-mini-deep-research-2025-06-26', 'openai:computer-use-preview', 'openai:computer-use-preview-2025-03-11', 'openai:gpt-3.5-turbo', 'openai:gpt-3.5-turbo-0125', 'openai:gpt-3.5-turbo-0301', 'openai:gpt-3.5-turbo-0613', 'openai:gpt-3.5-turbo-1106', 'openai:gpt-3.5-turbo-16k', 'openai:gpt-3.5-turbo-16k-0613', 'openai:gpt-4', 'openai:gpt-4-0314', 'openai:gpt-4-0613', 'openai:gpt-4-turbo', 'openai:gpt-4-turbo-2024-04-09', 'openai:gpt-4.1', 'openai:gpt-4.1-2025-04-14', 'openai:gpt-4.1-mini', 'openai:gpt-4.1-mini-2025-04-14', 'openai:gpt-4.1-nano', 'openai:gpt-4.1-nano-2025-04-14', 'openai:gpt-4o', 'openai:gpt-4o-2024-05-13', 'openai:gpt-4o-2024-08-06', 'openai:gpt-4o-2024-11-20', 'openai:gpt-4o-audio-preview', 'openai:gpt-4o-audio-preview-2024-12-17', 'openai:gpt-4o-audio-preview-2025-06-03', 'openai:gpt-4o-mini', 'openai:gpt-4o-mini-2024-07-18', 'openai:gpt-4o-mini-audio-preview', 'openai:gpt-4o-mini-audio-preview-2024-12-17', 'openai:gpt-4o-mini-search-preview', 'openai:gpt-4o-mini-search-preview-2025-03-11', 'openai:gpt-4o-search-preview', 'openai:gpt-4o-search-preview-2025-03-11', 'openai:gpt-5', 'openai:gpt-5-2025-08-07', 'openai:gpt-5-chat-latest', 'openai:gpt-5-codex', 'openai:gpt-5-mini', 'openai:gpt-5-mini-2025-08-07', 'openai:gpt-5-nano', 'openai:gpt-5-nano-2025-08-07', 'openai:gpt-5-pro', 'openai:gpt-5-pro-2025-10-06', 'openai:gpt-5.1', 'openai:gpt-5.1-2025-11-13', 'openai:gpt-5.1-chat-latest', 'openai:gpt-5.1-codex', 'openai:gpt-5.1-codex-max', 'openai:gpt-5.2', 'openai:gpt-5.2-2025-12-11', 'openai:gpt-5.2-chat-latest', 'openai:gpt-5.2-pro', 'openai:gpt-5.2-pro-2025-12-11', 'openai:gpt-5.3-chat-latest', 'openai:gpt-5.4', 'openai:gpt-5.4-mini', 'openai:gpt-5.4-mini-2026-03-17', 'openai:gpt-5.4-nano', 'openai:gpt-5.4-nano-2026-03-17', 'openai:o1', 'openai:o1-2024-12-17', 'openai:o1-pro', 'openai:o1-pro-2025-03-19', 'openai:o3', 'openai:o3-2025-04-16', 'openai:o3-deep-research', 'openai:o3-deep-research-2025-06-26', 'openai:o3-mini', 'openai:o3-mini-2025-01-31', 'openai:o3-pro', 'openai:o3-pro-2025-06-10', 'openai:o4-mini', 'openai:o4-mini-2025-04-16', 'openai:o4-mini-deep-research', 'openai:o4-mini-deep-research-2025-06-26', 'test', 'xai:grok-3', 'xai:grok-3-fast', 'xai:grok-3-fast-latest', 'xai:grok-3-latest', 'xai:grok-3-mini', 'xai:grok-3-mini-fast', 'xai:grok-3-mini-fast-latest', 'xai:grok-4', 'xai:grok-4-0709', 'xai:grok-4-1-fast', 'xai:grok-4-1-fast-non-reasoning', 'xai:grok-4-1-fast-non-reasoning-latest', 'xai:grok-4-1-fast-reasoning', 'xai:grok-4-1-fast-reasoning-latest', 'xai:grok-4-fast', 'xai:grok-4-fast-non-reasoning', 'xai:grok-4-fast-non-reasoning-latest', 'xai:grok-4-fast-reasoning', 'xai:grok-4-fast-reasoning-latest', 'xai:grok-4-latest', 'xai:grok-4.20', 'xai:grok-4.20-0309', 'xai:grok-4.20-0309-non-reasoning', 'xai:grok-4.20-0309-reasoning', 'xai:grok-4.20-multi-agent', 'xai:grok-4.20-multi-agent-0309', 'xai:grok-4.20-multi-agent-latest', 'xai:grok-4.20-non-reasoning', 'xai:grok-4.20-non-reasoning-latest', 'xai:grok-4.20-reasoning-latest', 'xai:grok-4.3', 'xai:grok-4.3-latest', 'xai:grok-code-fast-1'])` +**Default:** `TypeAliasType('KnownModelName', Literal['anthropic:claude-fable-5', 'anthropic:claude-haiku-4-5', 'anthropic:claude-haiku-4-5-20251001', 'anthropic:claude-mythos-5', 'anthropic:claude-mythos-preview', 'anthropic:claude-opus-4-1', 'anthropic:claude-opus-4-1-20250805', 'anthropic:claude-opus-4-5', 'anthropic:claude-opus-4-5-20251101', 'anthropic:claude-opus-4-6', 'anthropic:claude-opus-4-7', 'anthropic:claude-opus-4-8', 'anthropic:claude-sonnet-4-5', 'anthropic:claude-sonnet-4-5-20250929', 'anthropic:claude-sonnet-4-6', 'anthropic:claude-sonnet-5', 'bedrock:amazon.titan-text-express-v1', 'bedrock:amazon.titan-text-lite-v1', 'bedrock:amazon.titan-tg1-large', 'bedrock:anthropic.claude-3-5-haiku-20241022-v1:0', 'bedrock:anthropic.claude-3-5-sonnet-20240620-v1:0', 'bedrock:anthropic.claude-3-5-sonnet-20241022-v2:0', 'bedrock:anthropic.claude-3-7-sonnet-20250219-v1:0', 'bedrock:anthropic.claude-3-haiku-20240307-v1:0', 'bedrock:anthropic.claude-3-opus-20240229-v1:0', 'bedrock:anthropic.claude-3-sonnet-20240229-v1:0', 'bedrock:anthropic.claude-haiku-4-5-20251001-v1:0', 'bedrock:anthropic.claude-instant-v1', 'bedrock:anthropic.claude-opus-4-20250514-v1:0', 'bedrock:anthropic.claude-sonnet-4-20250514-v1:0', 'bedrock:anthropic.claude-sonnet-4-5-20250929-v1:0', 'bedrock:anthropic.claude-sonnet-4-6', 'bedrock:anthropic.claude-v2', 'bedrock:anthropic.claude-v2:1', 'bedrock:cohere.command-light-text-v14', 'bedrock:cohere.command-r-plus-v1:0', 'bedrock:cohere.command-r-v1:0', 'bedrock:cohere.command-text-v14', 'bedrock:deepseek.r1-v1:0', 'bedrock:deepseek.v3.2', 'bedrock:eu.anthropic.claude-haiku-4-5-20251001-v1:0', 'bedrock:eu.anthropic.claude-sonnet-4-20250514-v1:0', 'bedrock:eu.anthropic.claude-sonnet-4-5-20250929-v1:0', 'bedrock:eu.anthropic.claude-sonnet-4-6', 'bedrock:global.amazon.nova-2-lite-v1:0', 'bedrock:global.anthropic.claude-fable-5', 'bedrock:global.anthropic.claude-opus-4-5-20251101-v1:0', 'bedrock:global.anthropic.claude-opus-4-6-v1', 'bedrock:global.anthropic.claude-opus-4-7', 'bedrock:global.anthropic.claude-opus-4-8', 'bedrock:global.anthropic.claude-sonnet-5', 'bedrock:google.gemma-3-12b-it', 'bedrock:google.gemma-3-27b-it', 'bedrock:google.gemma-3-4b-it', 'bedrock:meta.llama3-1-405b-instruct-v1:0', 'bedrock:meta.llama3-1-70b-instruct-v1:0', 'bedrock:meta.llama3-1-8b-instruct-v1:0', 'bedrock:meta.llama3-70b-instruct-v1:0', 'bedrock:meta.llama3-8b-instruct-v1:0', 'bedrock:minimax.minimax-m2', 'bedrock:minimax.minimax-m2.1', 'bedrock:minimax.minimax-m2.5', 'bedrock:mistral.devstral-2-123b', 'bedrock:mistral.magistral-small-2509', 'bedrock:mistral.ministral-3-14b-instruct', 'bedrock:mistral.ministral-3-3b-instruct', 'bedrock:mistral.ministral-3-8b-instruct', 'bedrock:mistral.mistral-7b-instruct-v0:2', 'bedrock:mistral.mistral-large-2402-v1:0', 'bedrock:mistral.mistral-large-2407-v1:0', 'bedrock:mistral.mistral-large-3-675b-instruct', 'bedrock:mistral.mistral-small-2402-v1:0', 'bedrock:mistral.mixtral-8x7b-instruct-v0:1', 'bedrock:mistral.pixtral-large-2502-v1:0', 'bedrock:moonshot.kimi-k2-thinking', 'bedrock:moonshotai.kimi-k2.5', 'bedrock:nvidia.nemotron-nano-12b-v2', 'bedrock:nvidia.nemotron-nano-3-30b', 'bedrock:nvidia.nemotron-nano-9b-v2', 'bedrock:nvidia.nemotron-super-3-120b', 'bedrock:qwen.qwen3-32b-v1:0', 'bedrock:qwen.qwen3-coder-30b-a3b-v1:0', 'bedrock:qwen.qwen3-coder-next', 'bedrock:qwen.qwen3-next-80b-a3b', 'bedrock:qwen.qwen3-vl-235b-a22b', 'bedrock:us.amazon.nova-2-lite-v1:0', 'bedrock:us.amazon.nova-lite-v1:0', 'bedrock:us.amazon.nova-micro-v1:0', 'bedrock:us.amazon.nova-premier-v1:0', 'bedrock:us.amazon.nova-pro-v1:0', 'bedrock:us.anthropic.claude-3-5-haiku-20241022-v1:0', 'bedrock:us.anthropic.claude-3-5-sonnet-20240620-v1:0', 'bedrock:us.anthropic.claude-3-5-sonnet-20241022-v2:0', 'bedrock:us.anthropic.claude-3-7-sonnet-20250219-v1:0', 'bedrock:us.anthropic.claude-3-haiku-20240307-v1:0', 'bedrock:us.anthropic.claude-3-opus-20240229-v1:0', 'bedrock:us.anthropic.claude-3-sonnet-20240229-v1:0', 'bedrock:us.anthropic.claude-fable-5', 'bedrock:us.anthropic.claude-haiku-4-5-20251001-v1:0', 'bedrock:us.anthropic.claude-opus-4-1-20250805-v1:0', 'bedrock:us.anthropic.claude-opus-4-20250514-v1:0', 'bedrock:us.anthropic.claude-opus-4-5-20251101-v1:0', 'bedrock:us.anthropic.claude-opus-4-6-v1', 'bedrock:us.anthropic.claude-opus-4-7', 'bedrock:us.anthropic.claude-opus-4-8', 'bedrock:us.anthropic.claude-sonnet-4-20250514-v1:0', 'bedrock:us.anthropic.claude-sonnet-4-5-20250929-v1:0', 'bedrock:us.anthropic.claude-sonnet-4-6', 'bedrock:us.anthropic.claude-sonnet-5', 'bedrock:us.meta.llama3-1-70b-instruct-v1:0', 'bedrock:us.meta.llama3-1-8b-instruct-v1:0', 'bedrock:us.meta.llama3-2-11b-instruct-v1:0', 'bedrock:us.meta.llama3-2-1b-instruct-v1:0', 'bedrock:us.meta.llama3-2-3b-instruct-v1:0', 'bedrock:us.meta.llama3-2-90b-instruct-v1:0', 'bedrock:us.meta.llama3-3-70b-instruct-v1:0', 'bedrock:us.meta.llama4-maverick-17b-instruct-v1:0', 'bedrock:us.meta.llama4-scout-17b-instruct-v1:0', 'bedrock:us.mistral.pixtral-large-2502-v1:0', 'bedrock:us.writer.palmyra-x4-v1:0', 'bedrock:us.writer.palmyra-x5-v1:0', 'bedrock:zai.glm-4.7', 'bedrock:zai.glm-4.7-flash', 'bedrock:zai.glm-5', 'cerebras:gpt-oss-120b', 'cerebras:llama3.1-8b', 'cerebras:qwen-3-235b-a22b-instruct-2507', 'cerebras:zai-glm-4.7', 'cohere:c4ai-aya-expanse-32b', 'cohere:c4ai-aya-expanse-8b', 'cohere:command-nightly', 'cohere:command-r-08-2024', 'cohere:command-r-plus-08-2024', 'cohere:command-r7b-12-2024', 'deepseek:deepseek-chat', 'deepseek:deepseek-reasoner', 'deepseek:deepseek-v4-flash', 'deepseek:deepseek-v4-pro', 'gateway/anthropic:claude-fable-5', 'gateway/anthropic:claude-haiku-4-5', 'gateway/anthropic:claude-haiku-4-5-20251001', 'gateway/anthropic:claude-opus-4-1', 'gateway/anthropic:claude-opus-4-1-20250805', 'gateway/anthropic:claude-opus-4-5', 'gateway/anthropic:claude-opus-4-5-20251101', 'gateway/anthropic:claude-opus-4-6', 'gateway/anthropic:claude-opus-4-7', 'gateway/anthropic:claude-opus-4-8', 'gateway/anthropic:claude-sonnet-4-5', 'gateway/anthropic:claude-sonnet-4-5-20250929', 'gateway/anthropic:claude-sonnet-4-6', 'gateway/anthropic:claude-sonnet-5', 'gateway/bedrock:anthropic.claude-3-haiku-20240307-v1:0', 'gateway/bedrock:deepseek.r1-v1:0', 'gateway/bedrock:deepseek.v3.2', 'gateway/bedrock:eu.anthropic.claude-haiku-4-5-20251001-v1:0', 'gateway/bedrock:eu.anthropic.claude-sonnet-4-20250514-v1:0', 'gateway/bedrock:eu.anthropic.claude-sonnet-4-5-20250929-v1:0', 'gateway/bedrock:eu.anthropic.claude-sonnet-4-6', 'gateway/bedrock:global.amazon.nova-2-lite-v1:0', 'gateway/bedrock:global.anthropic.claude-fable-5', 'gateway/bedrock:global.anthropic.claude-opus-4-5-20251101-v1:0', 'gateway/bedrock:global.anthropic.claude-opus-4-6-v1', 'gateway/bedrock:global.anthropic.claude-opus-4-7', 'gateway/bedrock:global.anthropic.claude-opus-4-8', 'gateway/bedrock:global.anthropic.claude-sonnet-5', 'gateway/bedrock:google.gemma-3-12b-it', 'gateway/bedrock:google.gemma-3-27b-it', 'gateway/bedrock:google.gemma-3-4b-it', 'gateway/bedrock:minimax.minimax-m2', 'gateway/bedrock:minimax.minimax-m2.1', 'gateway/bedrock:minimax.minimax-m2.5', 'gateway/bedrock:mistral.devstral-2-123b', 'gateway/bedrock:mistral.magistral-small-2509', 'gateway/bedrock:mistral.ministral-3-14b-instruct', 'gateway/bedrock:mistral.ministral-3-3b-instruct', 'gateway/bedrock:mistral.ministral-3-8b-instruct', 'gateway/bedrock:mistral.mistral-large-3-675b-instruct', 'gateway/bedrock:mistral.mistral-small-2402-v1:0', 'gateway/bedrock:mistral.pixtral-large-2502-v1:0', 'gateway/bedrock:moonshot.kimi-k2-thinking', 'gateway/bedrock:moonshotai.kimi-k2.5', 'gateway/bedrock:nvidia.nemotron-nano-12b-v2', 'gateway/bedrock:nvidia.nemotron-nano-3-30b', 'gateway/bedrock:nvidia.nemotron-nano-9b-v2', 'gateway/bedrock:nvidia.nemotron-super-3-120b', 'gateway/bedrock:qwen.qwen3-32b-v1:0', 'gateway/bedrock:qwen.qwen3-coder-30b-a3b-v1:0', 'gateway/bedrock:qwen.qwen3-coder-next', 'gateway/bedrock:qwen.qwen3-next-80b-a3b', 'gateway/bedrock:qwen.qwen3-vl-235b-a22b', 'gateway/bedrock:us.amazon.nova-premier-v1:0', 'gateway/bedrock:us.anthropic.claude-fable-5', 'gateway/bedrock:us.anthropic.claude-opus-4-1-20250805-v1:0', 'gateway/bedrock:us.anthropic.claude-opus-4-5-20251101-v1:0', 'gateway/bedrock:us.anthropic.claude-opus-4-6-v1', 'gateway/bedrock:us.anthropic.claude-opus-4-7', 'gateway/bedrock:us.anthropic.claude-opus-4-8', 'gateway/bedrock:us.anthropic.claude-sonnet-5', 'gateway/bedrock:us.meta.llama4-maverick-17b-instruct-v1:0', 'gateway/bedrock:us.meta.llama4-scout-17b-instruct-v1:0', 'gateway/bedrock:us.mistral.pixtral-large-2502-v1:0', 'gateway/bedrock:us.writer.palmyra-x4-v1:0', 'gateway/bedrock:us.writer.palmyra-x5-v1:0', 'gateway/bedrock:zai.glm-4.7', 'gateway/bedrock:zai.glm-4.7-flash', 'gateway/bedrock:zai.glm-5', 'gateway/google-cloud:gemini-2.5-flash', 'gateway/google-cloud:gemini-2.5-flash-image', 'gateway/google-cloud:gemini-2.5-flash-lite', 'gateway/google-cloud:gemini-2.5-flash-lite-preview-09-2025', 'gateway/google-cloud:gemini-2.5-pro', 'gateway/google-cloud:gemini-3-flash-preview', 'gateway/google-cloud:gemini-3-pro-image-preview', 'gateway/google-cloud:gemini-3.1-flash-image-preview', 'gateway/google-cloud:gemini-3.1-flash-lite-preview', 'gateway/google-cloud:gemini-3.1-pro-preview', 'gateway/google-cloud:gemini-3.5-flash', 'gateway/google:gemini-2.5-flash', 'gateway/google:gemini-2.5-flash-image', 'gateway/google:gemini-2.5-flash-lite', 'gateway/google:gemini-2.5-flash-lite-preview-09-2025', 'gateway/google:gemini-2.5-pro', 'gateway/google:gemini-3-flash-preview', 'gateway/google:gemini-3-pro-image-preview', 'gateway/google:gemini-3.1-flash-image-preview', 'gateway/google:gemini-3.1-flash-lite-preview', 'gateway/google:gemini-3.1-pro-preview', 'gateway/google:gemini-3.5-flash', 'gateway/groq:llama-3.1-8b-instant', 'gateway/groq:llama-3.3-70b-versatile', 'gateway/groq:meta-llama/llama-4-scout-17b-16e-instruct', 'gateway/groq:openai/gpt-oss-120b', 'gateway/groq:openai/gpt-oss-20b', 'gateway/groq:openai/gpt-oss-safeguard-20b', 'gateway/groq:qwen/qwen3-32b', 'gateway/openai:computer-use-preview', 'gateway/openai:computer-use-preview-2025-03-11', 'gateway/openai:gpt-3.5-turbo', 'gateway/openai:gpt-3.5-turbo-0125', 'gateway/openai:gpt-3.5-turbo-1106', 'gateway/openai:gpt-4', 'gateway/openai:gpt-4-0613', 'gateway/openai:gpt-4-turbo', 'gateway/openai:gpt-4-turbo-2024-04-09', 'gateway/openai:gpt-4.1', 'gateway/openai:gpt-4.1-2025-04-14', 'gateway/openai:gpt-4.1-mini', 'gateway/openai:gpt-4.1-mini-2025-04-14', 'gateway/openai:gpt-4.1-nano', 'gateway/openai:gpt-4.1-nano-2025-04-14', 'gateway/openai:gpt-4o', 'gateway/openai:gpt-4o-2024-05-13', 'gateway/openai:gpt-4o-2024-08-06', 'gateway/openai:gpt-4o-2024-11-20', 'gateway/openai:gpt-4o-mini', 'gateway/openai:gpt-4o-mini-2024-07-18', 'gateway/openai:gpt-5', 'gateway/openai:gpt-5-2025-08-07', 'gateway/openai:gpt-5-chat-latest', 'gateway/openai:gpt-5-codex', 'gateway/openai:gpt-5-mini', 'gateway/openai:gpt-5-mini-2025-08-07', 'gateway/openai:gpt-5-nano', 'gateway/openai:gpt-5-nano-2025-08-07', 'gateway/openai:gpt-5-pro', 'gateway/openai:gpt-5-pro-2025-10-06', 'gateway/openai:gpt-5.1', 'gateway/openai:gpt-5.1-2025-11-13', 'gateway/openai:gpt-5.1-chat-latest', 'gateway/openai:gpt-5.1-codex', 'gateway/openai:gpt-5.1-codex-max', 'gateway/openai:gpt-5.2', 'gateway/openai:gpt-5.2-2025-12-11', 'gateway/openai:gpt-5.2-chat-latest', 'gateway/openai:gpt-5.2-pro', 'gateway/openai:gpt-5.2-pro-2025-12-11', 'gateway/openai:gpt-5.3-chat-latest', 'gateway/openai:gpt-5.4', 'gateway/openai:gpt-5.4-mini', 'gateway/openai:gpt-5.4-mini-2026-03-17', 'gateway/openai:gpt-5.4-nano', 'gateway/openai:gpt-5.4-nano-2026-03-17', 'gateway/openai:gpt-5.6-luna', 'gateway/openai:gpt-5.6-sol', 'gateway/openai:gpt-5.6-terra', 'gateway/openai:o1', 'gateway/openai:o1-2024-12-17', 'gateway/openai:o1-pro', 'gateway/openai:o1-pro-2025-03-19', 'gateway/openai:o3', 'gateway/openai:o3-2025-04-16', 'gateway/openai:o3-mini', 'gateway/openai:o3-mini-2025-01-31', 'gateway/openai:o3-pro', 'gateway/openai:o3-pro-2025-06-10', 'gateway/openai:o4-mini', 'gateway/openai:o4-mini-2025-04-16', 'google-cloud:gemini-2.0-flash', 'google-cloud:gemini-2.0-flash-lite', 'google-cloud:gemini-2.5-flash', 'google-cloud:gemini-2.5-flash-image', 'google-cloud:gemini-2.5-flash-lite', 'google-cloud:gemini-2.5-flash-lite-preview-09-2025', 'google-cloud:gemini-2.5-flash-preview-09-2025', 'google-cloud:gemini-2.5-pro', 'google-cloud:gemini-3-flash-preview', 'google-cloud:gemini-3-pro-image-preview', 'google-cloud:gemini-3-pro-preview', 'google-cloud:gemini-3.1-flash-image-preview', 'google-cloud:gemini-3.1-flash-lite-preview', 'google-cloud:gemini-3.1-pro-preview', 'google-cloud:gemini-3.5-flash', 'google-cloud:gemini-flash-latest', 'google-cloud:gemini-flash-lite-latest', 'google:gemini-2.0-flash', 'google:gemini-2.0-flash-lite', 'google:gemini-2.5-flash', 'google:gemini-2.5-flash-image', 'google:gemini-2.5-flash-lite', 'google:gemini-2.5-flash-lite-preview-09-2025', 'google:gemini-2.5-flash-preview-09-2025', 'google:gemini-2.5-pro', 'google:gemini-3-flash-preview', 'google:gemini-3-pro-image-preview', 'google:gemini-3-pro-preview', 'google:gemini-3.1-flash-image-preview', 'google:gemini-3.1-flash-lite-preview', 'google:gemini-3.1-pro-preview', 'google:gemini-3.5-flash', 'google:gemini-flash-latest', 'google:gemini-flash-lite-latest', 'groq:llama-3.1-8b-instant', 'groq:llama-3.3-70b-versatile', 'groq:meta-llama/llama-4-maverick-17b-128e-instruct', 'groq:meta-llama/llama-4-scout-17b-16e-instruct', 'groq:meta-llama/llama-guard-4-12b', 'groq:meta-llama/llama-prompt-guard-2-22m', 'groq:meta-llama/llama-prompt-guard-2-86m', 'groq:openai/gpt-oss-120b', 'groq:openai/gpt-oss-20b', 'groq:openai/gpt-oss-safeguard-20b', 'groq:playai-tts', 'groq:playai-tts-arabic', 'groq:qwen/qwen3-32b', 'groq:whisper-large-v3', 'groq:whisper-large-v3-turbo', 'heroku:claude-3-5-haiku', 'heroku:claude-3-5-sonnet-latest', 'heroku:claude-3-7-sonnet', 'heroku:claude-3-haiku', 'heroku:claude-4-5-haiku', 'heroku:claude-4-5-sonnet', 'heroku:claude-4-6-sonnet', 'heroku:claude-4-sonnet', 'heroku:claude-opus-4-5', 'heroku:claude-opus-4-6', 'heroku:deepseek-v3-2', 'heroku:glm-4-7', 'heroku:glm-4-7-flash', 'heroku:gpt-oss-120b', 'heroku:kimi-k2-5', 'heroku:kimi-k2-thinking', 'heroku:minimax-m2', 'heroku:minimax-m2-1', 'heroku:nova-2-lite', 'heroku:nova-lite', 'heroku:nova-pro', 'heroku:qwen3-235b', 'heroku:qwen3-coder-480b', 'huggingface:Qwen/QwQ-32B', 'huggingface:Qwen/Qwen2.5-72B-Instruct', 'huggingface:Qwen/Qwen3-235B-A22B', 'huggingface:Qwen/Qwen3-32B', 'huggingface:deepseek-ai/DeepSeek-R1', 'huggingface:meta-llama/Llama-3.3-70B-Instruct', 'huggingface:meta-llama/Llama-4-Maverick-17B-128E-Instruct', 'huggingface:meta-llama/Llama-4-Scout-17B-16E-Instruct', 'mistral:codestral-latest', 'mistral:mistral-large-latest', 'mistral:mistral-moderation-latest', 'mistral:mistral-small-latest', 'moonshotai:kimi-k2-0711-preview', 'moonshotai:kimi-k2.5', 'moonshotai:kimi-k2.6', 'moonshotai:kimi-k2.7-code', 'moonshotai:kimi-k2.7-code-highspeed', 'moonshotai:kimi-latest', 'moonshotai:kimi-thinking-preview', 'moonshotai:moonshot-v1-128k', 'moonshotai:moonshot-v1-128k-vision-preview', 'moonshotai:moonshot-v1-32k', 'moonshotai:moonshot-v1-32k-vision-preview', 'moonshotai:moonshot-v1-8k', 'moonshotai:moonshot-v1-8k-vision-preview', 'moonshotai:moonshot-v1-auto', 'openai-chat:computer-use-preview', 'openai-chat:computer-use-preview-2025-03-11', 'openai-chat:gpt-3.5-turbo', 'openai-chat:gpt-3.5-turbo-0125', 'openai-chat:gpt-3.5-turbo-0301', 'openai-chat:gpt-3.5-turbo-1106', 'openai-chat:gpt-3.5-turbo-16k', 'openai-chat:gpt-4', 'openai-chat:gpt-4-0314', 'openai-chat:gpt-4-0613', 'openai-chat:gpt-4-turbo', 'openai-chat:gpt-4-turbo-2024-04-09', 'openai-chat:gpt-4.1', 'openai-chat:gpt-4.1-2025-04-14', 'openai-chat:gpt-4.1-mini', 'openai-chat:gpt-4.1-mini-2025-04-14', 'openai-chat:gpt-4.1-nano', 'openai-chat:gpt-4.1-nano-2025-04-14', 'openai-chat:gpt-4o', 'openai-chat:gpt-4o-2024-05-13', 'openai-chat:gpt-4o-2024-08-06', 'openai-chat:gpt-4o-2024-11-20', 'openai-chat:gpt-4o-audio-preview', 'openai-chat:gpt-4o-audio-preview-2024-12-17', 'openai-chat:gpt-4o-audio-preview-2025-06-03', 'openai-chat:gpt-4o-mini', 'openai-chat:gpt-4o-mini-2024-07-18', 'openai-chat:gpt-4o-mini-audio-preview', 'openai-chat:gpt-4o-mini-audio-preview-2024-12-17', 'openai-chat:gpt-4o-mini-search-preview', 'openai-chat:gpt-4o-mini-search-preview-2025-03-11', 'openai-chat:gpt-4o-search-preview', 'openai-chat:gpt-4o-search-preview-2025-03-11', 'openai-chat:gpt-5', 'openai-chat:gpt-5-2025-08-07', 'openai-chat:gpt-5-chat-latest', 'openai-chat:gpt-5-codex', 'openai-chat:gpt-5-mini', 'openai-chat:gpt-5-mini-2025-08-07', 'openai-chat:gpt-5-nano', 'openai-chat:gpt-5-nano-2025-08-07', 'openai-chat:gpt-5-pro', 'openai-chat:gpt-5-pro-2025-10-06', 'openai-chat:gpt-5.1', 'openai-chat:gpt-5.1-2025-11-13', 'openai-chat:gpt-5.1-chat-latest', 'openai-chat:gpt-5.1-codex', 'openai-chat:gpt-5.1-codex-max', 'openai-chat:gpt-5.2', 'openai-chat:gpt-5.2-2025-12-11', 'openai-chat:gpt-5.2-chat-latest', 'openai-chat:gpt-5.2-pro', 'openai-chat:gpt-5.2-pro-2025-12-11', 'openai-chat:gpt-5.3-chat-latest', 'openai-chat:gpt-5.4', 'openai-chat:gpt-5.4-mini', 'openai-chat:gpt-5.4-mini-2026-03-17', 'openai-chat:gpt-5.4-nano', 'openai-chat:gpt-5.4-nano-2026-03-17', 'openai-chat:gpt-5.6-luna', 'openai-chat:gpt-5.6-sol', 'openai-chat:gpt-5.6-terra', 'openai-chat:o1', 'openai-chat:o1-2024-12-17', 'openai-chat:o1-pro', 'openai-chat:o1-pro-2025-03-19', 'openai-chat:o3', 'openai-chat:o3-2025-04-16', 'openai-chat:o3-deep-research', 'openai-chat:o3-deep-research-2025-06-26', 'openai-chat:o3-mini', 'openai-chat:o3-mini-2025-01-31', 'openai-chat:o3-pro', 'openai-chat:o3-pro-2025-06-10', 'openai-chat:o4-mini', 'openai-chat:o4-mini-2025-04-16', 'openai-chat:o4-mini-deep-research', 'openai-chat:o4-mini-deep-research-2025-06-26', 'openai:computer-use-preview', 'openai:computer-use-preview-2025-03-11', 'openai:gpt-3.5-turbo', 'openai:gpt-3.5-turbo-0125', 'openai:gpt-3.5-turbo-0301', 'openai:gpt-3.5-turbo-1106', 'openai:gpt-4', 'openai:gpt-4-0314', 'openai:gpt-4-0613', 'openai:gpt-4-turbo', 'openai:gpt-4-turbo-2024-04-09', 'openai:gpt-4.1', 'openai:gpt-4.1-2025-04-14', 'openai:gpt-4.1-mini', 'openai:gpt-4.1-mini-2025-04-14', 'openai:gpt-4.1-nano', 'openai:gpt-4.1-nano-2025-04-14', 'openai:gpt-4o', 'openai:gpt-4o-2024-05-13', 'openai:gpt-4o-2024-08-06', 'openai:gpt-4o-2024-11-20', 'openai:gpt-4o-audio-preview', 'openai:gpt-4o-audio-preview-2024-12-17', 'openai:gpt-4o-audio-preview-2025-06-03', 'openai:gpt-4o-mini', 'openai:gpt-4o-mini-2024-07-18', 'openai:gpt-4o-mini-audio-preview', 'openai:gpt-4o-mini-audio-preview-2024-12-17', 'openai:gpt-5', 'openai:gpt-5-2025-08-07', 'openai:gpt-5-chat-latest', 'openai:gpt-5-codex', 'openai:gpt-5-mini', 'openai:gpt-5-mini-2025-08-07', 'openai:gpt-5-nano', 'openai:gpt-5-nano-2025-08-07', 'openai:gpt-5-pro', 'openai:gpt-5-pro-2025-10-06', 'openai:gpt-5.1', 'openai:gpt-5.1-2025-11-13', 'openai:gpt-5.1-chat-latest', 'openai:gpt-5.1-codex', 'openai:gpt-5.1-codex-max', 'openai:gpt-5.2', 'openai:gpt-5.2-2025-12-11', 'openai:gpt-5.2-chat-latest', 'openai:gpt-5.2-pro', 'openai:gpt-5.2-pro-2025-12-11', 'openai:gpt-5.3-chat-latest', 'openai:gpt-5.4', 'openai:gpt-5.4-mini', 'openai:gpt-5.4-mini-2026-03-17', 'openai:gpt-5.4-nano', 'openai:gpt-5.4-nano-2026-03-17', 'openai:gpt-5.6-luna', 'openai:gpt-5.6-sol', 'openai:gpt-5.6-terra', 'openai:o1', 'openai:o1-2024-12-17', 'openai:o1-pro', 'openai:o1-pro-2025-03-19', 'openai:o3', 'openai:o3-2025-04-16', 'openai:o3-deep-research', 'openai:o3-deep-research-2025-06-26', 'openai:o3-mini', 'openai:o3-mini-2025-01-31', 'openai:o3-pro', 'openai:o3-pro-2025-06-10', 'openai:o4-mini', 'openai:o4-mini-2025-04-16', 'openai:o4-mini-deep-research', 'openai:o4-mini-deep-research-2025-06-26', 'test', 'xai:grok-3', 'xai:grok-3-fast', 'xai:grok-3-fast-latest', 'xai:grok-3-latest', 'xai:grok-3-mini', 'xai:grok-3-mini-fast', 'xai:grok-3-mini-fast-latest', 'xai:grok-4', 'xai:grok-4-0709', 'xai:grok-4-1-fast', 'xai:grok-4-1-fast-non-reasoning', 'xai:grok-4-1-fast-non-reasoning-latest', 'xai:grok-4-1-fast-reasoning', 'xai:grok-4-1-fast-reasoning-latest', 'xai:grok-4-fast', 'xai:grok-4-fast-non-reasoning', 'xai:grok-4-fast-non-reasoning-latest', 'xai:grok-4-fast-reasoning', 'xai:grok-4-fast-reasoning-latest', 'xai:grok-4-latest', 'xai:grok-4.20', 'xai:grok-4.20-0309', 'xai:grok-4.20-0309-non-reasoning', 'xai:grok-4.20-0309-reasoning', 'xai:grok-4.20-multi-agent', 'xai:grok-4.20-multi-agent-0309', 'xai:grok-4.20-multi-agent-latest', 'xai:grok-4.20-non-reasoning', 'xai:grok-4.20-non-reasoning-latest', 'xai:grok-4.20-reasoning-latest', 'xai:grok-4.3', 'xai:grok-4.3-latest', 'xai:grok-4.5', 'xai:grok-4.5-latest', 'xai:grok-code-fast-1', 'zai:autoglm-phone-multilingual', 'zai:glm-4-32b-0414-128k', 'zai:glm-4.5', 'zai:glm-4.5-air', 'zai:glm-4.5-airx', 'zai:glm-4.5-flash', 'zai:glm-4.5-x', 'zai:glm-4.5v', 'zai:glm-4.6', 'zai:glm-4.6v', 'zai:glm-4.6v-flash', 'zai:glm-4.6v-flashx', 'zai:glm-4.7', 'zai:glm-4.7-flash', 'zai:glm-4.7-flashx', 'zai:glm-5', 'zai:glm-5-turbo', 'zai:glm-5.1', 'zai:glm-5.2', 'zai:glm-5v-turbo'])` ### ALLOW\_MODEL\_REQUESTS @@ -2449,7 +2524,7 @@ Get the provider base URL. Latest Bedrock models. -**Default:** `Literal['amazon.titan-tg1-large', 'amazon.titan-text-lite-v1', 'amazon.titan-text-express-v1', 'us.amazon.nova-2-lite-v1:0', 'us.amazon.nova-pro-v1:0', 'us.amazon.nova-lite-v1:0', 'us.amazon.nova-micro-v1:0', 'anthropic.claude-3-5-sonnet-20241022-v2:0', 'us.anthropic.claude-3-5-sonnet-20241022-v2:0', 'anthropic.claude-3-5-haiku-20241022-v1:0', 'us.anthropic.claude-3-5-haiku-20241022-v1:0', 'anthropic.claude-instant-v1', 'anthropic.claude-v2:1', 'anthropic.claude-v2', 'anthropic.claude-3-sonnet-20240229-v1:0', 'us.anthropic.claude-3-sonnet-20240229-v1:0', 'anthropic.claude-3-haiku-20240307-v1:0', 'us.anthropic.claude-3-haiku-20240307-v1:0', 'anthropic.claude-3-opus-20240229-v1:0', 'us.anthropic.claude-3-opus-20240229-v1:0', 'anthropic.claude-3-5-sonnet-20240620-v1:0', 'us.anthropic.claude-3-5-sonnet-20240620-v1:0', 'anthropic.claude-3-7-sonnet-20250219-v1:0', 'us.anthropic.claude-3-7-sonnet-20250219-v1:0', 'anthropic.claude-opus-4-20250514-v1:0', 'us.anthropic.claude-opus-4-20250514-v1:0', 'global.anthropic.claude-opus-4-5-20251101-v1:0', 'anthropic.claude-sonnet-4-20250514-v1:0', 'us.anthropic.claude-sonnet-4-20250514-v1:0', 'eu.anthropic.claude-sonnet-4-20250514-v1:0', 'anthropic.claude-sonnet-4-5-20250929-v1:0', 'us.anthropic.claude-sonnet-4-5-20250929-v1:0', 'eu.anthropic.claude-sonnet-4-5-20250929-v1:0', 'anthropic.claude-sonnet-4-6', 'us.anthropic.claude-sonnet-4-6', 'eu.anthropic.claude-sonnet-4-6', 'anthropic.claude-haiku-4-5-20251001-v1:0', 'us.anthropic.claude-haiku-4-5-20251001-v1:0', 'eu.anthropic.claude-haiku-4-5-20251001-v1:0', 'cohere.command-text-v14', 'cohere.command-r-v1:0', 'cohere.command-r-plus-v1:0', 'cohere.command-light-text-v14', 'meta.llama3-8b-instruct-v1:0', 'meta.llama3-70b-instruct-v1:0', 'meta.llama3-1-8b-instruct-v1:0', 'us.meta.llama3-1-8b-instruct-v1:0', 'meta.llama3-1-70b-instruct-v1:0', 'us.meta.llama3-1-70b-instruct-v1:0', 'meta.llama3-1-405b-instruct-v1:0', 'us.meta.llama3-2-11b-instruct-v1:0', 'us.meta.llama3-2-90b-instruct-v1:0', 'us.meta.llama3-2-1b-instruct-v1:0', 'us.meta.llama3-2-3b-instruct-v1:0', 'us.meta.llama3-3-70b-instruct-v1:0', 'mistral.mistral-7b-instruct-v0:2', 'mistral.mixtral-8x7b-instruct-v0:1', 'mistral.mistral-large-2402-v1:0', 'mistral.mistral-large-2407-v1:0']` +**Default:** `Literal['amazon.titan-tg1-large', 'amazon.titan-text-lite-v1', 'amazon.titan-text-express-v1', 'us.amazon.nova-2-lite-v1:0', 'us.amazon.nova-pro-v1:0', 'us.amazon.nova-lite-v1:0', 'us.amazon.nova-micro-v1:0', 'anthropic.claude-3-5-sonnet-20241022-v2:0', 'us.anthropic.claude-3-5-sonnet-20241022-v2:0', 'anthropic.claude-3-5-haiku-20241022-v1:0', 'us.anthropic.claude-3-5-haiku-20241022-v1:0', 'anthropic.claude-instant-v1', 'anthropic.claude-v2:1', 'anthropic.claude-v2', 'anthropic.claude-3-sonnet-20240229-v1:0', 'us.anthropic.claude-3-sonnet-20240229-v1:0', 'anthropic.claude-3-haiku-20240307-v1:0', 'us.anthropic.claude-3-haiku-20240307-v1:0', 'anthropic.claude-3-opus-20240229-v1:0', 'us.anthropic.claude-3-opus-20240229-v1:0', 'anthropic.claude-3-5-sonnet-20240620-v1:0', 'us.anthropic.claude-3-5-sonnet-20240620-v1:0', 'anthropic.claude-3-7-sonnet-20250219-v1:0', 'us.anthropic.claude-3-7-sonnet-20250219-v1:0', 'anthropic.claude-opus-4-20250514-v1:0', 'us.anthropic.claude-opus-4-20250514-v1:0', 'global.anthropic.claude-opus-4-5-20251101-v1:0', 'anthropic.claude-sonnet-4-20250514-v1:0', 'us.anthropic.claude-sonnet-4-20250514-v1:0', 'eu.anthropic.claude-sonnet-4-20250514-v1:0', 'anthropic.claude-sonnet-4-5-20250929-v1:0', 'us.anthropic.claude-sonnet-4-5-20250929-v1:0', 'eu.anthropic.claude-sonnet-4-5-20250929-v1:0', 'anthropic.claude-sonnet-4-6', 'us.anthropic.claude-sonnet-4-6', 'eu.anthropic.claude-sonnet-4-6', 'anthropic.claude-haiku-4-5-20251001-v1:0', 'us.anthropic.claude-haiku-4-5-20251001-v1:0', 'eu.anthropic.claude-haiku-4-5-20251001-v1:0', 'cohere.command-text-v14', 'cohere.command-r-v1:0', 'cohere.command-r-plus-v1:0', 'cohere.command-light-text-v14', 'meta.llama3-8b-instruct-v1:0', 'meta.llama3-70b-instruct-v1:0', 'meta.llama3-1-8b-instruct-v1:0', 'us.meta.llama3-1-8b-instruct-v1:0', 'meta.llama3-1-70b-instruct-v1:0', 'us.meta.llama3-1-70b-instruct-v1:0', 'meta.llama3-1-405b-instruct-v1:0', 'us.meta.llama3-2-11b-instruct-v1:0', 'us.meta.llama3-2-90b-instruct-v1:0', 'us.meta.llama3-2-1b-instruct-v1:0', 'us.meta.llama3-2-3b-instruct-v1:0', 'us.meta.llama3-3-70b-instruct-v1:0', 'mistral.mistral-7b-instruct-v0:2', 'mistral.mixtral-8x7b-instruct-v0:1', 'mistral.mistral-large-2402-v1:0', 'mistral.mistral-large-2407-v1:0', 'us.anthropic.claude-opus-4-1-20250805-v1:0', 'us.anthropic.claude-opus-4-5-20251101-v1:0', 'us.anthropic.claude-opus-4-6-v1', 'global.anthropic.claude-opus-4-6-v1', 'us.anthropic.claude-opus-4-7', 'global.anthropic.claude-opus-4-7', 'us.anthropic.claude-opus-4-8', 'global.anthropic.claude-opus-4-8', 'us.anthropic.claude-sonnet-5', 'global.anthropic.claude-sonnet-5', 'us.anthropic.claude-fable-5', 'global.anthropic.claude-fable-5', 'us.amazon.nova-premier-v1:0', 'global.amazon.nova-2-lite-v1:0', 'us.meta.llama4-maverick-17b-instruct-v1:0', 'us.meta.llama4-scout-17b-instruct-v1:0', 'mistral.mistral-small-2402-v1:0', 'mistral.mistral-large-3-675b-instruct', 'mistral.ministral-3-3b-instruct', 'mistral.ministral-3-8b-instruct', 'mistral.ministral-3-14b-instruct', 'mistral.magistral-small-2509', 'mistral.devstral-2-123b', 'mistral.pixtral-large-2502-v1:0', 'us.mistral.pixtral-large-2502-v1:0', 'deepseek.r1-v1:0', 'deepseek.v3.2', 'qwen.qwen3-32b-v1:0', 'qwen.qwen3-coder-30b-a3b-v1:0', 'qwen.qwen3-coder-next', 'qwen.qwen3-next-80b-a3b', 'qwen.qwen3-vl-235b-a22b', 'google.gemma-3-4b-it', 'google.gemma-3-12b-it', 'google.gemma-3-27b-it', 'minimax.minimax-m2', 'minimax.minimax-m2.1', 'minimax.minimax-m2.5', 'nvidia.nemotron-nano-9b-v2', 'nvidia.nemotron-nano-12b-v2', 'nvidia.nemotron-nano-3-30b', 'nvidia.nemotron-super-3-120b', 'us.writer.palmyra-x4-v1:0', 'us.writer.palmyra-x5-v1:0', 'zai.glm-4.7', 'zai.glm-4.7-flash', 'zai.glm-5', 'moonshot.kimi-k2-thinking', 'moonshotai.kimi-k2.5']` ### BedrockModelName @@ -2756,6 +2831,8 @@ Try each model in sequence until one succeeds. In case of failure, raise a FallbackExceptionGroup with all exceptions. +If a previous response set `state='suspended'`, the request is routed directly to the pinned continuation model, bypassing the fallback chain. If the pinned model raises a fallback-eligible error during continuation, the messages are rewound (stripping the suspended response and trailing continuation request) and the normal fallback chain is tried. + ###### Returns [`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse) @@ -2775,10 +2852,30 @@ def request_stream( Try each model in sequence until one succeeds. +If a previous response set `state='suspended'`, the request is routed directly to the pinned continuation model, bypassing the fallback chain. If the pinned model raises a fallback-eligible error while opening the stream, the messages are rewound and the normal fallback chain is tried. Mid-stream failures still propagate. + ###### Returns [`AsyncGenerator`](https://docs.python.org/3/library/typing.html#typing.AsyncGenerator)\[`StreamedResponse`\] +##### cancel\_suspended\_response + +`@async` + +```python +def cancel_suspended_response(response: ModelResponse) -> None +``` + +Cancel a suspended continuation on the underlying model holding the server-side job. + +When the response carries a continuation pin, resolve that model and delegate to it. Resolve the pin directly from metadata rather than via `_get_continuation_model`: the cancel path is driven by `_ContinuationStreamedResponse.get()`, whose `state` is already `'interrupted'`/`'incomplete'`/`'complete'` (never `'suspended'`) by the time cancellation unwinds, so gating on `state == 'suspended'` here would never find the pin. + +When no pin resolves, the response can still hold a live server-side job: the pin is only stamped when a segment _ends_ suspended, so a streamed background job cancelled during its first segment (e.g. OpenAI background mode, marked by `provider_details['background']` + `provider_response_id`) has no pin yet. Best-effort delegate to every inner model so the job is torn down rather than leaked. This is safe because each model's own cancel guard is strict (OpenAI only acts on its own `background` marker with a matching `provider_name`; others no-op), and a raising model doesn't stop the rest. + +###### Returns + +[`None`](https://docs.python.org/3/library/constants.html#None) + ### ExceptionHandler A sync or async callable that decides whether an exception should trigger fallback. @@ -3488,7 +3585,7 @@ Production Groq models from [https://console.groq.com/docs/models#production-mod Preview Groq models from [https://console.groq.com/docs/models#preview-models](https://console.groq.com/docs/models#preview-models). -**Default:** `Literal['meta-llama/llama-4-maverick-17b-128e-instruct', 'meta-llama/llama-4-scout-17b-16e-instruct', 'meta-llama/llama-prompt-guard-2-22m', 'meta-llama/llama-prompt-guard-2-86m', 'moonshotai/kimi-k2-instruct-0905', 'openai/gpt-oss-safeguard-20b', 'playai-tts', 'playai-tts-arabic', 'qwen/qwen-3-32b']` +**Default:** `Literal['meta-llama/llama-4-maverick-17b-128e-instruct', 'meta-llama/llama-4-scout-17b-16e-instruct', 'meta-llama/llama-prompt-guard-2-22m', 'meta-llama/llama-prompt-guard-2-86m', 'openai/gpt-oss-safeguard-20b', 'playai-tts', 'playai-tts-arabic', 'qwen/qwen3-32b']` ### GroqModelName @@ -4116,6 +4213,16 @@ See [OpenAI's built-in tools](https://platform.openai.com/docs/guides/tools?api- **Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[`FileSearchToolParam` | `WebSearchToolParam` | `ComputerToolParam`\] +##### openai\_reasoning\_mode + +The reasoning mode to use, for models that support it (currently the GPT-5.6 family). + +`standard` is the default. `pro` performs more model work to improve reliability on difficult tasks, at the cost of higher latency and token usage. Reasoning mode is independent of [`openai_reasoning_effort`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIChatModelSettings.openai_reasoning_effort), and the unified [`thinking`](/docs/ai/api/pydantic-ai/settings/#pydantic_ai.settings.ModelSettings.thinking) setting only influences the effort, never the mode. This setting is ignored when the resolved model profile does not support reasoning mode ([`OpenAIModelProfile.openai_responses_supports_reasoning_mode`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.openai.OpenAIModelProfile.openai_responses_supports_reasoning_mode)). + +See [OpenAI's reasoning mode documentation](https://developers.openai.com/api/docs/guides/reasoning#reasoning-mode) for more details. + +**Type:** [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\['standard', 'pro'\] + ##### openai\_reasoning\_summary A summary of the reasoning performed by the model. @@ -4225,6 +4332,14 @@ The [`OpenAICompaction`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.O **Type:** [`list`](https://docs.python.org/3/glossary.html#term-list)\[`ContextManagement`\] +##### openai\_background + +Enable background mode for long-running requests. + +When enabled, this setting passes `background=True` to the Responses API and opts into automatic polling for completion. If the response is still pending (`'queued'` or `'in_progress'`), the agent automatically polls for completion using `retrieve()`. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) + ### OpenAIChatModel **Bases:** `Model[AsyncOpenAI]` @@ -4363,6 +4478,22 @@ The model profile to use. Defaults to a profile picked by the provider based on Default model settings for this model instance. +##### cancel\_suspended\_response + +`@async` + +```python +def cancel_suspended_response(response: ModelResponse) -> None +``` + +Cancel a suspended background response by cancelling its server-side job. + +`responses.cancel` only applies to background responses; calling it on an ordinary (foreground) response returns a 400. The `provider_details['background']` marker is stamped from the API's own `response.background` field (see `_process_response` and `OpenAIResponsesStreamedResponse._track_background`), so it explicitly and reliably distinguishes a cancellable background job from a normal streamed response that happens to be interrupted by `cancel()` -- no need to infer background mode from the `continuation_delay` poll interval. + +###### Returns + +[`None`](https://docs.python.org/3/library/constants.html#None) + ##### supported\_native\_tools `@classmethod` @@ -4557,7 +4688,7 @@ Stateless-mode only. Custom callable that decides whether to compact based on th Models that are deprecated or don't exist but are still present in the OpenAI SDK's type definitions. -**Type:** [`frozenset`](https://docs.python.org/3/library/stdtypes.html#frozenset)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `frozenset({'chatgpt-4o-latest', 'codex-mini-latest', 'gpt-4-0125-preview', 'gpt-4-1106-preview', 'gpt-4-turbo-preview', 'gpt-4-32k', 'gpt-4-32k-0314', 'gpt-4-32k-0613', 'gpt-4-vision-preview', 'gpt-4o-audio-preview-2024-10-01', 'gpt-5.1-mini', 'o1-mini', 'o1-mini-2024-09-12', 'o1-preview', 'o1-preview-2024-09-12'})` +**Type:** [`frozenset`](https://docs.python.org/3/library/stdtypes.html#frozenset)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `frozenset({'chatgpt-4o-latest', 'codex-mini-latest', 'gpt-3.5-turbo-0613', 'gpt-3.5-turbo-16k-0613', 'gpt-4-0125-preview', 'gpt-4-1106-preview', 'gpt-4-turbo-preview', 'gpt-4-32k', 'gpt-4-32k-0314', 'gpt-4-32k-0613', 'gpt-4-vision-preview', 'gpt-4o-audio-preview-2024-10-01', 'gpt-5.1-mini', 'o1-mini', 'o1-mini-2024-09-12', 'o1-preview', 'o1-preview-2024-09-12'})` ### OpenAIModelName @@ -5341,7 +5472,9 @@ Get the timestamp of the response. Possible xAI model names. -**Default:** `str | ChatModel` +`grok-4.5`/`grok-4.5-latest` are bridged with a local `Literal` because `xai_sdk`'s `ChatModel` doesn't list them yet (as of 1.17.0). Drop the literal once the `xai-sdk` floor is bumped past the release that adds them to `ChatModel`. + +**Default:** `str | ChatModel | Literal['grok-4.5', 'grok-4.5-latest']` --- @@ -5374,7 +5507,9 @@ print(result.output) ##### end\_strategy -The strategy for handling function tool calls the model requests alongside an output tool. +The strategy for handling function tool calls the model requests alongside a result that ends the run. + +That result usually comes from an output tool call, but with `NativeOutput`, `PromptedOutput`, or image output it comes from the structured text or image the model returns in the same response. Plain, unstructured text (`str` or `TextOutput`) is not treated as such a result: since the model isn't told its text is final, `end_strategy` never skips tools on its account, even under `'early'`. Defaults to `'graceful'`. See [`EndStrategy`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.EndStrategy) for the behavior of each strategy. @@ -7250,7 +7385,9 @@ def run_stream_sync( Run the agent with a user prompt in sync streaming mode. -This is a convenience method that wraps [`run_stream()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run_stream) with `loop.run_until_complete(...)`. You therefore can't use this method inside async code or if there's an active event loop. +This is a convenience method that wraps [`run_stream()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run_stream), running all of the agent's async work on the caller's event loop while keeping context-manager and iterator lifecycles in stable tasks. You therefore can't use this method inside async code or if there's an active event loop. + +The returned [`StreamedRunResultSync`](/docs/ai/api/pydantic-ai/result/#pydantic_ai.result.StreamedRunResultSync) is a synchronous context manager and should be used and closed on the thread where it was created. Use a `with` block so the stream is cleaned up when you're done. This method builds an internal agent graph (using system prompts, tools and output schemas) and then runs the graph until the model produces output matching the `output_type`, for example text or structured data. At this point, a streaming run result object is yielded from which you can stream the output as it comes in, and -- once this output has completed streaming -- get the complete output, message history, and usage. @@ -7264,9 +7401,9 @@ from pydantic_ai import Agent agent = Agent('openai:gpt-5.2') def main(): - response = agent.run_stream_sync('What is the capital of the UK?') - print(response.get_output()) - #> The capital of the UK is London. + with agent.run_stream_sync('What is the capital of the UK?') as response: + print(response.get_output()) + #> The capital of the UK is London. ``` ###### Returns @@ -7392,6 +7529,8 @@ Run the agent with a user prompt in async mode and stream events from the run. This is a convenience method that wraps [`self.run`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run) and uses the `event_stream_handler` kwarg to get a stream of events from the run. +The background run starts on the first iteration of the event stream, not on entering the context manager, so entering and exiting without iterating never calls the model. + Must be used as an async context manager so the background run task is deterministically cleaned up when the consumer stops iterating early. Example: @@ -7817,6 +7956,22 @@ For per-agent configuration, use the [`ThreadExecutor`](/docs/ai/api/pydantic-ai The executor to use for running sync functions. +##### using\_sleep + +`@staticmethod` + +```python +def using_sleep(sleep_func: _agent_graph.AgentGraphSleepFunc) -> Generator[None] +``` + +Use a custom async sleep function for agent-graph delays during the context. + +By default the agent graph uses `asyncio.sleep` when it needs to wait during a run (e.g. between polls of a suspended/background model response). Durable execution frameworks (Temporal, Prefect, DBOS, ...) register their own durable sleep here so delays survive workflow replays and don't waste activity time. + +###### Returns + +[`Generator`](https://docs.python.org/3/library/typing.html#typing.Generator)\[[`None`](https://docs.python.org/3/library/constants.html#None)\] + ##### is\_model\_request\_node `@staticmethod` @@ -7900,6 +8055,7 @@ def to_cli( message_history: Sequence[_messages.ModelMessage] | None = None, model_settings: ModelSettings | None = None, usage_limits: _usage.UsageLimits | None = None, + model: models.Model | models.KnownModelName | str | None = None, ) -> None ``` @@ -7944,6 +8100,10 @@ Optional settings to use for this model's request. Optional limits on model request count or token usage. +**`model`** : [`models.Model`](/docs/ai/api/models/base/#pydantic_ai.models.Model) | [`models.KnownModelName`](/docs/ai/api/models/base/#pydantic_ai.models.KnownModelName) | [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +Optional model to use for the agent run. + ##### to\_cli\_sync ```python @@ -7953,6 +8113,7 @@ def to_cli_sync( message_history: Sequence[_messages.ModelMessage] | None = None, model_settings: ModelSettings | None = None, usage_limits: _usage.UsageLimits | None = None, + model: models.Model | models.KnownModelName | str | None = None, ) -> None ``` @@ -7994,6 +8155,10 @@ Optional settings to use for this model's request. Optional limits on model request count or token usage. +**`model`** : [`models.Model`](/docs/ai/api/models/base/#pydantic_ai.models.Model) | [`models.KnownModelName`](/docs/ai/api/models/base/#pydantic_ai.models.KnownModelName) | [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +Optional model to use for the agent run. + ### WrapperAgent **Bases:** `AbstractAgent[AgentDepsT, OutputDataT]` @@ -8798,6 +8963,8 @@ Note If you call `run`, `run_sync`, or `run_stream` more than once within a single `capture_run_messages` context, `messages` will represent the messages exchanged during the first call only. +Contexts can be nested: each `capture_run_messages` context captures the runs for which it is the innermost active context. A run started inside a nested context is captured by that nested context, not by any enclosing one, so wrapping a nested agent run (e.g. inside a tool) in its own `capture_run_messages` lets you inspect that inner run's messages independently. + If a run is interrupted by an exception or cancellation while streaming a response or executing tool calls, the partial [`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse) or [`ModelRequest`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelRequest) is still captured here with `state='interrupted'`, so consumers can detect and inspect partial state. #### Returns @@ -8806,12 +8973,16 @@ If a run is interrupted by an exception or cancellation while streaming a respon ### EndStrategy -How to handle tool calls a model requests alongside an output tool that produces a final result. +How to handle function tool calls a model requests alongside a result that ends the run. + +The final result usually comes from an output tool call, but with [`NativeOutput`](/docs/ai/api/pydantic-ai/output/#pydantic_ai.output.NativeOutput), [`PromptedOutput`](/docs/ai/api/pydantic-ai/output/#pydantic_ai.output.PromptedOutput), or image output it comes from the text or image the model returns in the same response. -- `'early'`: Output tools run in the order the model emitted them and the run ends at the first one that succeeds; function tools are not executed. If every output tool fails, function tools run so the model can correct on the next round. +- `'early'`: Output tools run in the order the model emitted them and the run ends at the first one that succeeds; function tools are not executed. If every output tool fails, function tools run so the model can correct on the next round. Likewise, if the response contains a valid structured output (`NativeOutput`/`PromptedOutput` text, or an image) alongside function tool calls, that output ends the run and the function tools are skipped. Plain, unstructured text output (`str` or `TextOutput`) does _not_ skip tools this way -- the model isn't told its text is final, so its preamble shouldn't silently cancel a tool call; the function tools run and the run continues. - `'graceful'` (default): Tools run in the order the model emitted them -- function tools that precede an output tool complete before it. Output tools run in order and the first success wins; subsequent output tools are skipped (their side effects don't run). If a function tool raises [`ModelRetry`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.ModelRetry), the output result is suppressed and the retry is surfaced to the model instead. - `'exhaustive'`: Every tool runs (in parallel by default); the first valid output by emission order becomes the final result. As with `'graceful'`, a function tool's [`ModelRetry`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.ModelRetry) suppresses the output result. Use `sequential=True` on a tool (including via [`ToolOutput`](/docs/ai/api/pydantic-ai/output/#pydantic_ai.output.ToolOutput)) to make it a barrier that doesn't overlap with others. +Under `'graceful'` and `'exhaustive'`, a structured output (`NativeOutput`/`PromptedOutput` text, or an image) returned alongside function tool calls does _not_ end the run early: the function tools run and the run continues, so their results can inform the model's eventual output. Only `'early'` skips them. + The default changed from `'early'` to `'graceful'` in v2. Set `end_strategy='early'` to keep the v1 behavior where the run ends the instant an output tool succeeds. **Default:** `Literal['early', 'graceful', 'exhaustive']` @@ -9372,7 +9543,7 @@ Can be a model name string, `Model` instance, or a callable taking `RunContext` ##### allowed\_x\_handles -If provided, only posts from these X handles will be included (max 10). +If provided, only posts from these X handles will be included (max 20). Honored by the native X search tool, whether used directly on an xAI model or via the `fallback_model` subagent. @@ -9380,7 +9551,7 @@ Honored by the native X search tool, whether used directly on an xAI model or vi ##### excluded\_x\_handles -If provided, posts from these X handles will be excluded (max 10). +If provided, posts from these X handles will be excluded (max 20). Honored by the native X search tool, whether used directly on an xAI model or via the `fallback_model` subagent. @@ -11058,6 +11229,14 @@ A sync/async function which takes a run context and returns a capability. **Type:** [`TypeAlias`](https://docs.python.org/3/library/typing.html#typing.TypeAlias) **Default:** `Callable[[RunContext[AgentDepsT]], AbstractCapability[AgentDepsT] | None | Awaitable[AbstractCapability[AgentDepsT] | None]]` +### HistoryProcessor + +A function that processes a list of model messages and returns a list of model messages. + +Can optionally accept a `RunContext` as a parameter. + +**Default:** `_HistoryProcessorSync | _HistoryProcessorAsync | _HistoryProcessorSyncWithCtx[DepsT] | _HistoryProcessorAsyncWithCtx[DepsT]` + ### AgentNode Type alias for an agent graph node (`UserPromptNode`, `ModelRequestNode`, `CallToolsNode`). @@ -11118,6 +11297,12 @@ Type alias for validated tool arguments. **Type:** [`TypeAlias`](https://docs.python.org/3/library/typing.html#typing.TypeAlias) **Default:** `dict[str, Any]` +### WrapToolValidateHandler + +Handler type for [`wrap_tool_validate`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.AbstractCapability.wrap_tool_validate). + +**Type:** [`TypeAlias`](https://docs.python.org/3/library/typing.html#typing.TypeAlias) **Default:** `Callable[[RawToolArgs], Awaitable[ValidatedToolArgs]]` + ### AgentCapability A capability or a [`CapabilityFunc`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.CapabilityFunc) that takes a run context and returns one. @@ -11126,12 +11311,6 @@ Use as the item type for `Agent(capabilities=[...])` and `agent.run(capabilities **Type:** [`TypeAlias`](https://docs.python.org/3/library/typing.html#typing.TypeAlias) **Default:** `AbstractCapability[AgentDepsT] | CapabilityFunc[AgentDepsT]` -### WrapToolValidateHandler - -Handler type for [`wrap_tool_validate`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.AbstractCapability.wrap_tool_validate). - -**Type:** [`TypeAlias`](https://docs.python.org/3/library/typing.html#typing.TypeAlias) **Default:** `Callable[[RawToolArgs], Awaitable[ValidatedToolArgs]]` - ### WrapToolExecuteHandler Handler type for [`wrap_tool_execute`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.AbstractCapability.wrap_tool_execute). @@ -12217,11 +12396,11 @@ Initialize the ConcurrencyLimiter. **`max_running`** : [`int`](https://docs.python.org/3/library/functions.html#int) -Maximum number of concurrent operations. +Maximum number of concurrent operations. Must be >= 1. **`max_queued`** : [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` -Maximum queue depth before raising ConcurrencyLimitExceeded. +Maximum queue depth before raising ConcurrencyLimitExceeded. Must be >= 0. **`name`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` @@ -12231,6 +12410,10 @@ Optional name for this limiter, used for observability when sharing a limiter ac OpenTelemetry tracer for span creation. +###### Raises + +- `UserError` -- If `max_running` is less than 1, or `max_queued` is less than 0. + ##### from\_limit `@classmethod` @@ -12305,11 +12488,11 @@ Configuration for concurrency limiting with optional backpressure. **`max_running`** : [`int`](https://docs.python.org/3/library/functions.html#int) -Maximum number of concurrent operations allowed. +Maximum number of concurrent operations allowed. Must be >= 1. **`max_queued`** : [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` -Maximum number of operations waiting in the queue. If None, the queue is unlimited. If exceeded, raises `ConcurrencyLimitExceeded`. +Maximum number of operations waiting in the queue. Must be >= 0. If None, the queue is unlimited. If exceeded, raises `ConcurrencyLimitExceeded`. ### AnyConcurrencyLimit @@ -12344,9 +12527,11 @@ These methods are thin wrappers around [`Model`](/docs/ai/api/models/base/#pydan ### StreamedResponseSync -Synchronous wrapper to async streaming responses by running the async producer in a background thread and providing a synchronous iterator. +Synchronous wrapper for an async streaming response, running the whole stream on the caller's event loop. + +The stream uses `SyncStreamBridge` to keep context-manager and iterator lifecycles in stable tasks. Exiting the `with` block cancels the underlying request promptly and closes the connection instead of waiting for the whole response to arrive. -This class must be used as a context manager with the `with` statement. +This class must be used as a context manager with the `with` statement. The synchronous stream is created when the `with` block is entered and must be used and closed on that thread. #### Attributes @@ -12612,7 +12797,7 @@ def model_request_stream_sync( Make a streamed synchronous request to a model. -This is the synchronous version of [`model_request_stream`](/docs/ai/api/pydantic-ai/direct/#pydantic_ai.direct.model_request_stream). It uses threading to run the asynchronous stream in the background while providing a synchronous iterator interface. +This is the synchronous version of [`model_request_stream`](/docs/ai/api/pydantic-ai/direct/#pydantic_ai.direct.model_request_stream). It drives the asynchronous stream on the caller's event loop while providing a synchronous iterator interface. The returned context manager must be used and closed on the thread where the synchronous stream is created. model\_request\_stream\_sync\_example.py @@ -12687,7 +12872,7 @@ Temporal Workflow base class that provides `__pydantic_ai_agents__` for direct a The [`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext) subclass to use to serialize and deserialize the run context for use inside a Temporal activity. -By default, only the `deps`, `run_id`, `metadata`, `retries`, `tool_call_id`, `tool_name`, `tool_call_approved`, `tool_call_metadata`, `retry`, `max_retries`, `run_step`, `usage`, `partial_output`, `loaded_capability_ids`, `discovered_tool_names`, and `capability_loaded` attributes will be available. +By default, only the `deps`, `run_id`, `metadata`, `retries`, `tool_call_id`, `tool_name`, `tool_call_approved`, `tool_call_metadata`, `retry`, `max_retries`, `run_step`, `usage`, `usage_limits`, `partial_output`, `loaded_capability_ids`, `discovered_tool_names`, and `capability_loaded` attributes will be available. The `capabilities` registry is intentionally excluded: it holds live capability objects (toolsets, hooks, callables) that aren't serializable across the activity boundary, like `tool_manager`. As a result `available_capability_ids` (which reads `capabilities`) is unavailable inside an activity, while `available_tool_names` still works via its `discovered_tool_names` fallback. To make another attribute available, create a `TemporalRunContext` subclass with a custom `serialize_run_context` class method that returns a dictionary that includes the attribute and pass it to [`TemporalAgent`](/docs/ai/api/pydantic-ai/durable_exec/#pydantic_ai.durable_exec.temporal.TemporalAgent). @@ -14683,6 +14868,22 @@ Make a model request, wrapped as a Prefect task when in a flow. [`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse) +##### cancel\_suspended\_response + +`@async` + +```python +def cancel_suspended_response(response: ModelResponse) -> None +``` + +Cancel a server-side suspended/background response, wrapped as a Prefect task. + +The teardown performs a raw HTTP call to the provider, so it runs as a task (durable, retried) rather than inline in the flow. + +###### Returns + +[`None`](https://docs.python.org/3/library/constants.html#None) + ##### request\_stream `@async` @@ -18002,6 +18203,14 @@ The error message. **Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) **Default:** `message` +### SuspendedResponseExpired + +**Bases:** [`AgentRunError`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.AgentRunError) + +Raised when resuming a suspended response whose server-side job is no longer available. + +Suspended/background jobs are only resumable within the provider's retention window (e.g. ~10 minutes for OpenAI background mode). Resuming a persisted suspended response after that window raises this instead of an opaque provider HTTP error; start a new run from the preceding messages to retry from scratch. + ### UsageLimitExceeded **Bases:** [`AgentRunError`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.AgentRunError) @@ -19804,6 +20013,8 @@ Supported by: - `GoogleModel`: `VideoUrl.vendor_metadata` is used as `video_metadata`: [https://ai.google.dev/gemini-api/docs/video-understanding#customize-video-processing](https://ai.google.dev/gemini-api/docs/video-understanding#customize-video-processing) - `OpenAIChatModel`, `OpenAIResponsesModel`: `ImageUrl.vendor_metadata['detail']` is used as `detail` setting for images - `XaiModel`: `ImageUrl.vendor_metadata['detail']` is used as `detail` setting for images +- `GroqModel`: `ImageUrl.vendor_metadata['detail']` is used as `detail` setting for images +- `MistralModel`: `ImageUrl.vendor_metadata['detail']` is used as `detail` setting for images **Type:** [`dict`](https://docs.python.org/3/reference/expressions.html#dict)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` @@ -20002,6 +20213,8 @@ Supported by: - `GoogleModel`: `BinaryContent.vendor_metadata` is used as `video_metadata`: [https://ai.google.dev/gemini-api/docs/video-understanding#customize-video-processing](https://ai.google.dev/gemini-api/docs/video-understanding#customize-video-processing) - `OpenAIChatModel`, `OpenAIResponsesModel`: `BinaryContent.vendor_metadata['detail']` is used as `detail` setting for images - `XaiModel`: `BinaryContent.vendor_metadata['detail']` is used as `detail` setting for images +- `GroqModel`: `BinaryContent.vendor_metadata['detail']` is used as `detail` setting for images +- `MistralModel`: `BinaryContent.vendor_metadata['detail']` is used as `detail` setting for images **Type:** [`dict`](https://docs.python.org/3/reference/expressions.html#dict)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` @@ -20351,8 +20564,9 @@ The outcome of the tool call. - `'success'`: The tool executed successfully. - `'failed'`: The tool raised an error during execution. - `'denied'`: The tool call was denied by the approval mechanism. +- `'interrupted'`: The tool call did not produce a result because the run was interrupted (e.g. a cancelled stream or a crash mid-execution); synthesized during message-history repair. Unlike `'failed'`, `'interrupted'` is not mapped to any provider native-error channel -- the result's content string carries the interruption wording. -**Type:** [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\['success', 'failed', 'denied'\] **Default:** `'success'` +**Type:** [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\['success', 'failed', 'denied', 'interrupted'\] **Default:** `'success'` ##### files @@ -20414,6 +20628,20 @@ This excludes multimodal files - use `.files` to get those separately. Gemini su [`dict`](https://docs.python.org/3/reference/expressions.html#dict)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] +##### structured\_content + +```python +def structured_content() -> dict[str, Any] | list[Any] | None +``` + +Return `content` as structured JSON data (a `dict` or `list`), or `None` if it has none. + +A JSON string is parsed; already-structured content is returned as-is; a plain/non-JSON string, scalar, or multimodal content yields `None` (there is no structured payload). A read-side companion to [`files`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.BaseToolReturnPart.files) and [`model_response_object`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.BaseToolReturnPart.model_response_object); some UI wire formats (e.g. AG-UI) transmit tool results as JSON strings, so [`narrow_type`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ToolReturnPart.narrow_type) uses it to recover the structured payload a typed return subclass expects. + +###### Returns + +[`dict`](https://docs.python.org/3/reference/expressions.html#dict)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] | [`list`](https://docs.python.org/3/glossary.html#term-list)\[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] | [`None`](https://docs.python.org/3/library/constants.html#None) + ##### model\_response\_str\_and\_user\_content ```python @@ -20470,7 +20698,7 @@ def narrow_type( Promote a base `ToolReturnPart` to its typed subclass when its `tool_kind` is registered. -Returns the input unchanged when neither the `tool_kind` kwarg nor `part.tool_kind` resolves to a registered subclass. Pass `tool_kind` to inject the discriminator inline -- the narrower applies it as part of its single dataclass clone, dropping the need for an upstream `replace(part, tool_kind=...)`. Use this on direct construction; Pydantic deserialization promotes automatically via the discriminated-union dispatch on [`ModelRequestPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelRequestPart). +Best-effort: returns the part unchanged when the `tool_kind` (kwarg or on the part) resolves to no registered subclass, and strips an unsubstantiated `tool_kind` when the part's data doesn't validate against that subclass -- keeping it on a base part would break a [`ModelMessagesTypeAdapter`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelMessagesTypeAdapter) round-trip. For direct construction; Pydantic deserialization promotes automatically via the discriminated union. ###### Returns @@ -20524,7 +20752,7 @@ def narrow_type( Promote a base `NativeToolReturnPart` to its typed subclass when its `tool_kind` is registered. -Returns the input unchanged when neither the `tool_kind` kwarg nor `part.tool_kind` resolves to a registered subclass. Pass `tool_kind` to inject the discriminator inline -- the narrower applies it as part of its single dataclass clone. Use this on direct construction; Pydantic deserialization promotes automatically via the discriminated-union dispatch on [`ModelResponsePart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponsePart). +Best-effort: returns the part unchanged when the `tool_kind` (kwarg or on the part) resolves to no registered subclass, and strips an unsubstantiated `tool_kind` when the part's data doesn't validate against that subclass -- keeping it on a base part would break a [`ModelMessagesTypeAdapter`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelMessagesTypeAdapter) round-trip. For direct construction; Pydantic deserialization promotes automatically via the discriminated union. ###### Returns @@ -20929,7 +21157,7 @@ A file response from a model. The file content of the response. -**Type:** [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated)\[[`BinaryContent`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.BinaryContent), [`pydantic.AfterValidator`](https://docs.pydantic.dev/latest/api/pydantic/functional_validators/#pydantic.functional_validators.AfterValidator)(`BinaryImage.narrow_type`)\] +**Type:** [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated)\[[`BinaryContent`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.BinaryContent), [`pydantic.AfterValidator`](https://docs.pydantic.dev/latest/api/pydantic/functional_validators/#pydantic.functional_validators.AfterValidator)([`BinaryContent.narrow_type`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.BinaryContent.narrow_type))\] ##### id @@ -21113,7 +21341,7 @@ def narrow_type( Promote a base `ToolCallPart` to its typed subclass when its `tool_kind` is registered. -Returns the input unchanged when neither the `tool_kind` kwarg nor `part.tool_kind` resolves to a registered subclass. Pass `tool_kind` to inject the discriminator inline -- the narrower applies it as part of its single dataclass clone, dropping the need for an upstream `replace(part, tool_kind=...)`. Use this on direct construction; Pydantic deserialization promotes automatically via the discriminated-union dispatch on [`ModelResponsePart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponsePart). +Best-effort: returns the part unchanged when the `tool_kind` (kwarg or on the part) resolves to no registered subclass, and strips an unsubstantiated `tool_kind` when the part's data doesn't validate against that subclass -- keeping it on a base part would break a [`ModelMessagesTypeAdapter`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelMessagesTypeAdapter) round-trip. For direct construction; Pydantic deserialization promotes automatically via the discriminated union. ###### Returns @@ -21163,7 +21391,7 @@ def narrow_type( Promote a base `NativeToolCallPart` to its typed subclass when its `tool_kind` is registered. -Returns the input unchanged when neither the `tool_kind` kwarg nor `part.tool_kind` resolves to a registered subclass. Pass `tool_kind` to inject the discriminator inline -- the narrower applies it as part of its single dataclass clone. Use this on direct construction; Pydantic deserialization promotes automatically via the discriminated-union dispatch on [`ModelResponsePart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponsePart). +Best-effort: returns the part unchanged when the `tool_kind` (kwarg or on the part) resolves to no registered subclass, and strips an unsubstantiated `tool_kind` when the part's data doesn't validate against that subclass -- keeping it on a base part would break a [`ModelMessagesTypeAdapter`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelMessagesTypeAdapter) round-trip. For direct construction; Pydantic deserialization promotes automatically via the discriminated union. ###### Returns @@ -21263,7 +21491,12 @@ Additional data that can be accessed programmatically by the application but is ##### state -Lifecycle state of the response. See [`ModelResponseState`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponseState). +The state of this response, indicating whether it is final or requires further action. + +- `'complete'` -- The response is done. This is the default. +- `'incomplete'` -- A streamed response is still in flight or was stopped before completion. +- `'suspended'` -- The model paused mid-turn and expects a continuation request. The agent graph will automatically send a continuation request. Set by providers that pause mid-turn (e.g. Anthropic `pause_turn`) or return background/async responses (e.g. OpenAI background mode). +- `'interrupted'` -- Streaming was explicitly cancelled before the model finished generating. Set when a streaming response is cancelled via `StreamedResponse.cancel()`. **Type:** [`ModelResponseState`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponseState) **Default:** `'complete'` @@ -21411,7 +21644,7 @@ Signatures are only sent back to the same provider. Required to be set when `pro Additional data returned by the provider that can't be mapped to standard fields. -Can be a dict to merge with existing details, or a callable that takes the existing details and returns updated details. +Can be a dict to merge with existing details, or a callable that takes the existing details and returns updated details. A callable is a transient merge callback and does not survive JSON serialization (it is emitted as `null`); it is resolved to a concrete dict once the delta is applied to a `ThinkingPart`. This is used for data that is required to be sent back to APIs, as well as data users may want to access programmatically. @@ -21782,9 +22015,97 @@ Check if obj is a MultiModalContent type, enabling type narrowing. [`TypeGuard`](https://docs.python.org/3/library/typing.html#typing.TypeGuard)\[[`MultiModalContent`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.MultiModalContent)\] +### parse\_tool\_kind + +```python +def parse_tool_kind(value: str) -> ToolPartKind | None +``` + +Return `value` if it's a known [`ToolPartKind`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ToolPartKind), else `None`. + +UI adapters call this at the wire boundary to validate an untrusted client-supplied `tool_kind` string before setting it on a part, so an unknown value degrades to `None` rather than asserting a bogus discriminator. + +#### Returns + +`ToolPartKind` | [`None`](https://docs.python.org/3/library/constants.html#None) + +### narrow\_message\_parts + +```python +def narrow_message_parts(messages: Sequence[ModelMessage]) -> list[ModelMessage] +``` + +Promote each tool call/return part across `messages` to its typed subclass via its `tool_kind`. + +Best-effort and idempotent: a part whose `tool_kind` resolves to a registered typed subclass and whose data validates against it is promoted; a part with no `tool_kind`, an unregistered one, or shape-invalid data is left a base part (an unsubstantiated `tool_kind` is stripped -- see [`ToolCallPart.narrow_type`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ToolCallPart.narrow_type)). + +UI adapters reconstruct base parts from the wire format with `tool_kind` set from client-echoed metadata, then call this once instead of narrowing each part inline. Pydantic deserialization of a `ModelMessage` performs the same promotion via its discriminated-union dispatch; this is the direct-construction equivalent for callers that build parts by hand. + +#### Returns + +[`list`](https://docs.python.org/3/glossary.html#term-list)\[[`ModelMessage`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelMessage)\] + +### sanitize\_messages + +```python +def sanitize_messages( + messages: Sequence[ModelMessage], + *, + strip_system_prompts: bool = True, + allowed_file_url_schemes: Collection[str] = ('http', 'https'), + allowed_file_url_force_download: Collection[ForceDownloadMode] = (), + allow_uploaded_files: bool = False, + resolved_tool_call_ids: Collection[str] = (), +) -> list[ModelMessage] +``` + +Strip message parts that aren't safe to honor from untrusted input. + +This is the same default sanitization the [UI adapters](/docs/ai/integrations/ui/overview) apply to client-submitted messages before they're passed to an agent. Use it when loading `message_history` from a source the application does not fully trust, such as a browser request. + +By default it strips: + +- [`SystemPromptPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.SystemPromptPart)s (disable with `strip_system_prompts=False`). The system prompt is the server's to own; a client that can inject one can override the agent's behavior. If stripping leaves a `ModelRequest` with no parts, the request is dropped from history entirely. +- [`FileUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.FileUrl) parts whose URL scheme is not in `allowed_file_url_schemes` (default `http`/`https`). Non-HTTP schemes like `s3://` or `gs://` cause the model provider to fetch the object using the server-side IAM role, so they should only be accepted from trusted clients. +- [`FileUrl.force_download`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.FileUrl.force_download) values other than `False` that aren't in `allowed_file_url_force_download`, resetting them to `False`. Both `True` and `'allow-local'` are reset by default. Applies to file URLs in user content and those nested in tool return parts. +- [`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile) items unless `allow_uploaded_files=True`. Like a non-HTTP `FileUrl`, an `UploadedFile` references an object the model provider fetches using the server-side IAM role. Applies to uploaded files in user content and those nested in tool return parts. +- [`ToolCallPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ToolCallPart)s at the end of the history that aren't in `resolved_tool_call_ids`. An unresolved tool call at the end of client-supplied history doesn't correspond to a paused agent run and shouldn't be executed. [`NativeToolCallPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.NativeToolCallPart)s are left in place: the provider executes them server-side and pairs each with a [`NativeToolReturnPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.NativeToolReturnPart) in the same response, and the agent loop never dispatches them, so they aren't a client-injection risk. If stripping leaves the final response with no parts, the response is dropped from history entirely. + +#### Returns + +[`list`](https://docs.python.org/3/glossary.html#term-list)\[[`ModelMessage`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelMessage)\] + +#### Parameters + +**`messages`** : [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[[`ModelMessage`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelMessage)\] + +Messages to sanitize. + +**`strip_system_prompts`** : [`bool`](https://docs.python.org/3/library/functions.html#bool) _Default:_ `True` + +Whether to strip [`SystemPromptPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.SystemPromptPart)s. + +**`allowed_file_url_schemes`** : [`Collection`](https://docs.python.org/3/library/typing.html#typing.Collection)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] _Default:_ `('http', 'https')` + +URL schemes allowed for [`FileUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.FileUrl) parts. Defaults to `http` and `https`. + +**`allowed_file_url_force_download`** : [`Collection`](https://docs.python.org/3/library/typing.html#typing.Collection)\[`ForceDownloadMode`\] _Default:_ `()` + +Additional [`FileUrl.force_download`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.FileUrl.force_download) values to allow. `False` is always allowed. Defaults to no additional values. + +**`allow_uploaded_files`** : [`bool`](https://docs.python.org/3/library/functions.html#bool) _Default:_ `False` + +Whether to honor [`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile) items from the untrusted input. Off by default, since an uploaded file references an object the model provider fetches using the server-side IAM role. + +**`resolved_tool_call_ids`** : [`Collection`](https://docs.python.org/3/library/typing.html#typing.Collection)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] _Default:_ `()` + +Tool call IDs to preserve when the final response ends with tool calls. Use this for human-in-the-loop resumption when matching tool results are being submitted with the same request. + ### FinishReason -Reason the model finished generating the response, normalized to OpenTelemetry values. +Reason the model finished generating the response. + +Mostly normalized to OpenTelemetry semantic convention values. Whether the agent should automatically continue is determined by `ModelResponse.state`, not by this field. **Type:** [`TypeAlias`](https://docs.python.org/3/library/typing.html#typing.TypeAlias) **Default:** `Literal['stop', 'length', 'content_filter', 'tool_call', 'error']` @@ -21794,9 +22115,10 @@ Lifecycle state of a model response. - `'complete'`: the response has been fully received from the model. - `'incomplete'`: the response is still being streamed and may receive more parts. Yielded by `AgentStream.response` and [`StreamedRunResult.stream_response`](/docs/ai/api/pydantic-ai/result/#pydantic_ai.result.StreamedRunResult.stream_response) while iteration is in flight. -- `'interrupted'`: streaming was explicitly stopped via [`StreamedRunResult.cancel()`](/docs/ai/api/pydantic-ai/result/#pydantic_ai.result.StreamedRunResult.cancel) before the model finished generating. +- `'suspended'`: the model paused mid-turn and expects a continuation request. Used by Anthropic `pause_turn` and OpenAI background mode. Pydantic AI issues these continuations transparently for both `agent.run` and `agent.run_stream`, merging every segment into a single completed [`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse), so a finished turn in the message history is never left in this state. +- `'interrupted'`: streaming was explicitly stopped via [`StreamedResponse.cancel()`](/docs/ai/api/models/base/#pydantic_ai.models.StreamedResponse.cancel) before the model finished generating. -**Type:** [`TypeAlias`](https://docs.python.org/3/library/typing.html#typing.TypeAlias) **Default:** `Literal['complete', 'incomplete', 'interrupted']` +**Type:** [`TypeAlias`](https://docs.python.org/3/library/typing.html#typing.TypeAlias) **Default:** `Literal['complete', 'incomplete', 'suspended', 'interrupted']` ### ModelRequestState @@ -21818,7 +22140,7 @@ Type for the force\_download parameter on FileUrl subclasses. Type for provider\_details input: can be a static dict, a callback to update existing details, or None. -**Type:** [`TypeAlias`](https://docs.python.org/3/library/typing.html#typing.TypeAlias) **Default:** `dict[str, Any] | Callable[[dict[str, Any] | None], dict[str, Any]] | None` +**Type:** [`TypeAlias`](https://docs.python.org/3/library/typing.html#typing.TypeAlias) **Default:** `Annotated[dict[str, Any] | Callable[[dict[str, Any] | None], dict[str, Any]] | None, pydantic.PlainSerializer(_serialize_provider_details_delta, return_type=(dict[str, Any] | None), when_used='json')]` ### UploadedFileProviderName @@ -21832,7 +22154,7 @@ The `'google-gla'` and `'google-vertex'` values are retained for backward compat Union of all multi-modal content types with a discriminator for Pydantic validation. -**Default:** `Annotated[ImageUrl | AudioUrl | DocumentUrl | VideoUrl | BinaryContent | UploadedFile, pydantic.Discriminator('kind')]` +**Default:** `Annotated[ImageUrl | AudioUrl | DocumentUrl | VideoUrl | Annotated[BinaryContent, pydantic.AfterValidator(BinaryContent.narrow_type)] | UploadedFile, pydantic.Discriminator('kind')]` ### RETURN\_VALUE\_KEY @@ -21840,6 +22162,12 @@ Key used to wrap non-dict tool return values in `model_response_object()`. **Default:** `'return_value'` +### tool\_return\_content\_ta + +TypeAdapter for `ToolReturnContent` -- used by UI adapters to rehydrate multimodal items (`BinaryContent`, `ImageUrl`, etc.) from raw JSON/dict payloads carried in wire-protocol fields typed as `Any` (e.g. Vercel's `ToolOutputAvailablePart.output`). + +**Type:** [`pydantic.TypeAdapter`](https://docs.pydantic.dev/latest/api/pydantic/type_adapter/#pydantic.type_adapter.TypeAdapter)\[`ToolReturnContent`\] **Default:** `pydantic.TypeAdapter(ToolReturnContent, config=(pydantic.ConfigDict(defer_build=True)))` + ### ToolPartKind Discriminator value for the typed call/return-part subclass associated with a tool. @@ -21984,6 +22312,7 @@ Supported by: - Anthropic - OpenAI Responses +- xAI, see [https://docs.x.ai/docs/guides/tools/search-tools#web-search-parameters](https://docs.x.ai/docs/guides/tools/search-tools#web-search-parameters) **Type:** [`WebSearchUserLocation`](/docs/ai/api/pydantic-ai/native_tools/#pydantic_ai.native_tools.WebSearchUserLocation) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` @@ -22042,6 +22371,7 @@ Supported by: - Anthropic - OpenAI Responses +- xAI #### Attributes @@ -22053,7 +22383,9 @@ The city where the user is located. ##### country -The country where the user is located. For OpenAI, this must be a 2-letter country code (e.g., 'US', 'GB'). +The country where the user is located. + +For OpenAI and xAI, this must be a 2-letter ISO 3166-1 alpha-2 country code (e.g., 'US', 'GB'). **Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) @@ -22087,7 +22419,7 @@ Supported by: ##### allowed\_x\_handles -If provided, only posts from these X handles will be included (max 10). +If provided, only posts from these X handles will be included (max 20). Supported by: @@ -22097,7 +22429,7 @@ Supported by: ##### excluded\_x\_handles -If provided, posts from these X handles will be excluded (max 10). +If provided, posts from these X handles will be excluded (max 20). Supported by: @@ -22185,6 +22517,19 @@ Supported by: #### Attributes +##### files + +Uploaded files to make available in the code execution environment. + +Only files matching the model provider are used; files from other providers are ignored. + +Supported by: + +- Anthropic +- OpenAI Responses + +**Type:** [`list`](https://docs.python.org/3/glossary.html#term-list)\[[`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile)\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + ##### kind The kind of tool. @@ -23141,11 +23486,27 @@ When True, sampling parameters may need to be dropped depending on reasoning\_ef **Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) +##### openai\_reasoning\_enabled\_by\_default + +Whether the model reasons by default when `reasoning_effort` is omitted. Default: `False`. + +True for models whose default effort is active (e.g. 'medium'), such as the o-series, the original GPT-5, and GPT-5.5+, and False for the GPT-5.1..5.4 mainline models which default to `reasoning_effort='none'`. This decides whether sampling parameters must be dropped when no effort is set, and is independent of whether reasoning can be turned off (`openai_supports_reasoning_effort_none`). + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) + ##### openai\_supports\_reasoning\_effort\_none -Whether the model supports sampling parameters (temperature, top\_p, etc.) when reasoning\_effort='none'. Default: `False`. +Whether the model accepts `reasoning_effort='none'` and allows sampling parameters (temperature, top\_p, etc.) while reasoning is off. Default: `False`. + +The GPT-5.1+ mainline models support turning reasoning off via `effort='none'`, and sampling params are accepted in that mode. When reasoning is enabled (low/medium/high/xhigh), sampling params are not supported. Whether the model reasons by default is tracked separately by `openai_reasoning_enabled_by_default`. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) + +##### openai\_responses\_supports\_reasoning\_mode -Models like GPT-5.1 and GPT-5.2 default to reasoning\_effort='none' and support sampling params in that mode. When reasoning is enabled (low/medium/high/xhigh), sampling params are not supported. +Whether the Responses API supports `reasoning.mode` (`'standard' | 'pro'`) for this model. Default: `False`. + +Currently only supported by the GPT-5.6 family. **Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) @@ -23266,6 +23627,14 @@ When True and the unified thinking level is a string (e.g. 'high'), it is also m **Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) +##### anthropic\_supports\_dynamic\_filtering + +Whether the model supports Anthropic-managed dynamic filtering for web search/fetch. Default: `False`. + +When enabled, Pydantic AI selects the `web_search_20260209` / `web_fetch_20260209` tool versions, which let Claude filter web results via code execution before they enter context. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) + ##### anthropic\_supports\_xhigh\_effort Whether the model supports the `xhigh` effort value in `output_config`. Default: `False`. @@ -23546,6 +23915,88 @@ Get the model profile for a Qwen model. [`ModelProfile`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile) | [`None`](https://docs.python.org/3/library/constants.html#None) +### GroqModelProfile + +**Bases:** [`ModelProfile`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile) + +Profile for models used with GroqModel. + +ALL FIELDS MUST BE `groq_` PREFIXED SO YOU CAN MERGE THEM WITH OTHER MODELS. + +#### Attributes + +##### groq\_always\_has\_web\_search\_builtin\_tool + +Whether the model always has the web search built-in tool available. Default: `False`. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) + +##### groq\_supports\_reasoning\_disable + +Whether `thinking=False` truly disables reasoning via `reasoning_effort='none'`. Default: `False`. + +Only the qwen3 family supports this; other Groq reasoning models can at most suppress reasoning _output_ via `reasoning_format='hidden'` while still reasoning internally. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) + +##### groq\_supports\_graded\_reasoning\_effort + +Whether the model accepts graded `reasoning_effort` values (`low`/`medium`/`high`). Default: `False`. + +Only the gpt-oss family supports this; unified `thinking` levels map to those values via [`GROQ_GPT_OSS_REASONING_EFFORT_MAP`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.groq.GROQ_GPT_OSS_REASONING_EFFORT_MAP). The qwen3 family instead only accepts `none`/`default` (see `groq_supports_reasoning_disable`). + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) + +### groq\_model\_profile + +```python +def groq_model_profile(model_name: str) -> ModelProfile +``` + +Get the model profile for a Groq model. + +#### Returns + +[`ModelProfile`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile) + +### GROQ\_GPT\_OSS\_REASONING\_EFFORT\_MAP + +Maps unified thinking effort levels to the graded `reasoning_effort` values the gpt-oss family accepts. + +gpt-oss only accepts `low`/`medium`/`high` (not `none`/`default`), so `minimal` folds into `low` and `xhigh` into `high`. `thinking=True` (bare enable) maps to `medium` in `GroqModel`, mirroring the neutral default other providers use. See [the Groq docs](https://console.groq.com/docs/reasoning#reasoning-effort). + +**Type:** [`dict`](https://docs.python.org/3/reference/expressions.html#dict)\[`ThinkingEffort`, [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\['low', 'medium', 'high'\]\] **Default:** `{'minimal': 'low', 'low': 'low', 'medium': 'medium', 'high': 'high', 'xhigh': 'high'}` + +### ZaiModelProfile + +**Bases:** [`ModelProfile`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile) + +Profile for Z.AI (Zhipu AI) GLM models. + +#### Attributes + +##### zai\_supports\_reasoning\_effort + +Whether the model accepts a per-request `reasoning_effort` level (GLM-5.2). + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) + +### zai\_model\_profile + +```python +def zai_model_profile(model_name: str) -> ModelProfile | None +``` + +The model profile for ZAI (Zhipu AI) GLM models, matched by Z.AI's native `glm-*` ids. + +Marks thinking-capable models (`glm-5`, `glm-4.7`, `glm-4.6`, `glm-4.5`) via `supports_thinking=True`. This includes the `glm-4.6v` and `glm-4.5v` vision models, which also support thinking mode per the Z.AI docs. GLM-5.2 additionally accepts a per-request reasoning effort level, flagged via `zai_supports_reasoning_effort=True`. + +The provider-specific request/response shape (e.g. the `reasoning_content` field used by Z.AI's API) is configured in `ZaiProvider.model_profile()` rather than here. Providers that serve GLM models under a different id scheme (e.g. Cerebras's `zai-glm-*`, which doesn't match the `glm-*` prefixes above) configure thinking support in their own `model_profile()`. + +#### Returns + +[`ModelProfile`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile) | [`None`](https://docs.python.org/3/library/constants.html#None) + --- # [pydantic_ai.providers](https://pydantic.dev/docs/ai/api/pydantic-ai/providers/) @@ -23757,6 +24208,7 @@ def __init__( api_key: str, http_client: httpx.AsyncClient | None = None, base_url: str | None = None, + retry_options: HttpRetryOptions | None = None, ) -> None def __init__(*, client: Client) -> None ``` @@ -23785,6 +24237,10 @@ An existing `httpx.AsyncClient` to use for making HTTP requests. The base URL for the Gemini API. +**`retry_options`** : `HttpRetryOptions` | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +HTTP retry options for transient errors (429, 5xx, etc.). See `google.genai.types.HttpRetryOptions` for available fields. + ### GoogleCloudLocation Regions available for Google Cloud. More details [here](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/locations#genai-locations). @@ -23944,7 +24400,39 @@ Default: `False`. Default: `frozenset({'image'})`. -**Type:** [`frozenset`](https://docs.python.org/3/library/stdtypes.html#frozenset)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] +**Type:** [`frozenset`](https://docs.python.org/3/library/stdtypes.html#frozenset)\[[`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\['image', 'document', 'video'\]\] + +##### bedrock\_tool\_result\_colocatable\_content + +Content-block kinds that this model accepts in the same user message as a `toolResult` block. + +pydantic-ai merges consecutive user turns into one Bedrock message, which can place a `toolResult` alongside a following turn's text/attachment. Some models reject that: Anthropic rejects documents and video next to a `toolResult`, while Llama and Mistral reject _any_ content sharing the turn (the `toolResult` must be alone). When a merge would co-locate a `toolResult` with a kind not listed here, the adapter splits the turns and separates them with a synthetic assistant message (Bedrock re-merges consecutive same-role turns, so a bare split doesn't suffice). See #6081. + +Default: all kinds (no restriction); the model receives merged turns unchanged. + +**Type:** [`frozenset`](https://docs.python.org/3/library/stdtypes.html#frozenset)\[[`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\['text', 'image', 'document', 'video'\]\] + +##### bedrock\_supports\_leading\_assistant\_message + +Whether this model accepts a conversation that starts with an assistant message. + +Bedrock's Converse API requires that a conversation start with a user message for most model families (Amazon Nova, Meta Llama, Mistral, Cohere, AI21, Writer, ...), which reject a leading assistant turn with `"A conversation must start with a user message..."`. Anthropic and Qwen models accept a leading assistant turn, so for them we don't need to synthesize a placeholder user message when `message_history` starts with a `ModelResponse`. + +Verified against Bedrock `us-east-1` on 2026-07-03. + +Default: `False` (strict -- synthesize a leading user message when history starts with an assistant turn). + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) + +##### bedrock\_supports\_tool\_result\_status + +Whether this model accepts the `status` field on a `toolResult` block in Bedrock's Converse API. + +Most families accept (and pydantic-ai emits) `status: 'success'`/`'error'` on `toolResult` blocks, but Writer Palmyra rejects it (`"This model doesn't support the status field. Remove status and try again."`), so the field is omitted for it. Verified against Bedrock `us-east-1`. + +Default: `True`. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) ##### bedrock\_supports\_strict\_tool\_definition @@ -24119,6 +24607,18 @@ Get the model profile for a DeepSeek model used via Bedrock. [`ModelProfile`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile) | [`None`](https://docs.python.org/3/library/constants.html#None) +### bedrock\_meta\_model\_profile + +```python +def bedrock_meta_model_profile(model_name: str) -> ModelProfile | None +``` + +Get the model profile for a Meta Llama model used via Bedrock. + +#### Returns + +[`ModelProfile`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile) | [`None`](https://docs.python.org/3/library/constants.html#None) + ### bedrock\_mistral\_model\_profile ```python @@ -24155,6 +24655,44 @@ Get the model profile for a Google model used via Bedrock. [`ModelProfile`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile) | [`None`](https://docs.python.org/3/library/constants.html#None) +### bedrock\_zai\_model\_profile + +```python +def bedrock_zai_model_profile(model_name: str) -> ModelProfile | None +``` + +Get the model profile for a Z.AI (Zhipu) GLM model used via Bedrock. + +#### Returns + +[`ModelProfile`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile) | [`None`](https://docs.python.org/3/library/constants.html#None) + +### bedrock\_moonshotai\_model\_profile + +```python +def bedrock_moonshotai_model_profile(model_name: str) -> ModelProfile | None +``` + +Get the model profile for a Moonshot AI Kimi model used via Bedrock. + +Registered for both the `moonshot.` and `moonshotai.` Bedrock provider prefixes. + +#### Returns + +[`ModelProfile`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile) | [`None`](https://docs.python.org/3/library/constants.html#None) + +### bedrock\_writer\_model\_profile + +```python +def bedrock_writer_model_profile(model_name: str) -> ModelProfile | None +``` + +Get the model profile for a Writer Palmyra model used via Bedrock. + +#### Returns + +[`ModelProfile`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile) | [`None`](https://docs.python.org/3/library/constants.html#None) + ### bedrock\_minimax\_model\_profile ```python @@ -24821,6 +25359,46 @@ Optional custom httpx.AsyncClient for making HTTP requests - `UserError` -- If API key is not provided and SAMBANOVA\_API\_KEY env var is not set +### ZaiProvider + +**Bases:** `Provider[AsyncOpenAI]` + +Provider for Z.AI (Zhipu AI) API. + +Z.AI provides GLM models with support for thinking/reasoning mode and preserved thinking across turns. + +#### Methods + +##### \_\_init\_\_ + +```python +def __init__() -> None +def __init__(*, api_key: str) -> None +def __init__(*, api_key: str, http_client: httpx.AsyncClient) -> None +def __init__(*, http_client: httpx.AsyncClient) -> None +def __init__(*, openai_client: AsyncOpenAI | None = None) -> None +``` + +Create a new Z.AI provider. + +###### Returns + +[`None`](https://docs.python.org/3/library/constants.html#None) + +###### Parameters + +**`api_key`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +The API key to use for authentication, if not provided, the `ZAI_API_KEY` environment variable will be used if available. + +**`openai_client`** : `AsyncOpenAI` | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +An existing `AsyncOpenAI` client to use. If provided, `api_key` and `http_client` must be `None`. + +**`http_client`** : `httpx.AsyncClient` | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +An existing `httpx.AsyncClient` to use for making HTTP requests. + --- # [pydantic_ai.result](https://pydantic.dev/docs/ai/api/pydantic-ai/result/) @@ -25104,6 +25682,23 @@ The interrupted response state is recorded in the message history so that `all_m Synchronous wrapper for [`StreamedRunResult`](/docs/ai/api/pydantic-ai/result/#pydantic_ai.result.StreamedRunResult) that only exposes sync methods. +All of the run's async work happens on the caller's event loop. Context-manager and iterator lifecycles remain in stable tasks, so cancel scopes entered and exited by the agent graph never straddle tasks and OpenTelemetry spans stay correctly nested. The wrapper must be used and closed on the thread where it was created. + +This is a synchronous context manager; the underlying stream is cleaned up on exit: + +```python +from pydantic_ai import Agent + +agent = Agent('openai:gpt-5.2') + +def main(): + with agent.run_stream_sync('What is the capital of the UK?') as response: + print(response.get_output()) + #> The capital of the UK is London. +``` + +Using it without a `with` block also works for backwards compatibility. Garbage collection requests best-effort cleanup on the owner loop, but it cannot drive a stopped owner loop from another thread or while another loop is running. A `with` block should be used whenever deterministic cleanup matters. + #### Attributes ##### response @@ -26145,14 +26740,14 @@ Override the client-level default timeout for a request, in seconds. Supported by: -- Gemini +- Gemini (numeric seconds only, not `httpx.Timeout`) - Anthropic - OpenAI - Groq -- Mistral +- Mistral (numeric seconds only, not `httpx.Timeout`) - xAI -**Type:** [`float`](https://docs.python.org/3/library/functions.html#float) | `Timeout` +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`float`](https://docs.python.org/3/library/functions.html#float) | `Timeout` ##### parallel\_tool\_calls @@ -26382,6 +26977,16 @@ LLM usage associated with the run. **Type:** [`RunUsage`](/docs/ai/api/pydantic-ai/usage/#pydantic_ai.usage.RunUsage) +##### usage\_limits + +The [`UsageLimits`](/docs/ai/api/pydantic-ai/usage/#pydantic_ai.usage.UsageLimits) enforced for this run. + +During a run this is always set: if no limits were passed, the run enforces the default [`UsageLimits()`](/docs/ai/api/pydantic-ai/usage/#pydantic_ai.usage.UsageLimits) (e.g. `request_limit=50`). It is only `None` on a bare/synthetic `RunContext` that isn't backed by a run. + +This reflects the limits the run is already enforcing, so tools and capabilities can disclose or adapt to the run's budget (e.g. a budget-disclosure capability) without having to be configured with a duplicate copy. Combine it with [`usage`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.usage) to compute how much budget remains. Treat it as read-only: it is the live object the run enforces against, so mutating a field here _would_ change what the run enforces on subsequent requests. + +**Type:** [`UsageLimits`](/docs/ai/api/pydantic-ai/usage/#pydantic_ai.usage.UsageLimits) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + ##### agent The agent running this context, or `None` if not set. @@ -26512,9 +27117,9 @@ Populated before each model request, after all model settings layers (model defa ##### pending\_messages -Internal: queue read and mutated by `PendingMessageDrainCapability`. +Queue read and mutated by `PendingMessageDrainCapability`. -Set to the run's live queue during an agent run; `None` in synthetic contexts that aren't backed by a running agent (e.g. the `RunContext` built by `Agent.system_prompt_parts`), where [`enqueue`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.enqueue) would have nowhere to drain to and so raises. Use [`enqueue`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.enqueue) to add messages -- don't append directly. +Set to the run's live queue during an agent run; `None` in synthetic contexts that aren't backed by a running agent (e.g. the `RunContext` built by `Agent.system_prompt_parts`), where [`enqueue`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.enqueue) would have nowhere to drain to and so raises. Managed by the framework: read it if useful, but use [`enqueue`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.enqueue) to add messages rather than mutating it directly. **Type:** [`list`](https://docs.python.org/3/glossary.html#term-list)\[`PendingMessage`\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `field(default=None, repr=False)` @@ -26538,7 +27143,7 @@ All capabilities registered for the current run, including deferred ones. IDs of the deferred capabilities the model has explicitly loaded via the `load_capability` tool. -The capability-side mirror of `discovered_tool_names`: the runtime-revealed subset. Seeded during run preparation from message history (`parse_loaded_capabilities`); the `load_capability` tool body adds to it for in-step loads. Use `available_capability_ids` for the full set of currently-active capabilities (auto/always-on plus these). +The capability-side mirror of `discovered_tool_names`: the runtime-revealed subset. Seeded during run preparation from message history (`parse_loaded_capabilities`); the `load_capability` tool body adds to it for in-step loads. Use `available_capability_ids` for the full set of currently-active capabilities (auto/always-on plus these). Managed by the framework: safe to read, but don't mutate it directly. **Type:** [`set`](https://docs.python.org/3/reference/expressions.html#set)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `field(default_factory=(set[str]))` @@ -26554,7 +27159,7 @@ This is `None` outside capability dispatch, where there is no current capability Names of deferred tools revealed via tool-search return parts in the message history. -The tool-side mirror of `loaded_capability_ids`: the runtime-revealed subset that `ToolSearchToolset.get_tools` reads to decide which deferred tools to make visible this turn. Populated during run preparation from message history. Use `available_tool_names` for the full set of currently-callable tools (always-visible plus these). +The tool-side mirror of `loaded_capability_ids`: the runtime-revealed subset that `ToolSearchToolset.get_tools` reads to decide which deferred tools to make visible this turn. Populated during run preparation from message history. Use `available_tool_names` for the full set of currently-callable tools (always-visible plus these). Managed by the framework: safe to read, but don't mutate it directly. **Type:** [`set`](https://docs.python.org/3/reference/expressions.html#set)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `field(default_factory=(set[str]))` @@ -29032,7 +29637,7 @@ def evaluate( task_name: str | None = None, metadata: dict[str, Any] | None = None, repeat: int = 1, - lifecycle: type[CaseLifecycle[InputsT, OutputT, MetadataT]] | None = None, + lifecycle: type[CaseLifecycle[InputsT, OutputT, MetadataT]] | Callable[[Case[InputsT, OutputT, MetadataT]], CaseLifecycle[InputsT, OutputT, MetadataT]] | None = None, ) -> EvaluationReport[InputsT, OutputT, MetadataT] ``` @@ -29082,7 +29687,7 @@ Optional dict of experiment metadata. Number of times to run each case. When > 1, each case is run multiple times and results are grouped by the original case name for aggregation. Defaults to 1. -**`lifecycle`** : [`type`](https://docs.python.org/3/glossary.html#term-type)\[`CaseLifecycle`\[`InputsT`, `OutputT`, `MetadataT`\]\] | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` +**`lifecycle`** : [`type`](https://docs.python.org/3/glossary.html#term-type)\[`CaseLifecycle`\[`InputsT`, `OutputT`, `MetadataT`\]\] | [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[`Case`\[`InputsT`, `OutputT`, `MetadataT`\]\], `CaseLifecycle`\[`InputsT`, `OutputT`, `MetadataT`\]\] | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` Optional lifecycle class for per-case setup, context preparation, and teardown hooks. A new instance is created for each case. See [`CaseLifecycle`](/docs/ai/api/pydantic_evals/lifecycle/#pydantic_evals.lifecycle.CaseLifecycle). @@ -29100,7 +29705,7 @@ def evaluate_sync( task_name: str | None = None, metadata: dict[str, Any] | None = None, repeat: int = 1, - lifecycle: type[CaseLifecycle[InputsT, OutputT, MetadataT]] | None = None, + lifecycle: type[CaseLifecycle[InputsT, OutputT, MetadataT]] | Callable[[Case[InputsT, OutputT, MetadataT]], CaseLifecycle[InputsT, OutputT, MetadataT]] | None = None, ) -> EvaluationReport[InputsT, OutputT, MetadataT] ``` @@ -29150,7 +29755,7 @@ Optional dict of experiment metadata. Number of times to run each case. When > 1, each case is run multiple times and results are grouped by the original case name for aggregation. Defaults to 1. -**`lifecycle`** : [`type`](https://docs.python.org/3/glossary.html#term-type)\[`CaseLifecycle`\[`InputsT`, `OutputT`, `MetadataT`\]\] | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` +**`lifecycle`** : [`type`](https://docs.python.org/3/glossary.html#term-type)\[`CaseLifecycle`\[`InputsT`, `OutputT`, `MetadataT`\]\] | [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[`Case`\[`InputsT`, `OutputT`, `MetadataT`\]\], `CaseLifecycle`\[`InputsT`, `OutputT`, `MetadataT`\]\] | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` Optional lifecycle class for per-case setup, context preparation, and teardown hooks. A new instance is created for each case. See [`CaseLifecycle`](/docs/ai/api/pydantic_evals/lifecycle/#pydantic_evals.lifecycle.CaseLifecycle). @@ -29532,12 +30137,6 @@ Experiment-level metadata. **Type:** [`dict`](https://docs.python.org/3/reference/expressions.html#dict)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] | [`None`](https://docs.python.org/3/library/constants.html#None) -### Equals - -**Bases:** `Evaluator[object, object, object]` - -Check if the output exactly equals the provided value. - ### EvaluatorContext **Bases:** `Generic[InputsT, OutputT, MetadataT]` @@ -29625,6 +30224,12 @@ The span tree is a graph where each node corresponds to an OpenTelemetry span re **Type:** `SpanTree` +### Equals + +**Bases:** `Evaluator[object, object, object]` + +Check if the output exactly equals the provided value. + ### EvaluationReason The result of running an evaluator with an optional explanation. @@ -29935,6 +30540,34 @@ Returns both a `PrecisionRecall` chart and a `ScalarResult` with the AUC value. Check if the execution time is under the specified maximum. +### ToolCorrectness + +**Bases:** `Evaluator[object, object, object]` + +Assert that the agent called a specific multiset of tools. + +This compares the names of tools actually invoked (as a multiset) against `expected_tools`. Repeated names require repeated calls -- for example, `expected_tools=['search', 'search']` passes only if `search` was called at least twice. + +#### Constructor Parameters + +**`expected_tools`** : [`list`](https://docs.python.org/3/glossary.html#term-list)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] + +The tool names the agent is expected to call. Order does not matter; duplicates are significant. + +**`allow_extra`** : [`bool`](https://docs.python.org/3/library/functions.html#bool) _Default:_ `False` + +If `False` (the default), any tool call not listed in `expected_tools` fails the check. Set to `True` to only require that the expected tools were called, permitting extras. + +**`include_failed`** : [`bool`](https://docs.python.org/3/library/functions.html#bool) _Default:_ `False` + +If `False` (the default), tool-call attempts that ended in an error (a raised exception, or a retry requested via `ModelRetry`) are not counted. Set to `True` to count every attempt. + +**`evaluation_name`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +Optional override for the reported evaluation name. + +Returns `EvaluationReason` with a `bool` value. + ### OutputConfig **Bases:** [`TypedDict`](https://docs.python.org/3/library/typing.html#typing.TypedDict) @@ -29957,11 +30590,51 @@ Computes an ROC curve and AUC from case data. Returns a `LinePlot` with the ROC curve (plus a dashed random-baseline diagonal) and a `ScalarResult` with the AUC value. -### HasMatchingSpan +### TrajectoryMatch **Bases:** `Evaluator[object, object, object]` -Check if the span tree contains a span that matches the specified query. +Compare the agent's tool-call trajectory to an expected one. + +#### Constructor Parameters + +**`expected_trajectory`** : [`list`](https://docs.python.org/3/glossary.html#term-list)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] + +The expected ordered list of tool names. + +**`order`** : `TrajectoryOrder` _Default:_ `'in_order'` + +How strictly to compare: + +- `'exact'`: actual must equal expected (1.0) or not (0.0). +- `'in_order'` (default): F1 computed from the longest common subsequence (LCS) of the two sequences. Extra calls reduce precision; missing calls reduce recall. +- `'any_order'`: F1 computed from the multiset intersection of the two trajectories. Order is ignored, but extra and missing calls still reduce the score. + +**`include_failed`** : [`bool`](https://docs.python.org/3/library/functions.html#bool) _Default:_ `False` + +If `False` (the default), tool-call attempts that ended in an error (a raised exception, or a retry requested via `ModelRetry`) are not part of the trajectory. Set to `True` to include every attempt. + +**`evaluation_name`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +Optional override for the reported evaluation name. + +Returns `EvaluationReason` with a `float` value in `[0.0, 1.0]` (including when no span tree was captured, in which case the value is `0.0`). For the F1-based modes, the `reason` text shows the precision, recall and F1 numbers so the score can be reproduced from the reported mismatch. + +If both the expected and actual trajectories are empty, all modes score `1.0`; if only one of them is empty, all modes score `0.0`. + +### GEval + +**Bases:** `Evaluator[object, object, object]` + +G-Eval-style chain-of-thought evaluator (Liu et al., 2023). + +The judge is shown the evaluation `criteria` and a list of explicit `evaluation_steps`, produces a short reasoning trace, and emits an integer score within `score_range` (inclusive), returned as an [`EvaluationReason`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.EvaluationReason). Because the criteria and steps are user-supplied, `GEval` puts no structural requirements on `ctx.inputs` or `ctx.output`. + +If you do not specify a model, it uses the default model for judging. This starts as 'openai:gpt-5.2', but can be overridden by calling [`set_default_judge_model`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.llm_as_a_judge.set_default_judge_model). + +Simplified G-Eval + +The paper computes a probability-weighted expectation over score tokens using log-probs. We ask the model for a direct integer score instead, trading some correlation with human judgment for provider-agnostic simplicity. ### KolmogorovSmirnovEvaluator @@ -29973,6 +30646,90 @@ Plots the empirical CDFs of the score distribution for positive and negative cas Returns a `LinePlot` with the two CDF curves and a `ScalarResult` with the KS statistic. +### HasMatchingSpan + +**Bases:** `Evaluator[object, object, object]` + +Check if the span tree contains a span that matches the specified query. + +### ArgumentCorrectness + +**Bases:** `Evaluator[object, object, object]` + +Assert that a specific tool call received particular arguments. + +Finds all local spans for `tool_name` in the run, picks the requested occurrence, parses the recorded JSON arguments, and compares them to `expected_arguments`. + +#### Constructor Parameters + +**`tool_name`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) + +The tool whose arguments should be checked. + +**`expected_arguments`** : [`dict`](https://docs.python.org/3/reference/expressions.html#dict)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] + +Expected argument keys/values. + +**`match_mode`** : `ArgumentMatchMode` _Default:_ `'subset'` + +`'subset'` (default) checks that every expected key/value is present in the actual arguments. `'exact'` requires deep equality. Note that the subset comparison applies only to top-level keys: an expected _value_ (including a nested dict) must compare equal to the actual value in full. + +**`occurrence`** : `ArgumentOccurrence` | [`int`](https://docs.python.org/3/library/functions.html#int) _Default:_ `'first'` + +Which invocation of the tool to inspect when the tool is called multiple times: `'first'`, `'last'`, or a 0-based integer index. A negative int is not supported. + +**`include_failed`** : [`bool`](https://docs.python.org/3/library/functions.html#bool) _Default:_ `False` + +If `False` (the default), tool-call attempts that ended in an error (a raised exception, or a retry requested via `ModelRetry`) are not considered. Set to `True` to consider every attempt; each attempt then counts as a separate occurrence, so `'first'` may select an attempt that was subsequently retried. + +**`evaluation_name`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +Optional override for the reported evaluation name. + +Returns `EvaluationReason` with a `bool` value. Fails gracefully with a descriptive reason if the tool was never called, the requested occurrence doesn't exist, or arguments weren't recorded (e.g. `include_content=False`). + +### MaxToolCalls + +**Bases:** `Evaluator[object, object, object]` + +Assert that the agent made at most `max_calls` locally-executed tool calls. + +#### Constructor Parameters + +**`max_calls`** : [`int`](https://docs.python.org/3/library/functions.html#int) + +Maximum allowed locally-executed tool calls. + +**`include_failed`** : [`bool`](https://docs.python.org/3/library/functions.html#bool) _Default:_ `True` + +If `True` (the default), tool-call attempts that ended in an error (a raised exception, or a retry requested via `ModelRetry`) count against the budget -- they still consumed time and tokens. Set to `False` to count only successful calls. + +**`evaluation_name`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +Optional override for the reported evaluation name. + +Returns `EvaluationReason` with a `bool` value. + +### MaxModelRequests + +**Bases:** `Evaluator[object, object, object]` + +Assert that the agent made at most `max_requests` model (chat) requests. + +Prefers the `requests` value from `ctx.metrics` when available, otherwise counts LLM request spans in the span tree directly (both use the same criteria, so the two sources agree whenever both are populated). + +#### Constructor Parameters + +**`max_requests`** : [`int`](https://docs.python.org/3/library/functions.html#int) + +Maximum allowed model requests. + +**`evaluation_name`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +Optional override for the reported evaluation name. + +Returns `EvaluationReason` with a `bool` value. + ### EvaluatorSpec The specification of an evaluator to be run. @@ -29993,12 +30750,45 @@ Type for the output of an evaluator, which can be a scalar, an EvaluationReason, **Default:** `EvaluationScalar | EvaluationReason | Mapping[str, EvaluationScalar | EvaluationReason]` +### TrajectoryOrder + +How to compare the actual tool sequence to `expected_trajectory`. + +- `'exact'`: actual must equal expected (1.0) or not (0.0). +- `'in_order'`: F1 score combining precision and recall of the longest common subsequence. +- `'any_order'`: F1 score combining precision and recall of the multiset intersection (order is ignored, but extra and missing calls both reduce the score). + +**Default:** `Literal['exact', 'in_order', 'any_order']` + +### ArgumentMatchMode + +How to compare actual tool arguments to `expected_arguments`. + +- `'exact'`: actual must deep-equal expected. +- `'subset'`: every key/value in expected must be present (and equal) in actual. + +**Default:** `Literal['exact', 'subset']` + +### ArgumentOccurrence + +Which occurrence of a tool call to inspect when a tool is called multiple times. + +**Default:** `Literal['first', 'last']` + ### GradingOutput **Bases:** `BaseModel` The output of a grading operation. +### GEvalOutput + +**Bases:** `BaseModel` + +The output of a G-Eval grading operation. + +G-Eval asks the judge to emit a short chain-of-thought `reason` followed by an integer `score` in a user-specified range (see [`judge_g_eval`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.llm_as_a_judge.judge_g_eval)). + ### judge\_output `@async` @@ -30101,6 +30891,64 @@ This model is used if `None` is passed to the `model` argument of `judge_output` [`None`](https://docs.python.org/3/library/constants.html#None) +### judge\_g\_eval + +`@async` + +```python +def judge_g_eval( + output: Any, + criteria: str, + evaluation_steps: Sequence[str], + score_range: tuple[int, int] = (1, 5), + inputs: Any | None = None, + model: models.Model | models.KnownModelName | str | None = None, + model_settings: ModelSettings | None = None, +) -> GEvalOutput +``` + +Judge an output using a G-Eval style chain-of-thought prompt. + +This is a simplified implementation of G-Eval (Liu et al., 2023, "G-Eval: NLG Evaluation using GPT-4 with Better Human Alignment"). The original paper computes an expectation over the distribution of score tokens using log-probs. We skip that step and simply ask the model for a direct integer score. This keeps the evaluator provider-agnostic at the cost of some correlation with human judgments. + +#### Returns + +`GEvalOutput` -- A [`GEvalOutput`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.llm_as_a_judge.GEvalOutput) containing `GEvalOutput` -- the judge's reasoning and integer score. + +#### Parameters + +**`output`** : [`Any`](https://docs.python.org/3/library/typing.html#typing.Any) + +The output being evaluated. + +**`criteria`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) + +The aspect being evaluated (e.g. "coherence", "fluency"). + +**`evaluation_steps`** : [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] + +Explicit chain-of-thought steps the judge should follow. + +**`score_range`** : [`tuple`](https://docs.python.org/3/library/stdtypes.html#tuple)\[[`int`](https://docs.python.org/3/library/functions.html#int), [`int`](https://docs.python.org/3/library/functions.html#int)\] _Default:_ `(1, 5)` + +Inclusive `(min, max)` integer score range. + +**`inputs`** : [`Any`](https://docs.python.org/3/library/typing.html#typing.Any) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +Optional inputs/context to show alongside the output. + +**`model`** : [`models.Model`](/docs/ai/api/models/base/#pydantic_ai.models.Model) | [`models.KnownModelName`](/docs/ai/api/models/base/#pydantic_ai.models.KnownModelName) | [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +The model to use. If not specified, the default judge model is used. + +**`model_settings`** : [`ModelSettings`](/docs/ai/api/pydantic-ai/settings/#pydantic_ai.settings.ModelSettings) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +Optional model settings. + +#### Raises + +- `ValueError` -- If `score_range` is invalid, `evaluation_steps` is empty, or the judge returns a score outside the range. + --- # [pydantic_evals.generation](https://pydantic.dev/docs/ai/api/pydantic_evals/generation/) @@ -31077,6 +31925,32 @@ Optional config override. Defaults to the global `DEFAULT_CONFIG`. # pydantic\_evals.otel +### SpanTreeRecordingError + +**Bases:** [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception) + +An exception that is used to provide the reason why a SpanTree was not recorded by `context_subtree`. + +This may be due to missing dependencies, a tracer provider not having been set, or a custom TracerProvider that does not support `add_span_processor`. + +#### Methods + +##### \_\_get\_pydantic\_core\_schema\_\_ + +`@classmethod` + +```python +def __get_pydantic_core_schema__(cls, _: Any, __: Any) -> core_schema.CoreSchema +``` + +Pydantic core schema to allow `SpanTreeRecordingError` to be (de)serialized. + +Only the human-readable `message` is preserved by design: the exception's `__context__`, `__cause__`, and traceback (e.g. the underlying `ImportError` chained in `context_subtree`) are dropped on serialization and not reconstructed on the way back. + +###### Returns + +`core_schema.CoreSchema` + ### SpanQuery **Bases:** [`TypedDict`](https://docs.python.org/3/library/typing.html#typing.TypedDict) @@ -31099,6 +31973,12 @@ A node in the span tree; provides references to parents/children for easy traver #### Attributes +##### status + +The span's status; `'error'` if the operation the span represents raised an exception. + +**Type:** `SpanStatus` **Default:** `'unset'` + ##### duration Return the span's duration as a timedelta, or None if start/end not set. @@ -31375,6 +32255,12 @@ Return an XML-like string representation of the tree, optionally including child [`str`](https://docs.python.org/3/library/stdtypes.html#str) +### SpanStatus + +The status of a span, mirroring `opentelemetry.trace.StatusCode`. + +**Default:** `Literal['unset', 'ok', 'error']` + --- # [pydantic_evals.reporting](https://pydantic.dev/docs/ai/api/pydantic_evals/reporting/) @@ -33808,17 +34694,17 @@ def as_node(inputs: None = None) -> JoinNode[StateT, DepsT] def as_node(inputs: InputT) -> JoinNode[StateT, DepsT] ``` -Create a step node with bound inputs. +Create a join node with bound inputs. ###### Returns -`JoinNode`\[`StateT`, `DepsT`\] -- A [`StepNode`](/docs/ai/api/pydantic_graph/step/#pydantic_graph.step.StepNode) with this step and the bound inputs +`JoinNode`\[`StateT`, `DepsT`\] -- A [`JoinNode`](/docs/ai/api/pydantic_graph/join/#pydantic_graph.join.JoinNode) with this join and the bound inputs ###### Parameters **`inputs`** : `InputT` | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` -The input data to bind to this step, or None +The input data to bind to this join, or None ### JoinNode @@ -34389,8 +35275,6 @@ Override to set timestamps on all AG-UI events. UI adapter for the Agent-User Interaction (AG-UI) protocol. -[`preserve_file_data`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.preserve_file_data) (inherited from `UIAdapter`, default `False`) gates two behaviors. On the way in, client-submitted [`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile) parts are dropped during `sanitize_messages` unless it is `True`, since the server resolves them with its own credentials; only set it when the frontend is trusted. On the way out and back in, when `True`, agent-generated files and uploaded files are stored as [activity messages](https://docs.ag-ui.com/concepts/messages) during `dump_messages` and restored during `load_messages`, enabling full round-trip fidelity. When `False`, they are dropped. If your AG-UI frontend uses activities, be aware that `pydantic_ai_*` activity types are reserved for internal round-trip use and should be ignored by frontend activity handlers. - #### Attributes ##### ag\_ui\_version @@ -34409,6 +35293,16 @@ Known thresholds: **Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) **Default:** `DEFAULT_AG_UI_VERSION` +##### preserve\_file\_data + +Whether to round-trip `FilePart` and `UploadedFile` through reserved `pydantic_ai_*` [activity messages](https://docs.ag-ui.com/concepts/messages). + +Defaults to `False`. AG-UI has no native representation for agent-generated files ([`FilePart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.FilePart)) or uploaded-file references ([`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile)), so when this is `True` they are serialized as sidecar activity messages on `dump_messages` and reconstructed on `load_messages`. A frontend only completes the round-trip if it echoes these activity messages back on the next request. + +This is a representation setting, not a security one: honoring a reconstructed inbound `UploadedFile` still requires [`allow_uploaded_files`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allow_uploaded_files), which the shared `sanitize_messages` step enforces regardless of this flag. Multimodal tool-return files are unaffected -- they ride inline in `ToolMessage.content`. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `False` + ##### messages Pydantic AI messages from the AG-UI run input. @@ -34497,6 +35391,7 @@ def from_request( manage_system_prompt: Literal['server', 'client'] = 'server', allowed_file_url_schemes: frozenset[str] = frozenset({'http', 'https'}), allowed_file_url_force_download: frozenset[ForceDownloadMode] = frozenset(), + allow_uploaded_files: bool = False, **kwargs: Any, ) -> AGUIAdapter[AgentDepsT, OutputDataT] ``` @@ -34548,11 +35443,16 @@ Note: The round-trip `dump_messages` -> `load_messages` is not fully lossless: - `ToolCallPart.id`, `.provider_name`, `.provider_details` are lost. - `NativeToolCallPart.id`, `.provider_details` are lost (only `.provider_name` survives via the prefixed tool call ID). - `NativeToolReturnPart.provider_details` is lost. +- `tool_kind` is lost when `ag_ui_version < '0.1.11'` (before its `encrypted_value` carrier existed), so typed tool parts reload as their base classes. +- `tool_kind` is not restored on error/denied tool returns (a typed return implies success to its readers), so those reload as plain `ToolReturnPart`. +- A non-`'success'` `outcome` on a (native) tool return survives via the `encrypted_value` carrier from 0.1.11 (`ToolMessage` has no outcome slot), and reloads as `'success'` below that. - `RetryPromptPart` becomes `ToolReturnPart` (or `UserPromptPart`) on reload. - `CachePoint` and `UploadedFile` content items are dropped (unless `preserve_file_data=True`). +- `FileUrl.force_download` is dropped when `ag_ui_version < '0.1.15'` (before typed multimodal content gained a metadata carrier). - `ThinkingPart` is dropped when `ag_ui_version='0.1.10'`. - `FilePart` is silently dropped unless `preserve_file_data=True`. - `UploadedFile` in a multi-item `UserPromptPart` is split into a separate activity message when `preserve_file_data=True`, which reloads as a separate `UserPromptPart`. +- `MultiModalContent` items in `ToolReturnPart`/`NativeToolReturnPart.content` always round-trip, regardless of `preserve_file_data`: the full content (files as base64/URL dicts) is serialized inline into the JSON `ToolMessage.content` and rehydrated on reload via the `ToolReturnContent` discriminator. The same serialization is used for both history (`dump_messages`) and the live event stream (`ToolCallResultEvent.content`), so files survive either round-trip. - Part ordering within a `ModelResponse` may change when text follows tool calls. ###### Returns @@ -34571,7 +35471,7 @@ AG-UI protocol version controlling `ThinkingPart` emission. **`preserve_file_data`** : [`bool`](https://docs.python.org/3/library/functions.html#bool) _Default:_ `False` -Whether to include `FilePart` and `UploadedFile` as `ActivityMessage`. +Whether to include `FilePart` and `UploadedFile` items as `ActivityMessage`s. (Multimodal tool-return files always ride inline in `ToolMessage.content` and are unaffected.) ### DEFAULT\_AG\_UI\_VERSION @@ -35520,15 +36420,17 @@ To opt into a value after auditing your frontend, add it to this set, e.g. `froz **Type:** [`frozenset`](https://docs.python.org/3/library/stdtypes.html#frozenset)\[`ForceDownloadMode`\] **Default:** `frozenset()` -##### preserve\_file\_data +##### allow\_uploaded\_files -Whether to keep [`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile) items from client-submitted messages. +Whether to honor [`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile) references from client-submitted messages. Defaults to `False`. By default, `UploadedFile` items in client-submitted messages are dropped with a warning before the messages are passed to the agent, mirroring how [`allowed_file_url_schemes`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allowed_file_url_schemes) filters [`FileUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.FileUrl) parts. This applies both to uploaded files in user content and to those nested in tool return parts. Like a non-HTTP `FileUrl`, an `UploadedFile` references an object that the model provider fetches using the server-side IAM role or service account, so a client that can supply arbitrary file references can read anything that identity can reach. Uploaded files should therefore only be accepted from trusted frontends. -Set to `True` to keep client-submitted uploaded files after auditing your frontend. Some adapters (e.g. AG-UI) additionally use this flag to round-trip agent-generated files and uploaded files through their protocol-specific message representation; see the adapter for details. +Set to `True` to honor client-submitted uploaded files after auditing your frontend. + +This is a purely inbound, security-oriented setting. It does not affect what the adapter sends _to_ the client: file content the agent produces is always serialized on the way out. **Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `False` @@ -35583,7 +36485,7 @@ def from_request( manage_system_prompt: Literal['server', 'client'] = 'server', allowed_file_url_schemes: frozenset[str] = frozenset({'http', 'https'}), allowed_file_url_force_download: frozenset[ForceDownloadMode] = frozenset(), - preserve_file_data: bool = False, + allow_uploaded_files: bool = False, **kwargs: Any, ) -> Self ``` @@ -35668,15 +36570,13 @@ def sanitize_messages( Strip parts of client-submitted messages that aren't trusted from the client. -Called on the messages produced from the protocol-specific run input before they're passed to the agent. Caller-supplied `message_history` is not passed through this method -- it is trusted as coming from server-side persistence. +Called on the messages produced from the protocol-specific run input before they're passed to the agent. Caller-supplied `message_history` is not passed through this method -- it is trusted as coming from server-side persistence. Use [`sanitize_messages`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.sanitize_messages) before passing `message_history` that came from an untrusted client. -Currently strips: +Delegates to [`sanitize_messages`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.sanitize_messages) -- see its docstring for the full list of what's stripped -- with these adapter-specific settings: -- [`SystemPromptPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.SystemPromptPart)s when [`manage_system_prompt`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.manage_system_prompt) is `'server'`. The agent's configured `system_prompt` is reinjected by [`ReinjectSystemPrompt`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.ReinjectSystemPrompt) on the next model request. If stripping leaves a `ModelRequest` with no parts, the request is dropped from history entirely. -- [`FileUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.FileUrl) parts whose URL scheme is not in [`allowed_file_url_schemes`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allowed_file_url_schemes). Non-HTTP schemes like `s3://` or `gs://` cause the model provider to fetch the object using the server-side IAM role, so they should only be accepted from trusted frontends. -- [`FileUrl.force_download`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.FileUrl.force_download) values other than `False` that aren't in [`allowed_file_url_force_download`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allowed_file_url_force_download) on kept parts. By default both `True` and `'allow-local'` are reset to `False`, since `'allow-local'` opts the URL out of the SSRF private-IP block and `True` makes the server fetch the file itself -- neither is safe to honor from untrusted client input. This applies to file URLs in user content and to those nested in tool return parts. -- [`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile) items unless [`preserve_file_data`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.preserve_file_data) is `True`. Like a non-HTTP `FileUrl`, an `UploadedFile` references an object the model provider fetches using the server-side IAM role, so it should only be accepted from trusted frontends. This applies both to uploaded files in user content and to those nested in tool return parts. -- [`ToolCallPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ToolCallPart) and [`NativeToolCallPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.NativeToolCallPart) entries at the end of the history that don't have a matching entry in `deferred_tool_results`. Tool calls are produced by the model on the server side, so an unresolved tool call at the end of client-supplied history doesn't correspond to a paused agent run and shouldn't be executed. Tool calls that correspond to a resolution in `deferred_tool_results` are preserved so that human-in-the-loop resumption continues to work. If stripping leaves the final response with no parts, the response is dropped from history entirely. +- [`SystemPromptPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.SystemPromptPart)s are stripped only when [`manage_system_prompt`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.manage_system_prompt) is `'server'`, and the agent's configured `system_prompt` is reinjected by [`ReinjectSystemPrompt`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.ReinjectSystemPrompt) on the next model request. +- File URL schemes and `force_download` values are checked against [`allowed_file_url_schemes`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allowed_file_url_schemes) and [`allowed_file_url_force_download`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allowed_file_url_force_download), and [`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile)s are kept only when [`allow_uploaded_files`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allow_uploaded_files) is `True`. +- Tool calls at the end of the history are kept when they correspond to a resolution in `deferred_tool_results`, so human-in-the-loop resumption continues to work. ###### Returns @@ -35950,7 +36850,7 @@ def dispatch_request( manage_system_prompt: Literal['server', 'client'] = 'server', allowed_file_url_schemes: frozenset[str] = frozenset({'http', 'https'}), allowed_file_url_force_download: frozenset[ForceDownloadMode] = frozenset(), - preserve_file_data: bool = False, + allow_uploaded_files: bool = False, **kwargs: Any, ) -> Response ``` @@ -36045,9 +36945,9 @@ URL schemes allowed for file URL parts from the client. See [`UIAdapter.allowed_ Additional `FileUrl.force_download` values allowed on file URL parts from the client (beyond `False`, which is always allowed). See [`UIAdapter.allowed_file_url_force_download`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allowed_file_url_force_download). -**`preserve_file_data`** : [`bool`](https://docs.python.org/3/library/functions.html#bool) _Default:_ `False` +**`allow_uploaded_files`** : [`bool`](https://docs.python.org/3/library/functions.html#bool) _Default:_ `False` -Whether to keep `UploadedFile` items from client-submitted messages. See [`UIAdapter.preserve_file_data`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.preserve_file_data). +Whether to honor `UploadedFile` references from client-submitted messages. See [`UIAdapter.allow_uploaded_files`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allow_uploaded_files). **`**kwargs`** : [`Any`](https://docs.python.org/3/library/typing.html#typing.Any) _Default:_ `{}` @@ -36093,9 +36993,9 @@ UI event stream transformer for the Vercel AI protocol. ##### sdk\_version -Vercel AI SDK version to target. Setting to 6 enables tool approval streaming. +Vercel AI SDK version to target. Setting to 6 enables tool approval streaming; 7 emits the same wire as 6 (v7's data-stream protocol equals v6's). -**Type:** [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\[5, 6\] **Default:** `5` +**Type:** [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\[5, 6, 7\] **Default:** `5` ##### server\_message\_id @@ -36115,9 +37015,9 @@ UI adapter for the Vercel AI protocol. Vercel AI SDK version to target. Default is 5 for backwards compatibility. -Setting `sdk_version=6` enables tool approval streaming for human-in-the-loop workflows. +Setting `sdk_version=6` enables tool approval streaming for human-in-the-loop workflows. `sdk_version=7` emits the same wire as 6 (v7's data-stream protocol equals v6's); it is accepted so the value reflects the client's real SDK major and reserves it for future v7-only chunks. -**Type:** [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\[5, 6\] **Default:** `5` +**Type:** [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\[5, 6, 7\] **Default:** `5` ##### server\_message\_id @@ -36171,18 +37071,21 @@ def from_request( request: Request, *, agent: AbstractAgent[AgentDepsT, OutputDataT], - sdk_version: Literal[5, 6] = 5, + sdk_version: Literal[5, 6, 7] = 5, server_message_id: str | None = None, manage_system_prompt: Literal['server', 'client'] = 'server', allowed_file_url_schemes: frozenset[str] = frozenset({'http', 'https'}), allowed_file_url_force_download: frozenset[ForceDownloadMode] = frozenset(), - preserve_file_data: bool = False, + allow_uploaded_files: bool = False, + preserve_file_data: bool | None = None, **kwargs: Any, ) -> VercelAIAdapter[AgentDepsT, OutputDataT] ``` Extends [`from_request`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.from_request) with Vercel AI-specific parameters. +`preserve_file_data` is a deprecated alias for `allow_uploaded_files`. + ###### Returns `VercelAIAdapter`\[`AgentDepsT`, `OutputDataT`\] @@ -36199,7 +37102,7 @@ def dispatch_request( request: Request, *, agent: AbstractAgent[DispatchDepsT, DispatchOutputDataT], - sdk_version: Literal[5, 6] = 5, + sdk_version: Literal[5, 6, 7] = 5, server_message_id: str | None = None, message_history: Sequence[ModelMessage] | None = None, deferred_tool_results: DeferredToolResults | None = None, @@ -36219,13 +37122,16 @@ def dispatch_request( manage_system_prompt: Literal['server', 'client'] = 'server', allowed_file_url_schemes: frozenset[str] = frozenset({'http', 'https'}), allowed_file_url_force_download: frozenset[ForceDownloadMode] = frozenset(), - preserve_file_data: bool = False, + allow_uploaded_files: bool = False, + preserve_file_data: bool | None = None, **kwargs: Any, ) -> Response ``` Extends [`dispatch_request`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.dispatch_request) with Vercel AI-specific parameters. +`preserve_file_data` is a deprecated alias for `allow_uploaded_files`. + ###### Returns `Response` @@ -36268,7 +37174,7 @@ def dump_messages( messages: Sequence[ModelMessage], *, generate_message_id: Callable[[ModelRequest | ModelResponse, Literal['system', 'user', 'assistant'], int], str] | None = None, - sdk_version: Literal[5, 6] = 5, + sdk_version: Literal[5, 6, 7] = 5, ) -> list[UIMessage] ``` @@ -36290,9 +37196,9 @@ A sequence of ModelMessage objects to convert Optional custom function to generate message IDs. If provided, it receives the message, the role ('system', 'user', or 'assistant'), and the message index (incremented per UIMessage appended), and should return a unique string ID. If not provided, uses `provider_response_id` for responses, run\_id-based IDs for messages with run\_id, or a deterministic UUID5 fallback. -**`sdk_version`** : [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\[5, 6\] _Default:_ `5` +**`sdk_version`** : [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\[5, 6, 7\] _Default:_ `5` -Vercel AI SDK version to target. Defaults to 5 for backwards compatibility. Set to 6 to emit tool approval parts for deferred tool calls. +Vercel AI SDK version to target: 5, 6, or 7. Defaults to 5 for backwards compatibility. Set to 6 to emit tool approval parts for deferred tool calls; 7 emits identically to 6 (v7's data-stream protocol equals v6's). Vercel AI request types (UI messages). @@ -36804,12 +37710,6 @@ Reason why the model finished generating. **Default:** `Literal['stop', 'length', 'content-filter', 'tool-calls', 'error', 'other'] | None` ---- - -# [](https://pydantic.dev/docs/ai/builtin-tools/) - - - --- # [Agents](https://pydantic.dev/docs/ai/core-concepts/agent/) @@ -37468,7 +38368,9 @@ try: ) except UsageLimitExceeded as e: print(e) - #> Exceeded the output_tokens_limit of 10 (output_tokens=32) + """ + Exceeded the output_tokens_limit of 10 (output_tokens=32). Consider raising the limit, or see the docs on usage limits for budget-aware patterns: https://ai.pydantic.dev/agent/#usage-limits + """ ``` Restricting the number of requests can be useful in preventing infinite loops or excessive tool calling: @@ -37506,7 +38408,9 @@ try: ) except UsageLimitExceeded as e: print(e) - #> The next request would exceed the request_limit of 3 + """ + The next request would exceed the request_limit of 3. Consider raising the limit, or see the docs on usage limits for budget-aware patterns: https://ai.pydantic.dev/agent/#usage-limits + """ ``` This tool has the ability to retry 5 times before erroring, simulating a tool that might get stuck in a loop. @@ -37533,7 +38437,9 @@ try: agent.run_sync('Please call the tool twice', usage_limits=UsageLimits(tool_calls_limit=1)) except UsageLimitExceeded as e: print(e) - #> The next tool call(s) would exceed the tool_calls_limit of 1 (tool_calls=2). + """ + The next tool call(s) would exceed the tool_calls_limit of 1 (tool_calls=2). Consider raising the limit, or see the docs on usage limits for budget-aware patterns: https://ai.pydantic.dev/agent/#usage-limits + """ ``` Note @@ -37541,9 +38447,11 @@ Note - Usage limits are especially relevant if you've registered many tools. Use `request_limit` to bound the number of model turns, and `tool_calls_limit` to cap the number of successful tool executions within a run. - The `tool_calls_limit` is checked before executing tool calls. If the model returns parallel tool calls that would exceed the limit, no tools will be executed. +Tools and [capabilities](/docs/ai/core-concepts/capabilities) can read the run's limits from [`ctx.usage_limits`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.usage_limits) (alongside [`ctx.usage`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.usage) for usage so far), so a budget-aware tool or capability can disclose or adapt to the remaining budget without being configured with a duplicate copy of the limits. It reflects what the run is already enforcing and is read-only by convention. + #### Model (Run) Settings -Pydantic AI offers a [`settings.ModelSettings`](/docs/ai/api/pydantic-ai/settings/#pydantic_ai.settings.ModelSettings) structure to help you fine tune your requests. This structure allows you to configure common parameters that influence the model's behavior, such as `temperature`, `max_tokens`, `timeout`, and more. +Pydantic AI offers a [`settings.ModelSettings`](/docs/ai/api/pydantic-ai/settings/#pydantic_ai.settings.ModelSettings) structure to help you fine tune your requests. This structure allows you to configure common parameters that influence the model's behavior, such as `temperature`, `max_tokens`, `top_k`, `timeout`, and more. There are three ways to apply these settings, with a clear precedence order: @@ -38088,7 +38996,10 @@ with capture_run_messages() as messages: # (2) result = agent.run_sync('Please get me the volume of a box with size 6.') except UnexpectedModelBehavior as e: print('An error occurred:', e) - #> An error occurred: Tool 'calc_volume' exceeded max retries count of 1 + """ + An error occurred: + Tool 'calc_volume' exceeded max retries count of 1. Consider raising the retry limit, or see the docs on tool retries: https://ai.pydantic.dev/tools-advanced/#tool-retries + """ print('cause:', repr(e.__cause__)) #> cause: ModelRetry('Please try again.') print('messages:', messages) @@ -38161,7 +39072,7 @@ _(This example is complete, it can be run "as is")_ When a run is cut short by an exception while streaming, an exception inside a tool, or external cancellation, Pydantic AI still captures partial state where it can. Partial [`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse) and [`ModelRequest`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelRequest) messages have `state='interrupted'` so persistence layers and UIs can distinguish them from complete messages. -For model responses, interrupted messages contain the response parts streamed before the interruption. For model requests, interrupted messages contain the tool results that completed before tool execution stopped. Half-finished tool call parts are not turned into synthetic tool results; only completed tool returns are captured. +For model responses, interrupted messages contain the response parts streamed before the interruption. For model requests, interrupted messages contain the tool results that completed before tool execution stopped. The captured messages reflect exactly what happened -- half-finished tool call parts are not turned into synthetic tool results at capture time. When an interrupted history is passed back into a run, it is [repaired automatically](/docs/ai/core-concepts/message-history#making-histories-provider-valid) before the next model request. In this example, `get_volume` completes before `get_mass` raises, so the interrupted request contains the completed `get_volume` return: @@ -38220,6 +39131,8 @@ Note If you call [`run`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run), [`run_sync`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run_sync), or [`run_stream`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run_stream) more than once within a single `capture_run_messages` context, `messages` will represent the messages exchanged during the first call only. +`capture_run_messages` contexts can be nested: each context captures the runs for which it is the innermost active context. A run started inside a nested context is captured by that nested context, not by any enclosing one. This means you can wrap a nested agent run (for example inside a tool that calls another agent) in its own `capture_run_messages` to inspect that inner run's messages independently. + ## Agent Specs Agents can also be defined declaratively in YAML or JSON using [agent specs](/docs/ai/core-concepts/agent-spec). This separates agent configuration from application code: @@ -38439,7 +39352,7 @@ Retry budgets for [tools](/docs/ai/tools-toolsets/tools-advanced#tool-retries) a `EndStrategy` -When to stop (`'early'` or `'exhaustive'`) +When to stop (`'early'`, `'graceful'`, or `'exhaustive'`) `tool_timeout` @@ -38572,10 +39485,10 @@ The following capabilities are deferred and can be loaded using the `load_capabi - refunds: Use for refund eligibility, refund status, or processing a refund. ``` -The model does not receive the refund instructions, and `refund_status` is not callable yet. Depending on the active model, Pydantic AI may also send provider/tool-search plumbing to preserve the hidden state; that plumbing does not expose the refund tool until the capability is loaded. The exchange unfolds across model requests within a single `agent.run_sync` call: +The model does not receive the refund instructions or the `refund_status` tool definition yet, so it has no reason to call the tool. Depending on the active model, Pydantic AI may also send provider/tool-search plumbing to preserve the hidden state; that plumbing does not expose the refund tool definition until the capability is loaded. The exchange unfolds across model requests within a single `agent.run_sync` call: 1. **Request 1.** The model sees the catalog above and the user's prompt. It calls the `load_capability` tool with `id='refunds'`. -2. **Load.** Pydantic AI returns the capability's instructions -- _"Always confirm the order ID before issuing a refund."_ -- as the tool result, and registers `refund_status` for the next request. +2. **Load.** Pydantic AI returns the capability's instructions -- _"Always confirm the order ID before issuing a refund."_ -- as the tool result and exposes the `refund_status` definition on the next request. 3. **Request 2.** The model now sees those instructions in history and `refund_status` in its tool list. It calls `refund_status(order_id='ABC-123')` and answers the user from the result. Already-loaded capabilities stay loaded for the rest of the run -- the model never needs to re-open one. @@ -38692,6 +39605,7 @@ Several [`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunCont - `ctx.capability_loaded` -- only meaningful while Pydantic AI is running a capability-owned hook or callback. It is scoped to that capability; deferred hooks and callbacks are skipped until this value would be true. - `ctx.discovered_tool_names` -- deferred function tools revealed by tool search. This is tool-level discovery, separate from capability-level loading. - `ctx.available_tool_names` -- function tool names currently known as available: always-visible tools from the current step's assembled tool manager plus tool-search discoveries reconstructed from history. Early hooks such as `before_run` may see only the history-derived discovered names, or an empty set if none exist yet, before tool definitions have been prepared. See [Hook ordering](/docs/ai/core-concepts/hooks#hook-ordering) for how hook timing affects what is populated. +- `ctx.usage_limits` -- the [`UsageLimits`](/docs/ai/api/pydantic-ai/usage/#pydantic_ai.usage.UsageLimits) the run is enforcing (defaulting to `UsageLimits()` when none were passed, so it's only `None` outside of a run), alongside `ctx.usage` for the usage so far. A capability can read the run's limits to disclose or adapt to the remaining budget (e.g. budget disclosure) without being configured with a duplicate copy. Treat it as read-only: it's the live object the run enforces against, so mutating a field would change what the run enforces on subsequent requests. Loading a capability updates the capability state immediately, but the loaded bundle's function tools, native tools, and model settings take effect on the next model request. @@ -40827,7 +41741,7 @@ print(f'Output: {result.output}') ## Pydantic AI Harness -[**Pydantic AI Harness**](/docs/ai/harness/overview) is the official capability library for Pydantic AI -- standalone capabilities like memory, guardrails, context management, and [code mode](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/code_mode) live there rather than in core. See [What goes where?](/docs/ai/harness/overview#what-goes-where) for the full breakdown, or jump to the [capability matrix](https://github.com/pydantic/pydantic-ai-harness#capability-matrix). +[**Pydantic AI Harness**](https://pydantic.dev/docs/ai/harness/) is the official capability library for Pydantic AI -- standalone capabilities like memory, guardrails, context management, and [code mode](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/code_mode) live there rather than in core. See [What goes where?](https://pydantic.dev/docs/ai/harness/#what-goes-where) for the full breakdown, or jump to the [capability matrix](https://github.com/pydantic/pydantic-ai-harness#capability-matrix). ## Third-party capabilities @@ -40869,6 +41783,12 @@ Capabilities that implement [Agent Skills](https://agentskills.io) support help - [`pydantic-ai-skills`](https://github.com/DougTrajano/pydantic-ai-skills) - `SkillsCapability` implements Agent Skills support with progressive disclosure (load skills on-demand to reduce tokens). Supports filesystem and programmatic skills; compatible with [agentskills.io](https://agentskills.io). +### Data & Analytics + +Capabilities for querying and analyzing structured data help agents answer questions over files and databases: + +- [`pydantic-ai-chdb`](https://github.com/chdb-io/pydantic-ai-chdb) - `ChDBCapability` gives agents analytical SQL over local files (Parquet/CSV/JSON), object storage, and remote databases with [chDB](https://clickhouse.com/docs/en/chdb), the in-process ClickHouse engine -- the engine itself needs no server or connection string to run (remote sources are reached via ClickHouse table functions, which take their own credentials). Registers `run_select_query` (read-only ClickHouse SQL with parameter binding), `list_databases`, `list_tables`, `describe_table`, `get_sample_data`, `list_functions`, and `attach_file` (opt-in writable sessions) tools plus schema-first instructions. Sessions default to the engine-level `readonly=2` setting with capped results, and typed engine errors are mapped to [`ModelRetry`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.ModelRetry) so the model can correct its queries. Works with [agent specs](/docs/ai/core-concepts/agent-spec) out of the box, so it can be loaded via [`from_spec`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.AbstractCapability.from_spec) / [`Agent.from_spec`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.from_spec). Also available as a lower-level [toolset](/docs/ai/tools-toolsets/toolsets) via [`ChDBCapability(...).get_toolset()`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.AbstractCapability.get_toolset). + To add your package to this page, open a pull request. ## Publishing capabilities @@ -41526,42 +42446,36 @@ Both sync and async hook functions are accepted. Sync functions are automaticall ### On-demand hooks -[`Hooks`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.Hooks) is a capability, so it can be loaded on demand just like any other capability: +[`Hooks`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.Hooks) is a capability, so it can be loaded on demand just like any other capability. This is useful for optional, user-requested behavior such as verbose request logging: deferred\_hooks\_capability.py ```python -from pydantic_ai import Agent, RunContext, ToolDefinition -from pydantic_ai.capabilities import Hooks, ValidatedToolArgs -from pydantic_ai.messages import ToolCallPart +from pydantic_ai import Agent, ModelRequestContext, RunContext +from pydantic_ai.capabilities import Hooks -approval_hooks = Hooks( - id='approval-hooks', - description='Use when a workflow needs approval before destructive actions.', +request_logging_hooks = Hooks( + id='request-logging', + description='Use when the user asks for verbose request diagnostics.', defer_loading=True, ) -@approval_hooks.on.before_tool_execute -async def require_approval( +@request_logging_hooks.on.before_model_request +async def log_request( ctx: RunContext[None], - *, - call: ToolCallPart, - tool_def: ToolDefinition, - args: ValidatedToolArgs, -) -> ValidatedToolArgs: - # Runs only after the model loads `approval-hooks`. - return args + request_context: ModelRequestContext, +) -> ModelRequestContext: + print(f'Model request at step {ctx.run_step}: {len(request_context.messages)} messages') + return request_context -agent = Agent('openai-responses:gpt-5.4', capabilities=[approval_hooks]) +agent = Agent('openai-responses:gpt-5.4', capabilities=[request_logging_hooks]) ``` -You do not need to guard hooks owned by a deferred `Hooks` instance with `ctx.capability_loaded`; Pydantic AI skips those hooks until the model calls the `load_capability` tool for that capability. Once the hook runs, `ctx.capability_loaded` is true for that hook's owning capability. To check a different capability, inspect `ctx.loaded_capability_ids` or `ctx.available_capability_ids`. +Pydantic AI skips hooks owned by a deferred `Hooks` instance until its capability is loaded. -If a hook must enforce a rule before a workflow is loaded, keep that hook in an always-available capability and inspect `ctx.loaded_capability_ids`; an on-demand hook cannot run before the model loads its own capability. - -The run-scoped hooks -- `before_run` and `wrap_run` -- are bound at the start of the run, so a capability the model loads mid-run won't get them for that run; they only fire when the capability is already loaded at the start (for example after resuming from message history). The capability's per-step hooks (node, model-request, tool, output) fire from the next step onwards once it has loaded, and `after_run` fires at the end of the run if it was loaded at any point during it. +Use on-demand hooks for optional behavior that only applies after the capability is loaded. For human-in-the-loop tool approval, pass [`requires_approval=True`](/docs/ai/tools-toolsets/deferred-tools#human-in-the-loop-tool-approval) when registering a tool, raise [`ApprovalRequired`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.ApprovalRequired) for conditional approval, or wrap a toolset with [`ApprovalRequiredToolset`](/docs/ai/api/pydantic-ai/toolsets/#pydantic_ai.toolsets.ApprovalRequiredToolset). ## Hook types @@ -41673,6 +42587,10 @@ Model request hooks fire around each LLM call. `ModelRequestContext` bundles `mo To skip the model call entirely, raise [`SkipModelRequest(response)`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.SkipModelRequest) from `before_model_request` or `model_request` (wrap). +Note + +These hooks fire **once per model turn**, even when a provider pauses mid-turn (Anthropic `pause_turn`) or returns a background response (OpenAI background mode) and the agent transparently continues it. `before_model_request` runs before the turn starts, `wrap_model_request` wraps the whole turn including any continuations, and `after_model_request` receives the single completed [`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse). + ### Tool validation hooks `hooks.on.` @@ -42368,6 +43286,23 @@ print(result2.all_messages()) _(This example is complete, it can be run "as is")_ +### Making histories provider-valid + +Model providers reject a request whose message history has broken tool-call/tool-result pairing -- a tool call with no result, or a result with no call. A run that is cancelled or crashes partway through can leave the history in exactly this state, and so can a hand-built, truncated, or context-evicted history. You don't need to clean these up yourself: before each model request, Pydantic AI repairs the history it was given so the provider accepts it. + +The guiding rule is to massage the history into a shape the provider accepts without ever discarding something you meant to send. Repairs only **add** synthesized parts or **remove** parts that are fundamentally unsendable (no provider could accept them); nothing meaningful is silently dropped. Concretely, before each request Pydantic AI: + +- **Adds** a synthesized [`ToolReturnPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ToolReturnPart) for a tool call that has no result, telling the model the call was interrupted before a result was produced. It has [`outcome='interrupted'`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.BaseToolReturnPart.outcome) -- a neutral outcome that (unlike `'failed'`) is not surfaced as a provider error -- and carries `{'pydantic_ai_synthesized_tool_return': True}` in its [`metadata`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.BaseToolReturnPart.metadata) so your code can tell it apart from real tool results. This also covers a call whose arguments were cut off mid-stream: the call is kept as-is and closed out the same way. +- **Removes** an orphaned tool result -- a [`ToolReturnPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ToolReturnPart) or [`RetryPromptPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.RetryPromptPart) whose tool call is absent from the history (including a result placed before its call). If this empties an interior [`ModelRequest`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelRequest) the request is removed; if it empties the last message, an empty request is kept so the history still ends on a `ModelRequest`. + +After the invalid parts are handled, consecutive compatible messages are **merged** into one (two adjacent [`ModelRequest`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelRequest)s become a single turn, with tool results ordered ahead of user parts). This changes message boundaries but preserves all content, so processed history you inspect afterwards may have fewer messages than you passed in. + +The repair is deterministic and idempotent: repairing the same history always produces the same output, running a repaired history through another run leaves it untouched, and synthesized parts contain no wall-clock data, so reuse doesn't invalidate provider prompt caches. + +Tool calls that can still receive a real result are left alone: when the history ends on a `ModelResponse` with tool calls, running without a new `user_prompt` executes them, and [deferred tool calls](/docs/ai/tools-toolsets/deferred-tools) are matched to their `deferred_tool_results` -- including when a 'complete' `ModelRequest` with the already-executed results follows the response. Repair of that live frontier only happens when the interruption is evident: a final response with [`state='interrupted'`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse.state) or a trailing request with [`state='interrupted'`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelRequest.state) (e.g. from a [cancelled stream](/docs/ai/core-concepts/output#cancelling-streams) or a crash during tool execution) whose tool calls will never be executed. + +This pipeline handles regular, locally-executed tool calls only. Builtin (server-side) tool parts -- produced and resulted by the provider inline -- are left untouched and repaired by each model's own serializer instead. Some other provider-invalid shapes are also out of scope and may be rejected: duplicate tool results for one call, and provider-specific ordering rules beyond call/result pairing. + ### Correlating runs with `conversation_id` Each `ModelRequest` and `ModelResponse` carries two identifiers: @@ -42467,6 +43402,29 @@ You can now continue the conversation with history `same_history_as_step_1` desp _(This example is complete, it can be run "as is")_ +### Loading untrusted history + +The `message_history` parameter is trusted server-side state. If you load history that came from a browser request or another untrusted boundary, sanitize it before passing it to the agent. + +[`sanitize_messages`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.sanitize_messages) applies the same default message sanitization used by the [UI adapters](/docs/ai/integrations/ui/overview): it strips client-supplied system prompts, drops non-HTTP file URL schemes, resets non-allowlisted [`FileUrl.force_download`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.FileUrl.force_download) values to `False`, drops uploaded file references, and removes unresolved tool calls at the end of the history. + +sanitize untrusted message history + +```python +from pydantic_ai import Agent, ModelMessagesTypeAdapter +from pydantic_ai.messages import sanitize_messages + +agent = Agent('openai:gpt-5.2', instructions='Be a helpful assistant.') + +# `request_json` is the body submitted by an untrusted client. +loaded_history = ModelMessagesTypeAdapter.validate_python(request_json['message_history']) +message_history = sanitize_messages(loaded_history) + +result = agent.run_sync('Tell me a different joke.', message_history=message_history) +``` + +Each sanitization can be turned off individually when the corresponding parts were created by trusted server-side code: pass `strip_system_prompts=False`, add schemes to `allowed_file_url_schemes`, add values to `allowed_file_url_force_download`, or set `allow_uploaded_files=True`. See [file URL input security](/docs/ai/advanced-features/input#user-side-download-vs-direct-file-url) for the file input trust model. + ## Other ways of using messages Since messages are defined by simple dataclasses, you can manually create and manipulate, e.g. for testing. @@ -42549,6 +43507,51 @@ print(result2.all_messages()) """ ``` +_(This example is complete, it can be run "as is")_ + +## Sharing messages between agents + +The same `message_history` parameter also works when the next run uses a different [`Agent`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent). This is useful for [programmatic agent hand-off](/docs/ai/guides/multi-agent-applications#programmatic-agent-hand-off), where your application runs one agent, then gives another agent the conversation so far as context. + +sharing\_messages\_between\_agents.py + +```python +from pydantic_ai import Agent + +biography_agent = Agent( + 'openai:gpt-5.2', + instructions='Answer biographical questions concisely.', +) + +science_agent = Agent( + 'anthropic:claude-sonnet-4-6', + instructions='Answer science questions for a general audience.', +) + +biography_result = biography_agent.run_sync('Who was Albert Einstein?') +print(biography_result.output) +#> Albert Einstein was a German-born theoretical physicist. + +science_result = science_agent.run_sync( + 'What was his most famous equation?', + message_history=biography_result.new_messages(), +) +print(science_result.output) +#> Albert Einstein's most famous equation is (E = mc^2). +``` + +_(This example is complete, it can be run "as is")_ + +Instructions, system prompts, and tools + +When you pass `message_history` to another agent, previous [`ModelRequest`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelRequest) messages still contain the instructions used by the originating agent, but those instructions are not sent to the model again. The receiving agent uses its own `instructions`; see [Instructions](/docs/ai/core-concepts/agent#instructions) for how this differs from [system prompts](/docs/ai/core-concepts/agent#system-prompts) when `message_history` is provided. + +`system_prompt` is different: system prompt parts are part of the message history. If the receiving agent has its own `system_prompt` and you need to ensure it is present when reusing history, see [`ReinjectSystemPrompt`](/docs/ai/core-concepts/capabilities#reinjectsystemprompt). Use `replace_existing=True` when a system prompt from another agent should not remain authoritative. + +Tool call and tool return parts also remain in the history. Prefer sharing history between agents that can understand the same tool context, or pass only the messages that make sense for the receiving agent. + +For more complex multi-agent patterns, see the [multi-agent applications](/docs/ai/guides/multi-agent-applications) documentation. + ## Injecting messages mid-run Tools, capability hooks, and external code driving an agent run can inject extra content into the conversation mid-run with [`RunContext.enqueue`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.enqueue) (when a `RunContext` is in scope, e.g. inside a tool or capability hook) or [`AgentRun.enqueue`](/docs/ai/api/pydantic-ai/run/#pydantic_ai.run.AgentRun.enqueue) (from external code driving [`agent.iter()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.iter)). Use this when something happens during a run that the agent should know about -- a tool wants to add follow-up context, an external event needs to _steer_ the agent's plan, or background work needs to reach the agent when it completes. @@ -43248,67 +44251,9 @@ If we were passing just `Fruit` and `Vehicle` without custom tool names, we coul _(This example is complete, it can be run "as is")_ -##### Parallel Output Tool Calls - -An [output tool](#tool-output) call is what ends a run and produces its final result. When a model emits one in the _same_ response as other tool calls, the agent's [`end_strategy`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.end_strategy) decides what happens to the rest. Most agents never need to think about this, since most responses don't mix an output tool with other tools -- but when one does, `end_strategy` controls how those calls run and which one becomes the final result. - -Priority of output and deferred tools in streaming methods - -The [`run_stream()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run_stream) and [`run_stream_sync()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run_stream_sync) methods will consider the first output that matches the [output type](/docs/ai/core-concepts/output#structured-output) (which could be text, an [output tool](/docs/ai/core-concepts/output#tool-output) call, or a [deferred](/docs/ai/tools-toolsets/deferred-tools) tool call) to be the final output of the agent run, even when the model generates (additional) tool calls after this "final" output. - -This means that if the model calls deferred tools before output tools when using these methods, the deferred tool calls determine the agent run's final output, while the other [run methods](/docs/ai/core-concepts/agent#running-agents) would have prioritized the tool output. Regardless of `end_strategy`, these methods commit the first matching output the instant it streams, so they behave like `'early'`: that result is locked in, and the [retry-after-tool-failure](#retrying-after-a-tool-failure) behavior below does not apply. - -Strategy - -Output tools - -Function tools -- output succeeded - -Function tools -- every output failed - -`'graceful'` (default) - -Run in emission order; first success is the final result, later output tools skipped - -Run, in parallel where possible, in emission order - -Run; the run continues - -`'early'` - -Run in emission order; the run ends at the first success - -Skipped - -Run; the run continues - -`'exhaustive'` - -All run, in parallel; first valid result by emission order wins - -Run, in parallel - -Run; the run continues - -`'graceful'` is the default and the right choice for most agents: function tools the model requested alongside an output tool still run, so their side effects happen and their results are available to the model if the run continues. Only the first successful output tool is used; later output tools are skipped so their side effects don't fire more than once. - -Choose `'early'` to end the run the instant an output tool succeeds -- function tools requested in the same response are then skipped entirely. This is the fastest option when you never need those function tools to run once you have a result. - -Choose `'exhaustive'` to run every tool, including additional output tools whose results won't be used. This gives the model full visibility that each tool ran, at the cost of executing output-tool side effects that are ultimately discarded. - -When _every_ output tool fails, function tools run and the run continues under all three strategies: there is no result to end on, so the output failures go back to the model as retries and the function tools the model also asked for are run, letting it react to both on the next round. - -##### Retrying after a tool failure - -Under the `'graceful'` and `'exhaustive'` [end strategies](#parallel-output-tool-calls), function tools requested alongside an output tool still run. If one of them raises [`ModelRetry`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.ModelRetry) (or its arguments fail validation) in the same response as a successful output tool, the output result is **not** used as the final result. Instead, the retry is sent back to the model so it can correct the problem, since the output may have been based on the failed tool call. This does not apply under `'early'`, where function tools don't run once an output succeeds, nor when [streaming](#parallel-output-tool-calls), where the first matching output is committed immediately. - -##### Controlling output tool parallelism - -Like function tools, [output tools](#tool-output) run concurrently. Under the `'exhaustive'` [end strategy](#parallel-output-tool-calls), where multiple output tools can run in parallel, you can make an output tool a barrier with [`ToolOutput(sequential=True)`](/docs/ai/api/pydantic-ai/output/#pydantic_ai.output.ToolOutput) -- useful when you want all of a response's function tools to finish before the output tool runs. This is the output-tool counterpart of the `sequential=True` flag for function tools; see [Parallel tool calls & concurrency](/docs/ai/tools-toolsets/tools-advanced#parallel-tool-calls-concurrency) for how the barrier behaves and how to run an entire run's tools serially. - #### Native Output -Native Output mode uses a model's native "Structured Outputs" feature (aka "JSON Schema response format"), where the model is forced to only output text matching the provided JSON schema. Note that this is not supported by all models, and sometimes comes with restrictions. For example, Gemini cannot use tools at the same time as structured output, and attempting to do so will result in an error. +Native Output mode uses a model's native "Structured Outputs" feature (aka "JSON Schema response format"), where the model is forced to only output text matching the provided JSON schema. Note that this is not supported by all models, and sometimes comes with restrictions. For example, [Gemini 3](https://ai.google.dev/gemini-api/docs/structured-output#structured_outputs_with_tools) supports Native Output alongside function and native tools, while earlier Gemini models cannot combine Native Output with function tools. To use this mode, you can wrap the output type(s) in the [`NativeOutput`](/docs/ai/api/pydantic-ai/output/#pydantic_ai.output.NativeOutput) marker class that also lets you specify a `name` and `description` if the name and docstring of the type or function are not sufficient. @@ -43389,6 +44334,72 @@ This could also have been a union: `output_type=Vehicle | Device`. However, as e _(This example is complete, it can be run "as is")_ +#### Tool calls alongside a final result + +A run ends when the model produces a final result. That result usually comes from an [output tool](#tool-output) call, but it can also come from [Native Output](#native-output), [Prompted Output](#prompted-output), plain text, or [image output](#image-output). When the model emits _other_ tool calls in the same response, the agent's [`end_strategy`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.end_strategy) decides what happens to them. Most agents never need to think about this, since most responses don't mix a final result with other tool calls -- but when one does, `end_strategy` controls how those calls run and which one becomes the final result. + +Priority of output and deferred tools in streaming methods + +The [`run_stream()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run_stream) and [`run_stream_sync()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run_stream_sync) methods will consider the first output that matches the [output type](/docs/ai/core-concepts/output#structured-output) (which could be text, an [output tool](/docs/ai/core-concepts/output#tool-output) call, or a [deferred](/docs/ai/tools-toolsets/deferred-tools) tool call) to be the final output of the agent run, even when the model generates (additional) tool calls after this "final" output. + +This means that if the model calls deferred tools before output tools when using these methods, the deferred tool calls determine the agent run's final output, while the other [run methods](/docs/ai/core-concepts/agent#running-agents) would have prioritized the tool output. Regardless of `end_strategy`, these methods commit the first matching output the instant it streams, so they behave like `'early'`: that result is locked in, and the [retry-after-tool-failure](#retrying-after-a-tool-failure) behavior below does not apply. + +Strategy + +Output tools + +Function tools -- output succeeded + +Function tools -- every output failed + +`'graceful'` (default) + +Run in emission order; first success is the final result, later output tools skipped + +Run, in parallel where possible, in emission order + +Run; the run continues + +`'early'` + +Run in emission order; the run ends at the first success + +Skipped + +Run; the run continues + +`'exhaustive'` + +All run, in parallel; first valid result by emission order wins + +Run, in parallel + +Run; the run continues + +`'graceful'` is the default and the right choice for most agents: function tools the model requested alongside an output tool still run, so their side effects happen and their results are available to the model if the run continues. Only the first successful output tool is used; later output tools are skipped so their side effects don't fire more than once. + +Choose `'early'` to end the run the instant an output tool succeeds -- function tools requested in the same response are then skipped entirely. This is the fastest option when you never need those function tools to run once you have a result. + +Choose `'exhaustive'` to run every tool, including additional output tools whose results won't be used. This gives the model full visibility that each tool ran, at the cost of executing output-tool side effects that are ultimately discarded. + +When _every_ output tool fails, function tools run and the run continues under all three strategies: there is no result to end on, so the output failures go back to the model as retries and the function tools the model also asked for are run, letting it react to both on the next round. + +Native, prompted, and image output alongside tool calls + +When your `output_type` uses [Native Output](#native-output), [Prompted Output](#prompted-output), or [image output](#image-output), the final result comes from the text or image the model returns rather than an output tool call. Because the model is asked to produce that output directly, it usually returns it on its own -- but some models occasionally return it _and_ a [function tool](/docs/ai/tools-toolsets/tools#function-tools) call in the same response. Under `'early'`, a _valid_ output ends the run and the co-emitted function tools are skipped, just like a successful output tool; output that fails validation falls through to normal tool execution instead. Under `'graceful'` and `'exhaustive'`, the function tools run and (outside [streaming](#parallel-output-tool-calls)) the run continues, so their results can inform the model's eventual output. + +This applies only to _function_ tools. If the response also contains an [output tool](#tool-output) call or a [deferred tool](/docs/ai/tools-toolsets/deferred-tools) call, that call takes precedence: the output tool still produces the final result, and a deferred call is still surfaced as `DeferredToolRequests`. + +**Plain text output is treated differently.** With `output_type=str` or [`TextOutput`](#text-output) -- including a `str` fallback in a larger output type -- the model is _not_ told that its text will be treated as the final result, so text it emits alongside a tool call is usually narration before it acts ("Let me look that up..."), not a finished answer. Ending the run on it would silently skip the tool, so plain text never preempts a co-emitted function tool: the tool runs under `'early'` exactly as it would under `'graceful'`. (The [streaming methods](#parallel-output-tool-calls) still commit the first text as it streams, as noted above.) + +##### Retrying after a tool failure + +Under the `'graceful'` and `'exhaustive'` [end strategies](#parallel-output-tool-calls), function tools requested alongside an output tool still run. If one of them raises [`ModelRetry`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.ModelRetry) (or its arguments fail validation) in the same response as a successful output tool, the output result is **not** used as the final result. Instead, the retry is sent back to the model so it can correct the problem, since the output may have been based on the failed tool call. This does not apply under `'early'`, where function tools don't run once an output succeeds, nor when [streaming](#parallel-output-tool-calls), where the first matching output is committed immediately. + +##### Controlling output tool parallelism + +Like function tools, [output tools](#tool-output) run concurrently. Under the `'exhaustive'` [end strategy](#parallel-output-tool-calls), where multiple output tools can run in parallel, you can make an output tool a barrier with [`ToolOutput(sequential=True)`](/docs/ai/api/pydantic-ai/output/#pydantic_ai.output.ToolOutput) -- useful when you want all of a response's function tools to finish before the output tool runs. This is the output-tool counterpart of the `sequential=True` flag for function tools; see [Parallel tool calls & concurrency](/docs/ai/tools-toolsets/tools-advanced#parallel-tool-calls-concurrency) for how the barrier behaves and how to run an entire run's tools serially. + ### Custom JSON schema If it's not feasible to define your desired structured output object using a Pydantic `BaseModel`, dataclass, or `TypedDict`, for example when you get a JSON schema from an external source or generate it dynamically, you can use the [`StructuredDict()`](/docs/ai/api/pydantic-ai/output/#pydantic_ai.output.StructuredDict) helper function to generate a `dict[str, Any]` subclass with a JSON schema attached that Pydantic AI will pass to the model. @@ -43621,9 +44632,9 @@ Once upon a time, in a hidden underwater cave, lived a curious axolotl named Pip ## Optional output (allowing `None`) -Some agents perform their work entirely through tool calls and don't need to produce a final output -- for example, an agent that updates a record via a tool and then stops. Certain models (notably [Anthropic](/docs/ai/models/anthropic)) will return an empty response in this case, which by default causes Pydantic AI to retry until the model produces content. +Some agents perform their work entirely through tool calls and don't need to produce a final output -- for example, an agent that updates a record via a tool and then stops. But with `str` in the `output_type` -- including the default -- the model is required to end its final turn with text. If it considers its work finished and has nothing left to say, it will return an empty response, or one containing only [thinking](/docs/ai/advanced-features/thinking) content (as [Anthropic](/docs/ai/models/anthropic) models notably do), and Pydantic AI will ask it to produce text anyway. -To instead treat an empty response as a successful run, include `None` in the `output_type`: +Include `None` in the `output_type` when finishing without a final message is a valid outcome for your agent, and you'd rather receive `None` than have the model say something for the sake of saying it: optional\_output.py @@ -43646,11 +44657,11 @@ print(result.output) When the model returns an empty response and `None` is an allowed output type, the agent will return `None` instead of retrying. [Output validator functions](#output-validator-functions) still run with `None` as the argument, so you can raise [`ModelRetry`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.ModelRetry) to reject it if needed. -`output_type=str | None` is the canonical case: it's handled as regular text output, and the **only** way the model signals `None` is by returning an empty response -- there's no output tool or structured schema involved. This mirrors how plain `str` is already treated specially as free-form text output rather than a structured tool call. +`output_type=str | None` is the canonical case: it's handled as regular text output, and the **only** way the model signals `None` is by returning a response with no text output -- either an empty response, or one containing only [thinking](/docs/ai/advanced-features/thinking) content, which some reasoning models emit after completing their work through a tool call. There's no output tool or structured schema involved. This mirrors how plain `str` is already treated specially as free-form text output rather than a structured tool call. `None` is also supported in the other output modes, with an extra structured commit path in addition to (or in place of) the empty-response fallback: -- **Bare unions including `None` that use tool mode** -- e.g. `output_type=int | None`, `output_type=[int, float, None]`, or `output_type=[ToolOutput(Foo), None]`: a dedicated `final_result_NoneType` output tool is exposed alongside the other output tools, so the model can commit to `None` through a tool call. An empty model response is still also treated as `None`, as with `str | None`. +- **Bare unions including `None` that use tool mode** -- e.g. `output_type=int | None`, `output_type=[int, float, None]`, or `output_type=[ToolOutput(Foo), None]`: a dedicated `final_result_NoneType` output tool is exposed alongside the other output tools, so the model can commit to `None` through a tool call. An empty or thinking-only model response is still also treated as `None`, as with `str | None`. - **Explicit output mode markers** -- e.g. `output_type=ToolOutput(int | None)`, `output_type=NativeOutput([int, None])`, or `output_type=PromptedOutput([int, None])`: `None` is included as a branch of the structured schema the wrapper generates. The model commits by calling the tool with `null` (for `ToolOutput`) or by selecting the `NoneType` branch of the discriminated schema (for `NativeOutput`/`PromptedOutput`). An empty response is **not** accepted -- once you've opted into an explicit structured output mode, the model is expected to commit through the schema. Note @@ -43785,7 +44796,7 @@ async def main(): _(This example is complete, it can be run "as is" -- you'll need to add `asyncio.run(main())` to run `main`)_ -As setting an `output_type` uses the [Tool Output](#tool-output) mode by default, this will only work if the model supports streaming tool arguments. For models that don't, like Gemini, try [Native Output](#native-output) or [Prompted Output](#prompted-output) instead. +As setting an `output_type` uses the [Tool Output](#tool-output) mode by default, this will only work if the model supports streaming tool arguments. For models that don't, try [Native Output](#native-output) or [Prompted Output](#prompted-output) instead. With Gemini 3, use Native Output; with earlier Gemini models that also use function tools, use Prompted Output. ### Streaming Model Responses @@ -43915,7 +44926,7 @@ If you `break` out of `stream_text()` and then leave the surrounding `async with Interrupted tool calls -Cancelling or breaking out of a model response stream can leave the final [`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse) with incomplete tool-call arguments. Pydantic AI records the response with `state='interrupted'`, but it does not filter incomplete tool calls, synthesize tool returns, or otherwise define run-resumption behavior for those partial responses. If you are controlling the graph with [`agent.iter()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.iter), stop the outer run loop as well, or check `response.state == 'interrupted'` before allowing the run to continue into tool execution. +Cancelling or breaking out of a model response stream can leave the final [`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse) with incomplete tool-call arguments. Pydantic AI records the response with `state='interrupted'`, and when the history is reused in another run the partial tool calls are [repaired automatically](/docs/ai/core-concepts/message-history#making-histories-provider-valid). If you are controlling the graph with [`agent.iter()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.iter), stop the outer run loop as well, or check `response.state == 'interrupted'` before allowing the run to continue into tool execution. #### Cancelling with `iter` @@ -43946,7 +44957,7 @@ _(This example is complete, it can be run "as is" -- you'll need to add `asyncio #### Message History After Cancellation -When a stream is cancelled, the response is recorded with `state='interrupted'` in the message history. The history includes any partial content that was received before cancellation: +When a stream is cancelled mid-generation, the response is recorded with `state='interrupted'` in the message history. The history includes any partial content that was received before cancellation: stream\_cancel\_history.py @@ -43975,7 +44986,7 @@ _(This example is complete, it can be run "as is" -- you'll need to add `asyncio Reusing interrupted history -Pydantic AI does not clean up incomplete tool calls in interrupted responses. Passing interrupted history directly into another run can therefore fail or lead to retries if the model was in the middle of emitting a tool call when cancellation happened. For now, applications that reuse interrupted history should inspect `state='interrupted'` responses and apply their own policy. +Interrupted history can be passed directly into another run. Before the next model request, Pydantic AI [repairs the transcript](/docs/ai/core-concepts/message-history#making-histories-provider-valid): any tool call that never received a result -- including one whose arguments were cut off mid-stream -- is answered with a synthesized [`ToolReturnPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ToolReturnPart) telling the model it was interrupted. Usage tracking for cancelled streams @@ -44051,8 +45062,8 @@ If you are using [Pydantic Logfire](https://logfire.pydantic.dev/docs/guides/web To install the Pydantic Evals package, run: -- [pip](#tab-panel-2) -- [uv](#tab-panel-3) +- [pip](#tab-panel-10) +- [uv](#tab-panel-11) Terminal @@ -44068,8 +45079,8 @@ uv add pydantic-evals `pydantic-evals` does not depend on `pydantic-ai`, but has an optional dependency on `logfire` if you'd like to use OpenTelemetry traces in your evals, or send evaluation results to [logfire](https://pydantic.dev/logfire). -- [pip](#tab-panel-4) -- [uv](#tab-panel-5) +- [pip](#tab-panel-12) +- [uv](#tab-panel-13) Terminal @@ -44653,6 +45664,39 @@ dataset = Dataset( **See Also:** [LLM Judge Deep Dive](/docs/ai/evals/evaluators/llm-judge) +### GEval + +Chain-of-thought evaluation following the G-Eval method (Liu et al., 2023): the judge applies explicit evaluation steps and returns an integer score in `score_range` with a reasoning trace. + +```python +from pydantic_evals.evaluators import GEval + +GEval( + criteria='coherence', + evaluation_steps=[ + 'Read the output carefully.', + 'Check that each sentence follows logically from the previous one.', + 'Assign a score from 1 (incoherent) to 5 (fully coherent).', + ], + score_range=(1, 5), + include_input=False, +) +``` + +**Parameters:** + +- `criteria` (str): The aspect being evaluated, e.g. `'coherence'` (required) +- `evaluation_steps` (list\[str\]): Explicit chain-of-thought steps the judge should follow (required) +- `score_range` (tuple\[int, int\]): Inclusive integer score range (default: `(1, 5)`) +- `include_input` (bool): Include task inputs in the prompt (default: `False`) +- `model` (Model | KnownModelName | None): Model to use (default: `'openai:gpt-5.2'`) +- `model_settings` (ModelSettings | None): Custom model settings +- `evaluation_name` (str | None): Custom name for the result (default: `'GEval'`) + +**Returns:** `EvaluationReason` with the integer score and the judge's reasoning + +**See Also:** [Standard Quality Metrics](/docs/ai/evals/evaluators/standard-quality-metrics) + * * * ## Span-Based Evaluation @@ -44694,7 +45738,7 @@ dataset = Dataset( ), # Check for errors HasMatchingSpan( - query={'has_attributes': {'error': True}}, + query={'has_status': 'error'}, evaluation_name='had_errors', ), # Check duration constraints @@ -44813,6 +45857,16 @@ $$ Slow +[`GEval`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.GEval) + +Chain-of-thought scoring + +`int` + reason + +$$ + +Slow + [`HasMatchingSpan`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.HasMatchingSpan) Behavioral check @@ -45721,7 +46775,7 @@ Pydantic Evals does not take a hard dependency on any particular metrics framewo Prefer a native evaluator where you can -If a rubric-based [`LLMJudge`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.LLMJudge) or a [custom evaluator](/docs/ai/evals/evaluators/custom) covers your use case, that's usually simpler -- zero extra dependencies and the scores slot into reports cleanly. Reach for the integrations below when you specifically want the _exact_ upstream implementation (for reproducibility with published benchmarks, parity with an existing evaluation suite, or features we don't expose natively). You can mix external and native evaluators in one dataset. +If a rubric-based [`LLMJudge`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.LLMJudge) (see the [standard quality metrics](/docs/ai/evals/evaluators/standard-quality-metrics) page for ready-made rubrics) or a [custom evaluator](/docs/ai/evals/evaluators/custom) covers your use case, that's usually simpler -- zero extra dependencies and the scores slot into reports cleanly. Reach for the integrations below when you specifically want the _exact_ upstream implementation (for reproducibility with published benchmarks, parity with an existing evaluation suite, or features we don't expose natively). You can mix external and native evaluators in one dataset. ## Pattern @@ -46598,6 +47652,36 @@ Behavior verification Tool calls, code paths +[`ToolCorrectness`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.ToolCorrectness) + +Required tool coverage + +Multiset of tool names invoked + +[`TrajectoryMatch`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.TrajectoryMatch) + +Tool-call sequence quality + +F1 against expected trajectory + +[`ArgumentCorrectness`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.ArgumentCorrectness) + +Tool argument checks + +Refund `order_id`, search query + +[`MaxToolCalls`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.MaxToolCalls) + +Budget discipline + +Tool-call budget + +[`MaxModelRequests`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.MaxModelRequests) + +Budget discipline + +Model-request budget + **Advantages:** - Fast execution (microseconds to milliseconds) @@ -46632,6 +47716,8 @@ dataset = Dataset( ) ``` +For metrics aligned with widely-used evaluation methods (G-Eval, the Ragas RAG metrics, GEMBA), see [Standard Quality Metrics](/docs/ai/evals/evaluators/standard-quality-metrics): the [`GEval`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.GEval) evaluator plus ready-made [`LLMJudge`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.LLMJudge) rubrics you can copy and adapt. To plug in the _exact_ upstream implementations of external frameworks, see [Third-Party Integrations](/docs/ai/evals/evaluators/framework-integrations). + **Advantages:** - Can evaluate subjective qualities (helpfulness, tone, creativity) @@ -47052,10 +48138,12 @@ dataset = Dataset( - **[Native Evaluators](/docs/ai/evals/evaluators/built-in)** - Complete reference of all provided evaluators - **[LLM Judge](/docs/ai/evals/evaluators/llm-judge)** - Deep dive on LLM-as-a-Judge evaluation +- **[Standard Quality Metrics](/docs/ai/evals/evaluators/standard-quality-metrics)** - G-Eval, plus LLM judge rubrics for common RAG and translation metrics - **[Third-Party Integrations](/docs/ai/evals/evaluators/framework-integrations)** - Wrap Ragas, DeepEval, and other metrics libraries - **[Custom Evaluators](/docs/ai/evals/evaluators/custom)** - Write your own evaluation logic - **[Report Evaluators](/docs/ai/evals/evaluators/report-evaluators)** - Experiment-wide analyses - **[Span-Based Evaluation](/docs/ai/evals/evaluators/span-based)** - Evaluate using OpenTelemetry spans +- **[Agentic Evaluators](/docs/ai/evals/evaluators/agentic)** - Trajectory, tool-correctness, argument, and step-budget checks for agents --- @@ -48213,6 +49301,18 @@ Match spans with specific attributes: {'has_attribute_keys': ['user_id', 'request_id']} ``` +### Status Conditions + +Match spans by their [status](/docs/ai/api/pydantic_evals/otel/#pydantic_evals.otel.SpanStatus): + +```python +# Spans that recorded an error +{'has_status': 'error'} + +# Spans explicitly marked OK (note: successful spans are typically 'unset', not 'ok') +{'has_status': 'ok'} +``` + ### Duration Conditions Match based on execution time: @@ -48269,7 +49369,7 @@ Query relationships between spans: {'all_children_have': {'max_duration': 0.5}} # No children match query -{'no_child_has': {'has_attributes': {'error': True}}} +{'no_child_has': {'has_status': 'error'}} # Descendant queries (recursive) {'min_descendant_count': 5} @@ -48288,7 +49388,7 @@ Query span hierarchy: # Ancestor queries {'some_ancestor_has': {'name_equals': 'agent_run'}} {'all_ancestors_have': {'max_duration': 10.0}} -{'no_ancestor_has': {'has_attributes': {'error': True}}} +{'no_ancestor_has': {'has_status': 'error'}} ``` ### Stop Recursing @@ -48388,15 +49488,26 @@ evaluators = [ ### Error Detection -Check for error conditions: +Check for error conditions using span [status](/docs/ai/api/pydantic_evals/otel/#pydantic_evals.otel.SpanStatus): ```python from pydantic_evals.evaluators import HasMatchingSpan evaluators = [ - # No errors occurred + # An error occurred somewhere in the trace + HasMatchingSpan( + query={'has_status': 'error'}, + evaluation_name='had_errors', + ), + + # No errors occurred: since HasMatchingSpan passes if *any* span matches, + # anchor the query on the root span and check it and all its descendants HasMatchingSpan( - query={'not_': {'has_attributes': {'error': True}}}, + query={ + 'name_equals': 'task_execution', + 'not_': {'has_status': 'error'}, + 'no_descendant_has': {'has_status': 'error'}, + }, evaluation_name='no_errors', ), @@ -48510,6 +49621,7 @@ def example_properties(node: SpanNode) -> None: _ = node.attributes # dict[str, AttributeValue] _ = node.start_timestamp # datetime _ = node.end_timestamp # datetime + _ = node.status # 'unset' | 'ok' | 'error' _ = node.children # list[SpanNode] _ = node.descendants # list[SpanNode] (recursive) _ = node.ancestors # list[SpanNode] @@ -53956,6 +55068,40 @@ async def main(): asyncio.run(main()) ``` +#### Serializing an `EvaluatorContext` + +To populate a store like the one above, you need to serialize an [`EvaluatorContext`](/docs/ai/api/pydantic_evals/evaluators/#pydantic_evals.evaluators.EvaluatorContext) to JSON (and read it back). `EvaluatorContext` is a Pydantic-serializable dataclass, so a [`TypeAdapter`](https://docs.pydantic.dev/latest/api/pydantic/type_adapter/#pydantic.type_adapter.TypeAdapter) handles both directions. Bind it to the concrete `inputs`, `output`, and `metadata` types your contexts carry so those fields are reconstructed faithfully: + +```python +from pydantic import TypeAdapter + +from pydantic_evals.evaluators import EvaluatorContext +from pydantic_evals.otel.span_tree import SpanTree + +context_adapter = TypeAdapter(EvaluatorContext[dict[str, str], str, dict[str, str]]) + +ctx = EvaluatorContext[dict[str, str], str, dict[str, str]]( + name='span_abc', + inputs={'query': 'What is AI?'}, + output='AI is artificial intelligence.', + expected_output=None, + metadata={'model': 'gpt-4o'}, + duration=1.2, + _span_tree=SpanTree(), + attributes={}, + metrics={}, +) + +json_bytes = context_adapter.dump_json(ctx) +restored = context_adapter.validate_json(json_bytes) +print(restored.output) +#> AI is artificial intelligence. +``` + +Bind the type parameters + +A bare `TypeAdapter(EvaluatorContext)` operates at the `Any` defaults for `inputs`, `output`, `metadata`, and `expected_output`: it does not reconstruct their concrete Python types, so non-primitive values round-trip back as plain dicts/lists, and a value that is not JSON-serializable raises `PydanticSerializationError` at dump time. Pass the concrete type parameters (as above) for faithful round-trips. + ## Concurrency Control Each [`OnlineEvaluator`](/docs/ai/api/pydantic_evals/online/#pydantic_evals.online.OnlineEvaluator) has a `max_concurrency` limit (default: 10). When the limit is reached, new evaluation requests for that evaluator are **dropped** (not queued). This prevents expensive evaluators from consuming unbounded resources: @@ -54186,8 +55332,8 @@ export OPENAI_API_KEY= Start the Pydantic AI AG-UI example backend. -- [pip](#tab-panel-24) -- [uv](#tab-panel-25) +- [pip](#tab-panel-18) +- [uv](#tab-panel-19) Terminal @@ -55621,7 +56767,7 @@ DuckDB-related questions. # RAG -RAG search example. This demo allows you to ask question of the [logfire](https://pydantic.dev/logfire) documentation. +RAG search example. This demo allows you to ask questions about an October 2024 snapshot of the [Logfire](https://pydantic.dev/logfire) documentation. Demonstrates: @@ -55650,8 +56796,8 @@ As with the [SQL gen](/docs/ai/examples/sql-gen) example, we run postgres on por With that running and [dependencies installed and environment variables set](/docs/ai/examples/setup#usage), we can build the search database with (**WARNING**: this requires the `OPENAI_API_KEY` env variable and will calling the OpenAI embedding API around 300 times to generate embeddings for each section of the documentation): -- [pip](#tab-panel-163) -- [uv](#tab-panel-164) +- [pip](#tab-panel-159) +- [uv](#tab-panel-160) Terminal @@ -55667,10 +56813,14 @@ uv run -m pydantic_ai_examples.rag build (Note building the database doesn't use Pydantic AI right now, instead it uses the OpenAI SDK directly.) +Embedding model and index schema + +This example uses `text-embedding-3-small` for documents and queries, storing its 1,536-dimensional output in a `vector(1536)` column. If you change the model or dimensions, stop PostgreSQL, delete the example's `postgres-data` directory (this removes the entire local example database), update `DB_SCHEMA` if needed, then restart PostgreSQL and rerun `build`. pgvector's HNSW `vector` index supports up to 2,000 dimensions. + You can then ask the agent a question with: -- [pip](#tab-panel-165) -- [uv](#tab-panel-166) +- [pip](#tab-panel-161) +- [uv](#tab-panel-162) Terminal @@ -56207,8 +57357,8 @@ graph TD With [dependencies installed and environment variables set](/docs/ai/examples/setup#usage), run: -- [pip](#tab-panel-10) -- [uv](#tab-panel-11) +- [pip](#tab-panel-14) +- [uv](#tab-panel-15) Terminal @@ -56486,8 +57636,8 @@ Demonstrates: With [dependencies installed and environment variables set](/docs/ai/examples/setup#usage), run: -- [pip](#tab-panel-159) -- [uv](#tab-panel-160) +- [pip](#tab-panel-163) +- [uv](#tab-panel-164) Terminal @@ -56503,8 +57653,8 @@ uv run -m pydantic_ai_examples.pydantic_model This examples uses `openai:gpt-5` by default, but it works well with other models, e.g. you can run it with Gemini using: -- [pip](#tab-panel-161) -- [uv](#tab-panel-162) +- [pip](#tab-panel-165) +- [uv](#tab-panel-166) Terminal @@ -56759,10 +57909,9 @@ async def stream_from_agent(prompt: str, chatbot: list[dict], past_messages: lis chatbot.append(gr_message) if isinstance(call, ToolReturnPart): for gr_message in chatbot: - if ( - gr_message.get('metadata', {}).get('id', '') - == call.tool_call_id - ): + if (gr_message.get('metadata') or {}).get( + 'id', '' + ) == call.tool_call_id: if isinstance(call.content, BaseModel): json_content = call.content.model_dump_json() else: @@ -56859,8 +58008,8 @@ Either way you'll need to install extra dependencies to run some examples, you j If you've installed `pydantic-ai` via pip/uv, you can install the extra dependencies with: -- [pip](#tab-panel-12) -- [uv](#tab-panel-13) +- [pip](#tab-panel-0) +- [uv](#tab-panel-1) Terminal @@ -56882,8 +58031,8 @@ These examples will need you to set up authentication with one or more of the LL TL;DR: in most cases you'll need to set one of the following environment variables: -- [OpenAI](#tab-panel-14) -- [Google Gemini](#tab-panel-15) +- [OpenAI](#tab-panel-2) +- [Google Gemini](#tab-panel-3) Terminal @@ -56901,8 +58050,8 @@ export GEMINI_API_KEY=your-api-key To run the examples (this will work whether you installed `pydantic_ai`, or cloned the repo), run: -- [pip](#tab-panel-16) -- [uv](#tab-panel-17) +- [pip](#tab-panel-4) +- [uv](#tab-panel-5) Terminal @@ -56918,8 +58067,8 @@ uv run -m pydantic_ai_examples. For example, to run the very simple [`pydantic_model`](/docs/ai/examples/pydantic-model) example: -- [pip](#tab-panel-18) -- [uv](#tab-panel-19) +- [pip](#tab-panel-6) +- [uv](#tab-panel-7) Terminal @@ -56947,8 +58096,8 @@ OPENAI_API_KEY='your-api-key' \ You'll probably want to edit examples in addition to just running them. You can copy the examples to a new directory with: -- [pip](#tab-panel-20) -- [uv](#tab-panel-21) +- [pip](#tab-panel-8) +- [uv](#tab-panel-9) Terminal @@ -57028,7 +58177,7 @@ You need to have a Slack workspace and the necessary permissions to create apps. ### Modal account 1. If you don't have a Modal account yet, create one on [https://modal.com/signup](https://modal.com/signup). -2. Create 3 Secrets of type "Custom" on [https://modal.com/secrets](https://modal.com/secrets): +2. Following the [Modal Secrets guide](https://modal.com/docs/guide/secrets), create 3 Secrets of type "Custom": - Name: `slack`, key: `SLACK_API_KEY`, value: the Slack Access Token you generated earlier - Name: `logfire`, key: `LOGFIRE_TOKEN`, value: the Logfire Write Token you generated earlier - Name: `openai`, key: `OPENAI_API_KEY`, value: the OpenAI API Key you generated earlier @@ -60386,8 +61535,8 @@ While this library is developed as part of Pydantic AI; it has no dependency on `pydantic-graph` is a required dependency of `pydantic-ai`, and an optional dependency of `pydantic-ai-slim`, see [installation instructions](/docs/ai/overview/install#slim-install) for more information. You can also install it directly: -- [pip](#tab-panel-22) -- [uv](#tab-panel-23) +- [pip](#tab-panel-16) +- [uv](#tab-panel-17) Terminal @@ -61093,7 +62242,7 @@ _(This example is complete, it can be run "as is" -- you'll need to add `asyncio ## Choosing a model -The best embedding model depends on your constraints. Here's a starting-point cheat sheet; consult each provider's docs and the [MTEB leaderboard](https://huggingface.co/spaces/mteb/leaderboard) before committing to a model for a large index. +The best embedding model depends on your language, domain, latency, deployment, and evaluation constraints. Use this table as a starting point, then check the [MTEB leaderboard](https://huggingface.co/spaces/mteb/leaderboard), the model card on the [Hugging Face Hub](https://huggingface.co/models?library=sentence-transformers), and your own retrieval evaluation before committing to a model for a large index. If you want... @@ -61125,7 +62274,85 @@ Any model with dimension control (see [Settings](#settings)) Switching models later -Swapping a model changes the output dimension and the similarity distribution, so you'll need to re-embed (and re-index) your documents. Pick a model you're happy to stick with, or one that supports [dimension control](#settings) so you can tune the index size without changing models. +Switching models changes the embedding space and may change the output dimension, so you'll need to re-embed your documents and update the index. Pick a model you're happy to stick with, or one that supports [dimension control](#settings) so you can tune the index size without changing models. + +## Using embeddings for RAG + +For Retrieval-Augmented Generation (RAG), embeddings are one part of a larger retrieval pipeline. Pydantic AI provides [`Embedder`](/docs/ai/api/pydantic-ai/embeddings/#pydantic_ai.embeddings.Embedder) for query and document embeddings, and [tools](/docs/ai/tools-toolsets/tools) for giving retrieved context to an agent. The [RAG example](/docs/ai/examples/rag) demonstrates vector storage, retrieval, and passing retrieved context to an agent using pre-split data. + +If you want a provider-managed pipeline instead, first upload or import files into a provider-managed store, then pass its ID to `FileSearchTool`. The provider handles chunking, embeddings, storage, and retrieval; see the [File Search Tool docs](/docs/ai/tools-toolsets/builtin-tools#file-search-tool) for supported providers. The rest of this section covers building your own pipeline, where these choices stay application-specific. + +A typical custom RAG pipeline looks like this: + +1. Split source documents into chunks when needed. +2. Embed the chunks with [`embed_documents()`](/docs/ai/api/pydantic-ai/embeddings/#pydantic_ai.embeddings.Embedder.embed_documents). +3. Store each vector with metadata such as source URL, title, heading, page number, and permissions. +4. Embed the user's search text with [`embed_query()`](/docs/ai/api/pydantic-ai/embeddings/#pydantic_ai.embeddings.Embedder.embed_query). +5. Search a vector index for similar chunks. +6. Optionally [rerank](#two-stage-retrieval-with-rerankers) the shortlist. +7. Pass the retrieved text to the agent through a tool. + +### Chunking + +Chunks are the units of source text that a retrieval index stores and returns. You do not need to split every document. Chunking is useful when: + +- you want to retrieve a relevant subsection instead of the full document; +- a document is longer than the embedding model's maximum input length; or +- a document contains enough independent facts or topics that one embedding may not represent them precisely. + +If none of these applies, embedding the whole document can be reasonable. There is no universally best chunking strategy or chunk size: the right choice depends on the embedding model, source format, domain, and the questions users ask. + +Strategy + +Useful starting point + +Trade-off + +Document structure, such as headings, paragraphs, sentences, pages, or records + +Clean, consistently structured sources + +Cheap and preserves natural boundaries, but produces uneven chunk sizes. + +Fixed-size token windows + +Unstructured text or strict model input limits + +Simple and predictable, but can split related text. Overlap preserves boundary context at the cost of a larger index and duplicate results. + +Semantic splitting + +Sources where topic boundaries matter more than layout + +Can keep related ideas together, but adds ingestion work and depends on the model and threshold. + +LLM-assisted splitting + +Irregular or domain-specific sources that require interpretation + +Flexible, but adds the most latency and cost and requires validation of the chosen boundaries and source coverage. + +For a first implementation, use natural document boundaries with a token limit, keep source metadata and a link or identifier for the full document with every chunk, and evaluate before adding a more expensive strategy. Chunks should be large enough to answer a focused question but small enough to avoid unrelated context. + +See Chroma's [chunking strategy evaluation](https://www.trychroma.com/research/evaluating-chunking) for a comparison of common strategies and reproducible evaluation code, and Hugging Face's [RAG evaluation cookbook](https://huggingface.co/learn/cookbook/rag_evaluation) for an end-to-end RAG evaluation workflow. + +### Vector storage + +Pydantic AI does not prescribe a vector database. Choose based on the index size, query and ingestion volume, metadata filtering and permission requirements, hybrid keyword search needs, availability requirements, and infrastructure you already operate: + +- A local index or embedded database, such as FAISS, LanceDB, or SQLite with a vector extension, can suit prototypes and small indexes. +- PostgreSQL with `pgvector` can suit applications that already use PostgreSQL. +- A managed vector or search service can suit applications that need hosted scaling and operational support. + +The index's vector dimension must match the embedding output dimension. Use a compatible model configuration when indexing and querying; matching dimensions alone do not make vectors compatible. If you change the embedding configuration, re-embed the documents and update or rebuild the index as required by your storage layer. + +Hugging Face's [advanced RAG cookbook](https://huggingface.co/learn/cookbook/advanced_rag) introduces vector indexes, similarity choices, reranking, and other retrieval trade-offs. + +### Evaluation + +Chunking, the embedding model, similarity metric, number of retrieved chunks, and reranking all interact. Public benchmarks can narrow the candidates, but they cannot prove which complete pipeline works best for your application. Compare changes using queries and documents representative of production, checking both that relevant text is retrieved and that irrelevant text is kept out of the agent's context. + +Use [Pydantic Evals](/docs/ai/evals/evals) to track retrieval quality across a dataset of representative queries. Hugging Face's [RAG evaluation cookbook](https://huggingface.co/learn/cookbook/rag_evaluation) demonstrates how to build a synthetic evaluation set and evaluate generated answers. ## Providers @@ -61137,8 +62364,8 @@ Swapping a model changes the output dimension and the similarity distribution, s To use OpenAI embedding models, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `openai` optional group: -- [pip](#tab-panel-26) -- [uv](#tab-panel-27) +- [pip](#tab-panel-22) +- [uv](#tab-panel-23) Terminal @@ -61262,8 +62489,8 @@ See [OpenAI-compatible Models](/docs/ai/models/openai#openai-compatible-models) To use Google embedding models, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `google` optional group: -- [pip](#tab-panel-28) -- [uv](#tab-panel-29) +- [pip](#tab-panel-24) +- [uv](#tab-panel-25) Terminal @@ -61406,8 +62633,8 @@ See [Google's task type documentation](https://ai.google.dev/gemini-api/docs/emb To use Cohere embedding models, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `cohere` optional group: -- [pip](#tab-panel-30) -- [uv](#tab-panel-31) +- [pip](#tab-panel-26) +- [uv](#tab-panel-27) Terminal @@ -61479,8 +62706,8 @@ embedder = Embedder( To use VoyageAI embedding models, you need to install `pydantic-ai-slim` with the `voyageai` optional group: -- [pip](#tab-panel-32) -- [uv](#tab-panel-33) +- [pip](#tab-panel-28) +- [uv](#tab-panel-29) Terminal @@ -61551,8 +62778,8 @@ embedder = Embedder( To use Bedrock embedding models, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `bedrock` optional group: -- [pip](#tab-panel-34) -- [uv](#tab-panel-35) +- [pip](#tab-panel-30) +- [uv](#tab-panel-31) Terminal @@ -61771,7 +62998,7 @@ Bedrock embedding models do not support the `count_tokens()` method because AWS ### Sentence Transformers (Local) -[`SentenceTransformerEmbeddingModel`](/docs/ai/api/pydantic-ai/embeddings/#pydantic_ai.embeddings.sentence_transformers.SentenceTransformerEmbeddingModel) runs embeddings locally using the [sentence-transformers](https://www.sbert.net/) library, giving you access to the thousands of [embedding models on Hugging Face](https://huggingface.co/models?library=sentence-transformers) without any API calls. This is ideal for: +[`SentenceTransformerEmbeddingModel`](/docs/ai/api/pydantic-ai/embeddings/#pydantic_ai.embeddings.sentence_transformers.SentenceTransformerEmbeddingModel) runs embeddings locally using the [Sentence Transformers](https://www.sbert.net/) library, giving you access to the thousands of [embedding models on Hugging Face](https://huggingface.co/models?library=sentence-transformers) without any API calls. This is ideal for: - **Privacy** -- Data never leaves your infrastructure - **Cost** -- No API charges for high-volume workloads @@ -61782,8 +63009,8 @@ Bedrock embedding models do not support the `count_tokens()` method because AWS To use Sentence Transformers embedding models, you need to install `pydantic-ai-slim` with the `sentence-transformers` optional group: -- [pip](#tab-panel-36) -- [uv](#tab-panel-37) +- [pip](#tab-panel-32) +- [uv](#tab-panel-33) Terminal @@ -62007,6 +63234,8 @@ Managed reranker alternatives If you'd rather not run a reranker locally, several providers offer hosted rerankers, including [Cohere Rerank](https://docs.cohere.com/docs/rerank-overview), [VoyageAI Rerank](https://docs.voyageai.com/docs/reranker), and [Jina Rerank](https://jina.ai/reranker). Call their HTTP clients or SDKs from a helper function with the same shape as `rerank()` above. +For more background on retrieve-and-rerank pipelines, see Hugging Face's [advanced RAG cookbook](https://huggingface.co/learn/cookbook/advanced_rag). To serve open-source embedding and reranker models yourself, see Hugging Face [Text Embeddings Inference](https://huggingface.co/docs/text-embeddings-inference) and its [supported rerankers](https://huggingface.co/docs/text-embeddings-inference/supported_models#supported-re-rankers-and-sequence-classification-models). + ## Building Custom Embedding Models To integrate a custom embedding provider, subclass [`EmbeddingModel`](/docs/ai/api/pydantic-ai/embeddings/#pydantic_ai.embeddings.EmbeddingModel): @@ -62074,7 +63303,7 @@ See [Capabilities](/docs/ai/core-concepts/capabilities) for using and building c Tip -If you want to contribute a capability, open an issue on [**Pydantic AI Harness**](https://github.com/pydantic/pydantic-ai-harness) rather than on pydantic-ai. Most capabilities belong in the harness -- see [What goes where?](/docs/ai/harness/overview#what-goes-where) for the distinction. +If you want to contribute a capability, open an issue on [**Pydantic AI Harness**](https://github.com/pydantic/pydantic-ai-harness) rather than on pydantic-ai. Most capabilities belong in the harness -- see [What goes where?](https://pydantic.dev/docs/ai/harness/#what-goes-where) for the distinction. ## Publishing capability packages @@ -62101,7 +63330,7 @@ See [Custom capabilities in specs](/docs/ai/core-concepts/agent-spec#custom-capa ## Pydantic AI Harness -[**Pydantic AI Harness**](/docs/ai/harness/overview) is the official capability library for Pydantic AI -- standalone capabilities like memory, guardrails, and context management live there rather than in core. See [What goes where?](/docs/ai/harness/overview#what-goes-where) for the full breakdown, or jump to the [capability matrix](https://github.com/pydantic/pydantic-ai-harness#capability-matrix). +[**Pydantic AI Harness**](https://pydantic.dev/docs/ai/harness/) is the official capability library for Pydantic AI -- standalone capabilities like memory, guardrails, and context management live there rather than in core. See [What goes where?](https://pydantic.dev/docs/ai/harness/#what-goes-where) for the full breakdown, or jump to the [capability matrix](https://github.com/pydantic/pydantic-ai-harness#capability-matrix). ## Third-party ecosystem @@ -62179,23 +63408,24 @@ from pydantic_ai import Agent, RunContext, UsageLimits joke_selection_agent = Agent( # (1) 'openai:gpt-5.2', + name='joke_selection_agent', # (2) instructions=( 'Use the `joke_factory` to generate some jokes, then choose the best. ' 'You must return just a single joke.' ), ) -joke_generation_agent = Agent( # (2) - 'google:gemini-3-flash-preview', output_type=list[str] +joke_generation_agent = Agent( # (3) + 'google:gemini-3-flash-preview', name='joke_generation_agent', output_type=list[str] ) @joke_selection_agent.tool async def joke_factory(ctx: RunContext, count: int) -> list[str]: - r = await joke_generation_agent.run( # (3) + r = await joke_generation_agent.run( # (4) f'Please generate {count} jokes.', - usage=ctx.usage, # (4) + usage=ctx.usage, # (5) ) - return r.output # (5) + return r.output # (6) result = joke_selection_agent.run_sync( @@ -62210,6 +63440,8 @@ print(result.usage) The "parent" or controlling agent. +Passing `name` is optional but recommended when you run more than one agent: it labels each agent's run span, so naming both lets you tell the parent and delegate apart in [Logfire](/docs/ai/integrations/logfire). When omitted, the name is inferred from the variable the agent is assigned to and falls back to `'agent'` when it can't be (e.g. agents kept in a list or dict). + The "delegate" agent, which is called from within a tool of the parent agent. Call the delegate agent from within a tool of the parent agent. @@ -62258,6 +63490,7 @@ class ClientAndKey: # (1) joke_selection_agent = Agent( 'openai:gpt-5.2', + name='joke_selection_agent', deps_type=ClientAndKey, # (2) instructions=( 'Use the `joke_factory` tool to generate some jokes on the given subject, ' @@ -62266,6 +63499,7 @@ joke_selection_agent = Agent( ) joke_generation_agent = Agent( 'google:gemini-3-flash-preview', + name='joke_generation_agent', deps_type=ClientAndKey, # (4) output_type=list[str], instructions=( @@ -62342,6 +63576,10 @@ graph TD Here agents don't need to use the same deps. +Message history between agents + +To give another agent the previous conversation as context, pass `message_history` to its run method. See [Sharing messages between agents](/docs/ai/core-concepts/message-history#sharing-messages-between-agents) for the details on instructions, system prompts, and tool context. + Here we show two agents used in succession, the first to find a flight and the second to extract the user's seat preference. programmatic\_handoff.py @@ -62365,6 +63603,7 @@ class Failed(BaseModel): flight_search_agent = Agent[object, FlightDetails | Failed]( # (1) 'openai:gpt-5.2', + name='flight_search_agent', output_type=FlightDetails | Failed, # type: ignore instructions=( 'Use the "flight_search" tool to find a flight ' @@ -62413,6 +63652,7 @@ class SeatPreference(BaseModel): # This agent is responsible for extracting the user's seat selection seat_preference_agent = Agent[object, SeatPreference | Failed]( # (5) 'openai:gpt-5.2', + name='seat_preference_agent', output_type=SeatPreference | Failed, # type: ignore instructions=( "Extract the user's seat preference. " @@ -62713,6 +63953,7 @@ async def test_forecast(): ), model_name='test', timestamp=IsNow(tz=timezone.utc), + provider_name='test', run_id=IsStr(), conversation_id=IsStr(), ), @@ -62742,6 +63983,7 @@ async def test_forecast(): ), model_name='test', timestamp=IsNow(tz=timezone.utc), + provider_name='test', run_id=IsStr(), conversation_id=IsStr(), ), @@ -62876,8 +64118,8 @@ The web UI is meant for local development and debugging. In production, you can Install the `web` extra (installs Starlette and Uvicorn): -- [pip](#tab-panel-38) -- [uv](#tab-panel-39) +- [pip](#tab-panel-20) +- [uv](#tab-panel-21) Terminal @@ -62971,6 +64213,14 @@ agent = Agent('openai:gpt-5.2') app = agent.to_web(instructions='Always respond in a friendly tone.') ``` +## Tool Approval + +Tools that [require approval](/docs/ai/tools-toolsets/deferred-tools#human-in-the-loop-tool-approval) are surfaced in the UI as approve/reject prompts: when the agent calls such a tool, the UI renders the pending call and lets you approve or deny it before the run continues. This works out of the box -- no extra configuration is needed. + +Caution + +The chat endpoint executes tool approvals relayed by the client, including for tools marked `requires_approval=True`. The server trusts the approval decision it receives, so a client with direct access to the endpoint can approve any pending call. As noted above, the web UI is meant for local development -- this is fine when it's bound to localhost, but do not expose `to_web()` to untrusted clients without putting authentication in front of it. + ## Reserved Routes The web UI app uses the following routes which should not be overwritten: @@ -62992,7 +64242,7 @@ For offline usage, download the html file once while you have internet access: from pydantic_ai.ui import DEFAULT_HTML_URL print(DEFAULT_HTML_URL) # Use this URL to download the UI HTML file -#> https://cdn.jsdelivr.net/npm/@pydantic/ai-chat-ui@1.2.0/dist/index.html +#> https://cdn.jsdelivr.net/npm/@pydantic/ai-chat-ui@2.0.0/dist/index.html ``` You can then download the file using the URL printed above: @@ -63019,24 +64269,325 @@ app = agent.to_web(html_source='https://cdn.example.com/ui/index.html') --- +# [ACP](https://pydantic.dev/docs/ai/harness/acp/) + +> Serve a Pydantic AI agent to editors and terminal UIs over the Agent Client Protocol -- streamed text, diff-rendered file edits, human-in-the-loop tool approval, and per-workspace sessions. + +# ACP + +Editors like [Zed](https://zed.dev/docs/ai/external-agents) speak ACP: a stdio JSON-RPC protocol that lets a TUI or editor drive an external coding agent -- streaming its text, rendering its file edits as diffs, and prompting the user to approve sensitive tool calls. Reach for this capability when you want a Pydantic AI `Agent` to appear as a first-class agent inside one of those editors, without implementing the ACP server side yourself. + +Experimental + +Unlike the graduated capabilities in these docs, ACP itself may still be **removed**, not just changed -- it lives under `pydantic_ai_harness.experimental` and may change or be removed in any release, without a deprecation period. Import it from the experimental path -- there is no top-level export: + +```python +from pydantic_ai_harness.experimental.acp import run_acp_stdio_sync +``` + +Importing any experimental capability emits a `HarnessExperimentalWarning`. Silence **all** harness experimental warnings with a single filter (no per-capability lines needed): + +```python +import warnings +from pydantic_ai_harness.experimental import HarnessExperimentalWarning + +warnings.filterwarnings('ignore', category=HarnessExperimentalWarning) +``` + +## The problem + +To plug a Pydantic AI agent into an ACP editor you would otherwise have to implement the ACP server side by hand -- chunking streamed text under the wire limit, rendering tool calls as diffs, mapping the protocol's permission requests onto the agent's tools, and managing per-workspace sessions. + +## The solution + +`run_acp_stdio` serves any Pydantic AI `Agent` as an ACP agent over stdin/stdout. The editor launches your script as a subprocess and talks to it; the adapter translates between ACP and the agent's run loop: + +ACP needs + +The adapter provides + +Streamed assistant text and reasoning + +Agent text/thinking deltas, chunked under the wire limit + +Rich tool calls (`kind`, file `locations`, diffs) + +A presenter that recognizes `FileSystem`/`Shell` tool calls + +Human-in-the-loop tool approval + +Maps ACP permission requests to Pydantic AI's deferred-approval tools + +Per-workspace sessions + +A `session_config` hook to root tools at the client's working directory + +Cancellation, multi-turn history, session close + +Handled per session + +## Installation + +Terminal + +```bash +uv add "pydantic-ai-harness[acp]" +``` + +This pulls in the [`agent-client-protocol`](https://pypi.org/project/agent-client-protocol/) SDK. The rest of the harness does not depend on it -- only `pydantic_ai_harness.experimental.acp` does. + +## Quick start + +Write a script that builds your agent and serves it: + +```python +# my_acp_agent.py +from pydantic_ai import Agent +from pydantic_ai_harness.experimental.acp import run_acp_stdio_sync + + +def build_agent() -> Agent[None, str]: + return Agent('anthropic:claude-sonnet-4-6', instructions='You are a coding assistant.') + + +if __name__ == '__main__': + run_acp_stdio_sync(build_agent()) +``` + +`run_acp_stdio_sync` blocks for the lifetime of the connection -- it is the `main()` of an agent the editor launches. Inside an existing event loop, use the async `run_acp_stdio` instead. + +## Connecting from an editor + +ACP clients launch the agent as a subprocess. In Zed, register it as an [external agent](https://zed.dev/docs/ai/external-agents) in `settings.json`: + +```json +{ + "agent_servers": { + "My Pydantic AI Agent": { + "type": "custom", + "command": "python", + "args": ["/absolute/path/to/my_acp_agent.py"], + "env": { "ANTHROPIC_API_KEY": "..." } + } + } +} +``` + +Any ACP-compatible client works the same way -- point it at `python my_acp_agent.py`. + +The provider environment must be available to the launched subprocess. GUI editors and SDK-based test wrappers may not source your interactive shell startup files. If a real-model agent exits before initialize or fails provider auth, first verify that the command's process can see variables such as `ANTHROPIC_API_KEY`. + +## Rooting tools at the workspace + +A coding agent should read and write files in the workspace the editor opened, not wherever the subprocess started. ACP gives each session a working directory (`cwd`); a `session_config` factory turns that into per-session tools: + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.experimental.acp import AcpSession, AcpSessionConfig, run_acp_stdio_sync +from pydantic_ai_harness.filesystem import FileSystem +from pydantic_ai_harness.shell import Shell + +agent = Agent('anthropic:claude-sonnet-4-6') + + +def session_config(session: AcpSession) -> AcpSessionConfig[None]: + # Root file and shell tools at the workspace the client opened. + return AcpSessionConfig( + deps=None, + toolsets=[ + FileSystem[None](root_dir=session.cwd).get_toolset(), + Shell[None](cwd=session.cwd).get_toolset(), + ], + ) + + +if __name__ == '__main__': + run_acp_stdio_sync(agent, session_config=session_config) +``` + +The factory runs once per session with the client's `AcpSession` setup (its `cwd`, `mcp_servers`, and capabilities) and returns an `AcpSessionConfig` whose `deps` and `toolsets` apply to every run in that session. This is correct across multiple concurrent sessions in one process, where a single static `FileSystem` could not be. + +## Editor-native filesystem and shell (optional) + +The local [`FileSystem`](/docs/ai/harness/filesystem) and [`Shell`](/docs/ai/harness/shell) above operate on the agent process's own disk and subprocesses. An editor's source of truth is different: unsaved buffers, its own idea of the workspace layout, and -- for a remote or containerized editor -- the machine the code actually lives on. When the client advertises support, `acp_filesystem` and `acp_terminal` give the agent `read_file`/`write_file`/`run_command` tools that route through the client, so it acts where the user is: + +```python +from pydantic_ai_harness.experimental.acp import AcpSession, AcpSessionConfig, acp_filesystem, acp_terminal +from pydantic_ai_harness.filesystem import FileSystem +from pydantic_ai_harness.shell import Shell + + +def session_config(session: AcpSession) -> AcpSessionConfig[None]: + # Use the editor's filesystem/terminal when offered; otherwise fall back to local. + fs = acp_filesystem(session) or FileSystem[None](root_dir=session.cwd).get_toolset() + shell = acp_terminal(session) or Shell[None](cwd=session.cwd).get_toolset() + return AcpSessionConfig(deps=None, toolsets=[fs, shell]) +``` + +Each helper returns `None` when the client did not advertise the capability, so the `or` falls back to local and the agent works either way. The tool names match the local `FileSystem`/`Shell`, so rich rendering stays identical. + +## Tool approval + +Mark a tool to require approval and ACP relays the decision to the client, which shows the user an approve/reject prompt: + +```python +@agent.tool_plain(requires_approval=True) +def delete_file(path: str) -> str: + ... +``` + +The lifecycle the client sees is `pending` (awaiting approval) -> `in_progress` (granted, running) -> `completed`/`failed`, so an unapproved action is never shown as already running. "Always allow"/"always reject" decisions are remembered for the session, scoped by default to the exact call (tool name plus arguments) so approving one call never silently approves a different one. Pass `permission_policy` to widen or narrow that scope. + +## Rich tool rendering + +By default the adapter recognizes the harness `FileSystem` and `Shell` tool calls by name and annotates them with an ACP `kind` (`read`/`edit`/`search`/`execute`), the file `locations` they touch, and an inline diff for edits -- so the editor renders click-to-file links and diff views instead of opaque JSON. Pass `tool_presenter` to add rendering for your own tools (optionally with `chain_presenters` ahead of the default `default_coding_presenter`), or `lambda _call: None` to disable it. + +## MCP servers + +An ACP client may offer MCP servers during session setup. This adapter does not connect them itself; a `session_config` is the place to turn `session.mcp_servers` into Pydantic AI toolsets (for example with `pydantic_ai.mcp.MCPServerStdio`). If a client sends MCP servers and no `session_config` is installed to consume them, the session request is rejected rather than silently ignoring them. A spec-following client only sends HTTP/SSE MCP servers when the agent advertises support during `initialize`; when your `session_config` connects them, say so: + +```python +from acp import schema +from pydantic_ai_harness.experimental.acp import PydanticAIACPAgent + +PydanticAIACPAgent( + agent, + session_config=connect_mcp_servers, + mcp_capabilities=schema.McpCapabilities(http=True, sse=True), +) +``` + +## Prompt content types + +The agent advertises which prompt content it accepts. The default is **text only**, so a client is not invited to send blocks a text model cannot handle. Enable the kinds your model supports: + +```python +from acp import schema + +run_acp_stdio_sync(agent, prompt_capabilities=schema.PromptCapabilities(image=True, embedded_context=True)) +``` + +## Session persistence + +Pass a `session_store` to let a client reopen a past conversation with `session/load`. Each committed turn is persisted as two parts -- the model's message history and the client-visible transcript -- and reopening restores the history into the agent and replays the transcript to the client, so its UI is rebuilt as the user last saw it. Without a store, `session/load` is advertised as unsupported. + +```python +from pydantic_ai_harness.experimental.acp import InMemorySessionStore + +run_acp_stdio_sync(agent, session_store=InMemorySessionStore()) +``` + +`InMemorySessionStore` keeps sessions for the lifetime of the process. Implement the `SessionStore` protocol (`save`/`load` a `StoredSession`) over a file or database to make them survive a restart -- the stored values are Pydantic models, so they serialize with Pydantic. Session persistence is for _reopening a conversation_; it is orthogonal to per-run durability. To also make individual turns crash-resilient, add a [step-durability capability](/docs/ai/harness/step-persistence) -- each ACP turn is one agent run, so the two layers compose with no glue. + +## Model selection + +Pass `models` to advertise a stable ACP session config option named `model` (using Pydantic AI [model names](/docs/ai/models/)). The first is each session's default. A selection is applied as a per-run override -- the shared agent is never mutated -- and is persisted with the session when a `session_store` is set. + +```python +run_acp_stdio_sync(agent, models=['anthropic:claude-sonnet-4-6', 'anthropic:claude-opus-4-8', 'openai:gpt-4o']) +``` + +A model id is any string a Pydantic AI model accepts, so newer models not yet in `KnownModelName` work too. Pass `models='all'` to offer every model Pydantic AI knows. To advertise ids `infer_model` does not understand (OAuth or subscription models), pass `model_resolver` to map the selected id to a prebuilt `Model`. + +## Cancellation and limitations + +- **Cancellation.** `session/cancel` and `session/close` cancel the in-flight turn; close waits for it to unwind before returning. Cooperative async tools stop promptly. A synchronous tool already running in a worker thread cannot be force-stopped, so prefer async tools for cancellation-sensitive work. +- **Approval detection.** Tools that require approval are recognized when they live in a `FunctionToolset` (which the harness `FileSystem`/`Shell` and `@agent.tool` all use). A tool whose approval requirement is decided dynamically per call (by raising `ApprovalRequired` from its body) starts as `in_progress`, and any side effects it ran before raising have already happened -- use an `ApprovalRequiredToolset` for actions that must not partially execute before approval. +- **Overwrite diffs.** `write_file` renders an overwrite as if creating a new file, so the diff understates what it replaced. +- **Live terminal panes.** `acp_terminal` returns a command's captured output; it does not embed a live terminal pane in the tool call. +- **Images.** Prompt image blocks are off by default and must be enabled via `prompt_capabilities` with a model that accepts them. +- **Slash commands.** The adapter does not yet advertise any commands, so no slash commands appear in the client. Planned. + +## API + +```python +run_acp_stdio( # async; serve until the client disconnects + agent, + *, + deps=None, + name=None, # advertised name; defaults to the agent's name + version='0.1.0', + session_config=None, # per-session deps/toolsets from the client's setup + permission_policy=None, # scope of remembered "always" approval decisions + prompt_capabilities=None, # defaults to text-only + mcp_capabilities=None, # MCP transports to advertise; needs a session_config to connect them + tool_presenter=None, # defaults to the FileSystem/Shell presenter + session_store=None, # enables session/load by persisting each session + models=None, # models offered as the `model` config option ('all' for every known model) + model_resolver=None, # maps an advertised model id to the Model used for the run + usage_limits=None, # per-run request/token ceilings +) + +run_acp_stdio_sync(...) # synchronous wrapper, same arguments + +PydanticAIACPAgent(agent, *, ...) # the ACP agent object, to embed in a custom server +``` + +The module also exports the session types (`AcpSession`, `AcpSessionConfig`, `McpServer`), the store types (`SessionStore`, `StoredSession`, `InMemorySessionStore`), the client toolsets (`AcpFileSystemToolset`, `AcpTerminalToolset`, `acp_filesystem`, `acp_terminal`), the permission types (`ToolCallPermission`, `default_permission_scope`), and the presentation helpers (`ToolCallPresentation`, `chain_presenters`, `default_coding_presenter`). + +Source: [`pydantic_ai_harness/experimental/acp/`](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/experimental/acp/). + +## Further reading + +- [Agent Client Protocol](https://agentclientprotocol.com) -- protocol specification +- [Zed external agents](https://zed.dev/docs/ai/external-agents) -- editor-side configuration +- [Human-in-the-loop tool approval](/docs/ai/tools-toolsets/deferred-tools/#human-in-the-loop-tool-approval) (Pydantic AI) +- [Pydantic AI capabilities](/docs/ai/core-concepts/capabilities/) + +--- + # [Code Mode](https://pydantic.dev/docs/ai/harness/code-mode/) +> Wrap an agent's tools into a single sandboxed run_code tool so the model orchestrates many calls in one Python program instead of many round-trips. + # Code Mode -Code mode is one of the capabilities in [**Pydantic AI Harness**](/docs/ai/harness/overview), the official capability library for Pydantic AI. The full docs live in the [harness repo](https://github.com/pydantic/pydantic-ai-harness) -- this page is a short intro. +`CodeMode` replaces individual tool calls with a single sandboxed Python execution environment. Instead of the model issuing one tool call per action, it writes a Python program that calls your tools as functions -- with loops, conditionals, variables, and `asyncio.gather` -- all inside a sandboxed [Monty](https://github.com/pydantic/monty) runtime. -[`CodeMode`](https://github.com/pydantic/pydantic-ai-harness/blob/main/pydantic_ai_harness/code_mode/README.md) wraps your tools into a single `run_code` tool powered by our [Monty](https://github.com/pydantic/monty) sandbox. The model writes Python that calls multiple tools with loops, conditionals, variables, and `asyncio.gather` -- all inside one tool call. +[Source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/code_mode/) -Standard tool calling requires one model round-trip per tool call. An agent that needs to fetch 10 items and process each one makes 11+ model calls -- slow, expensive, and context-heavy. Code mode collapses that into one. +## The problem -## Usage +Standard tool calling costs one model round-trip per tool call. An agent that needs to fetch 10 items and process each one makes 11+ model calls -- slow, expensive, and heavy on context. The conversation history grows with every intermediate result, and everything runs sequentially unless the model deliberately batches parallel tool calls. + +## The solution + +`CodeMode` wraps the agent's tools into a single `run_code` tool. The model writes one Python snippet that orchestrates many tool calls locally: fan them out with `asyncio.gather`, filter and transform results in plain Python, and return only what matters. + +Standard tool calling + +Code mode + +1 model call per tool + +1 model call for N tools + +Sequential by default + +Parallel via `asyncio.gather` + +No local computation + +Filter, transform, aggregate in code + +Large conversation history + +Compact -- fewer messages + +## Installation + +Code mode requires the Monty sandbox, available via the `codemode` extra (the `code-mode` extra is an equivalent alias): Terminal ```bash -uv add "pydantic-ai-harness[code-mode]" +uv add "pydantic-ai-harness[codemode]" ``` +## Usage + +Construct an `Agent` with `CodeMode()` in its `capabilities`, then register tools as usual. Every tool becomes callable from inside `run_code`: + ```python from pydantic_ai import Agent from pydantic_ai_harness import CodeMode @@ -63060,9 +64611,11 @@ result = agent.run_sync("What's the weather in Paris and Tokyo, in Celsius?") print(result.output) ``` -The model writes code like: +Inside a single `run_code` call, the model writes code like the following (illustrative -- the exact code the model emits will vary): ```python +import asyncio + paris, tokyo = await asyncio.gather( get_weather(city='Paris'), get_weather(city='Tokyo'), @@ -63072,62 +64625,6244 @@ tokyo_c = await convert_temp(fahrenheit=tokyo['temp_f']) {'paris': paris_c, 'tokyo': tokyo_c} ``` -## Full documentation +Both weather lookups run in parallel, the conversions run locally, and the whole thing collapses into one model round-trip instead of five. + +## Selective tool sandboxing + +By default, `CodeMode(tools='all')` sandboxes every tool. The `tools` field is a Pydantic AI `ToolSelector`, so you can control precisely which tools go through the sandbox. Tools that match the selector become callables inside `run_code`; non-matching tools stay visible to the model as regular tool calls. + +```python +from pydantic_ai_harness import CodeMode + +# By name -- only these tools are available inside run_code +CodeMode(tools=['search', 'fetch']) + +# By predicate -- (ctx, tool_def) -> bool | Awaitable[bool] +CodeMode(tools=lambda ctx, td: td.name != 'dangerous_tool') + +# By metadata -- combine with SetToolMetadata or a toolset's .with_metadata() +CodeMode(tools={'code_mode': True}) +``` + +### Metadata-based selection + +Use metadata when the decision should travel with a tool or toolset, rather than with one `CodeMode` instance. This suits shared toolsets: the toolset author tags the tools that are safe and useful to call from generated code, and each agent opts into that tag with `CodeMode(tools={...})`. + +`CodeMode(tools={'code_mode': True})` uses the standard Pydantic AI [`ToolSelector`](/docs/ai/api/pydantic-ai/tools/) metadata form. A tool is sandboxed when its `ToolDefinition.metadata` contains all of the selector's key-value pairs. Extra metadata on the tool is fine, and nested dictionaries are matched by deep inclusion. + +The common pattern is to tag an entire toolset with `.with_metadata(...)`: + +```python +from pydantic_ai import Agent +from pydantic_ai.toolsets import FunctionToolset +from pydantic_ai_harness import CodeMode + + +def search(query: str) -> str: + """Search the web.""" + return f'results for {query}' + + +def fetch(url: str) -> str: + """Fetch a URL.""" + return f'contents of {url}' + + +search_tools = FunctionToolset(tools=[search, fetch]).with_metadata(code_mode=True) + +agent = Agent( + 'anthropic:claude-sonnet-4-6', + toolsets=[search_tools], + capabilities=[CodeMode(tools={'code_mode': True})], +) +``` + +Here `search` and `fetch` are removed from the model-facing tool list and become callable functions inside `run_code`. Tools without `metadata['code_mode'] == True` stay visible as regular tool calls. + +## Tool Search interaction + +When you mark tools or whole toolsets `defer_loading=True` ([Tool Search](/docs/ai/tools-toolsets/tools-advanced/#tool-search)), `CodeMode` keeps them out of `run_code` while they're undiscovered -- they pass straight through, so Tool Search drives them as usual (sent on the wire with `defer_loading` on providers with native tool search; otherwise dropped until discovered, with a `search_tools` tool alongside `run_code`). Once the model discovers a tool it comes back with `defer_loading=False`, and from then on `CodeMode` folds it into `run_code` like any other tool, so it's callable from generated code. + +That fold-in grows `run_code`'s description, which invalidates the prompt-cache prefix once at the moment of discovery (turns with no discovery stay cache-warm). Two ways to avoid the bust: + +- Pass `dynamic_catalog=True` to keep `run_code`'s description static across discoveries. The catalog of sandboxed-tool signatures moves into the agent instructions (as a dynamic [`InstructionPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.InstructionPart)) and newly-discovered tools are announced via [`ctx.enqueue`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.enqueue) instead of by rebuilding the description: + + ```python + from pydantic_ai_harness import CodeMode + + CodeMode(dynamic_catalog=True) + ``` + + This pays off when paired with Tool Search: the tool-definitions block stays byte-stable so the prefix cache survives discoveries, at the cost of a larger (but cache-friendly) system prompt. With a fixed toolset and no Tool Search, the default keeps the system prompt shorter and is the better choice. + +- To instead keep a Tool Search corpus fully native -- never folded into `run_code`, but not callable from inside it -- exclude it with a `tools` selector; corpus members carry `with_native` set to the managing native tool: + + ```python + from pydantic_ai_harness import CodeMode + + CodeMode(tools=lambda ctx, td: td.with_native is None) + ``` + + +## Return values + +The last expression in the snippet is automatically captured as the return value -- the model does not need to `print()`. Reserve `print()` for supplementary logging: printed text is surfaced separately, wrapped alongside the last-expression result. + +Scenario + +Return + +No print output + +Last expression value + +With print output + +`{'output': '', 'result': }` + +Multimodal content (e.g. images) + +Returned natively for model processing + +## REPL state + +State persists between `run_code` calls within the same agent run -- variables, imports, and function definitions carry over. Pass `restart: true` in the tool call to reset state. + +## Observability + +Nested tool calls inside `run_code` produce their own spans when instrumented with [Logfire](https://pydantic.dev/logfire) or any OpenTelemetry backend -- the easiest way to understand what code mode actually did, since each `run_code` span fans out into the tool calls the model issued from inside the sandbox. See the [Pydantic AI Logfire docs](/docs/ai/integrations/logfire/) for setup. + +The `run_code` tool return also carries metadata with every nested call, keyed by call id: + +```python +from pydantic_ai import Agent +from pydantic_ai.messages import ToolReturnPart +from pydantic_ai_harness import CodeMode + +agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[CodeMode()]) + + +@agent.tool_plain +def get_weather(city: str) -> dict: + """Get current weather for a city.""" + return {'city': city, 'temp_f': 72} + + +result = agent.run_sync("What's the weather in Paris?") + +for msg in result.all_messages(): + for part in msg.parts: + if isinstance(part, ToolReturnPart) and part.tool_name == 'run_code': + metadata = part.metadata or {} + tool_calls = metadata['tool_calls'] # dict[str, ToolCallPart] + tool_returns = metadata['tool_returns'] # dict[str, ToolReturnPart] +``` + +## In practice + +A representative run wires `CodeMode` up against an MCP server and a web search and asks it to find the most-discussed Hacker News story across three feeds, pull the comment thread and the submitter's profile, and search the web for follow-up coverage. `CodeMode` collapses that into two `run_code` calls: the first fetches all three feeds in parallel via `asyncio.gather`, dedupes by id, filters by score, and ranks by comment count -- in plain Python; the second batches the three follow-up calls (`hn_get_thread`, `hn_get_user`, `duckduckgo_search`) together. + +[![CodeMode's first run_code: parallel asyncio.gather over three HN feeds, then a dedupe and a score filter](/docs/ai/harness/img/code-mode-trace.png)](https://logfire-us.pydantic.dev/public-trace/84bcf123-2106-49da-9f6f-5c26395339bb?spanId=7650806a0785b946) + +**[See the full Logfire trace ->](https://logfire-us.pydantic.dev/public-trace/84bcf123-2106-49da-9f6f-5c26395339bb?spanId=7650806a0785b946)** Each `run_code` span fans out into the tool calls the model issued from inside the sandbox. + +## Filesystem and OS access + +Sandboxed code runs with no access to the host's files, environment, or clock. Two parameters grant it access -- reach for them only when the agent's task genuinely needs the host. + +Both parameters are fixed when the capability is built, so construct `CodeMode` per request to scope host access to that request. + +### `mount` -- share host directories + +Reach for `mount` when the agent works with real files: analyzing a dataset you've dropped in a folder and writing a report back, editing a checkout, or processing a batch of documents. Sandboxed `pathlib` code reads and writes under the mounted path. (For environment variables or the clock, use `os_access` instead.) + +```python +from pydantic_ai import Agent +from pydantic_monty import MountDir +from pydantic_ai_harness import CodeMode + +# The agent can read /work/data.csv and write /work/summary.md back to the host: +agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[CodeMode(mount=MountDir('/work', '/tmp/agent-workspace', mode='read-write'))], +) +``` + +A `MountDir` defaults to copy-on-write `mode='overlay'`: the sandbox reads host files and sees its own writes, but those writes do **not** reach the host. Pass `mode='read-write'` to persist them, or `mode='read-only'` to forbid writes. `mount` also accepts a list of `MountDir` for multiple mount points. + +### `os_access` -- answer the sandbox's OS calls yourself + +Reach for `os_access` when the agent needs environment variables, the current date and time, or filesystem behavior you control. Hand it a ready-made OS implementation (`AbstractOS`), or a callback that decides each call -- so you can inject just the secrets it needs, pin "now" for reproducible runs, or route file access to your own store. + +```python +from pydantic_ai import Agent +from pydantic_monty import OSAccess +from pydantic_ai_harness import CodeMode + +# Give the agent a fixed set of environment values: +agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[CodeMode(os_access=OSAccess(environ={'API_BASE': 'https://api.example.com'}))], +) +``` + +A callback receives each OS call and decides its fate: + +```python +from pydantic_ai import Agent +from pydantic_monty import NOT_HANDLED +from pydantic_ai_harness import CodeMode + +allowed_env = {'API_KEY': 'sk-...'} + + +def my_os(fn, args, kwargs): + if fn == 'os.getenv': + # Answer the call: allow-listed keys resolve, every other key reads back + # as None -- absent, exactly like a real unset variable. + return allowed_env.get(args[0]) + # Refuse everything else: NOT_HANDLED makes the call fail in the sandbox. + return NOT_HANDLED + + +agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[CodeMode(os_access=my_os)]) +``` + +Your callback's return value decides the call's fate, and the two outcomes are easy to confuse: + +- **Return any value** -- including `None`, `''`, or `0` -- and that becomes the result the sandbox sees. `os.getenv` returning `None` looks exactly like a normal unset variable, so the agent's code keeps running. This is how you _hide_ something: answer with an empty value. +- **Return `NOT_HANDLED`** and the call is treated as unsupported: it raises inside the sandbox and the model gets a retry. This _refuses_ a capability outright -- use it to block, not to say "no value". Returning `NOT_HANDLED` for a key the agent reasonably expects will burn retries. + +Both expose the real host to model-written code + +`mount` and `os_access` hand model-generated Python real access to your filesystem and environment. Grant only what the task needs, and prefer constructing `CodeMode` per request so the granted access is scoped to that request. + +Monty-specific types + +These hooks use Monty's `AbstractOS`/`MountDir` types from `pydantic_monty`. + +## Sandbox restrictions + +Code runs inside [Monty](https://github.com/pydantic/monty), a sandboxed Python subset. Key restrictions: + +- No class definitions. +- No third-party imports. Allowed stdlib modules: `sys`, `typing`, `asyncio`, `math`, `json`, `re`, `datetime`, `os`, `pathlib` (each must be imported before use). +- No wall-clock or timing primitives by default: `asyncio.sleep`, `datetime.datetime.now()`, `datetime.date.today()`, and the `time` module. `datetime.datetime.now()` / `datetime.date.today()` become available with an `os_access` handler (above); `asyncio.sleep` and `time` never do. +- No `import *`. +- Filesystem I/O needs an `os_access` handler or a `mount`; `os.getenv` / `os.environ` need an `os_access` handler. +- Tools requiring approval or with deferred (`CallDeferred`) execution are sandboxed like any other tool; without a `HandleDeferredToolCalls` (or equivalent) capability on the agent to resolve them inline, calling one from `run_code` raises an error that surfaces to the model as a retry. + +## Agent spec (YAML/JSON) + +`CodeMode` works with Pydantic AI's [agent spec](/docs/ai/core-concepts/agent-spec/) feature for defining agents in YAML or JSON: + +```yaml +# agent.yaml +model: anthropic:claude-sonnet-4-6 +capabilities: + - CodeMode: {} +``` + +```python +from pydantic_ai import Agent +from pydantic_ai_harness import CodeMode + +agent = Agent.from_file('agent.yaml', custom_capability_types=[CodeMode]) +result = agent.run_sync('...') +print(result.output) +``` + +Pass `custom_capability_types` so the spec loader knows how to instantiate `CodeMode`. Arguments can be passed in the YAML too: + +```yaml +capabilities: + - CodeMode: + tools: ['search', 'fetch'] + max_retries: 5 +``` + +## Further reading + +- [Tool use via code](https://www.anthropic.com/engineering/code-execution-with-mcp) (Anthropic) +- [Code mode in production](https://blog.cloudflare.com/code-mode/) (Cloudflare) +- [Pydantic AI capabilities](/docs/ai/core-concepts/capabilities/) + +## API reference + +### CodeMode + +**Bases:** `AbstractCapability[AgentDepsT]` + +Capability that exposes selected tools as callables inside a `run_code` sandbox. + +By default (`tools='all'`) every tool the agent has is wrapped behind a single `run_code` tool -- the model writes Python that calls them as functions instead of issuing tool calls directly. + +Pass a list of tool names or a callable predicate to `tools` to split the toolset: matching tools become callables inside the sandbox, and the rest stay visible to the model as normal tool calls. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness import CodeMode + +# Sandbox all tools +agent = Agent('openai:gpt-5', capabilities=[CodeMode()]) + +# Sandbox only specific tools +agent = Agent('openai:gpt-5', capabilities=[CodeMode(tools=['search', 'fetch'])]) +``` + +By default, sandboxed code cannot touch the host -- no filesystem, environment variables, or clock. Two parameters open it up: + +- `mount` shares specific host directories: reach for it when the agent reads or writes real files. +- `os_access` routes the sandbox's OS calls to a handler you provide: reach for it when the agent needs environment variables, the clock, or filesystem behavior you control. + +Both expose the real host to model-written code, so grant only what the task needs. + +```python +from pydantic_monty import MountDir + +agent = Agent('openai:gpt-5', capabilities=[CodeMode(mount=MountDir('/work', '/tmp/agent-work'))]) +``` + +#### Attributes + +##### tools + +Which wrapped tools should be sandboxed inside `run_code`. + +- `'all'` (default): every tool the agent has is sandboxed. +- `Sequence[str]`: only tools whose names are listed are sandboxed. +- Callable `(ctx, tool_def) -> bool | Awaitable[bool]`: tools where the callable returns `True` are sandboxed; the rest stay as native tool calls. + +**Type:** `ToolSelector`\[`AgentDepsT`\] **Default:** `field(default='all')` + +##### max\_retries + +Maximum number of retries for the `run_code` tool (syntax errors count as retries). + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `3` + +##### os\_access + +Give sandboxed code environment variables, the clock, and file I/O through a handler you provide; unset, they are unavailable. + +**Type:** `CodeModeOS` | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### mount + +Host directories to expose to sandboxed `pathlib` code; each mount's `mode` controls whether writes reach the host. + +**Type:** `CodeModeMount` | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### dynamic\_catalog + +Keep the `run_code` tool definition cache-stable as the sandboxed toolset grows. + +By default the signatures of all sandboxed tools are rendered into `run_code`'s description, which lives in the prompt-cache-keyed tool-definitions block. When the toolset changes mid-run -- e.g. [`ToolSearch`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.ToolSearch) reveals a new tool that then gets folded into `run_code` -- the description changes and busts the prefix cache from that point on. + +Set `dynamic_catalog=True` to instead: + +- keep only the static base prose (sandbox restrictions, return-value contract) in `run_code.description`, so the tool-definitions block stays byte-stable across discoveries; +- move the "available functions" catalog (TypedDict definitions + signatures) into agent instructions as a dynamic [`InstructionPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.InstructionPart), which providers with static/dynamic instruction splitting (Anthropic, Bedrock) place after the cache breakpoint; +- announce newly-discovered tools via a short [`SystemPromptPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.SystemPromptPart) enqueued through [`RunContext.enqueue`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.enqueue), so the model knows the new functions are callable without rewriting the cached description. + +This pays off when paired with [`ToolSearch`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.ToolSearch): the tool-definitions cache survives discoveries at the cost of a larger (but cache-friendly) system prompt. With a fixed toolset and no `ToolSearch`, the default keeps the system prompt shorter and is the better choice. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `False` + +#### Methods + +##### get\_ordering + +```python +def get_ordering() -> CapabilityOrdering +``` + +CodeMode wraps around ToolSearch so that search\_tools stays native. + +###### Returns + +`CapabilityOrdering` + +##### for\_run + +`@async` + +```python +def for_run(ctx: RunContext[AgentDepsT]) -> CodeMode[AgentDepsT] +``` + +Return a fresh instance so concurrent runs don't share `_announced_tools`. + +###### Returns + +`CodeMode`\[`AgentDepsT`\] + +##### get\_wrapper\_toolset + +```python +def get_wrapper_toolset( + toolset: AbstractToolset[AgentDepsT], +) -> AbstractToolset[AgentDepsT] | None +``` + +Wrap the agent's assembled toolset, splitting it into native + sandboxed subsets if needed. + +###### Returns + +[`AbstractToolset`](/docs/ai/api/pydantic-ai/toolsets/#pydantic_ai.toolsets.AbstractToolset)\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### after\_tool\_execute + +`@async` + +```python +def after_tool_execute( + ctx: RunContext[AgentDepsT], + *, + call: ToolCallPart, + tool_def: ToolDefinition, + args: ValidatedToolArgs, + result: Any, +) -> Any +``` + +Announce newly-discovered tools from a local `search_tools` return. + +Only active with `dynamic_catalog=True`. The native-search path is handled by [`after_model_request`](/docs/ai/harness/code-mode/#pydantic_ai_harness.CodeMode.after_model_request) instead (server-side search emits a `NativeToolSearchReturnPart` rather than a regular tool execute result). + +###### Returns + +[`Any`](https://docs.python.org/3/library/typing.html#typing.Any) + +##### after\_model\_request + +`@async` + +```python +def after_model_request( + ctx: RunContext[AgentDepsT], + *, + request_context: ModelRequestContext, + response: ModelResponse, +) -> ModelResponse +``` + +Announce newly-discovered tools from a native (server-side) tool-search return. + +Only active with `dynamic_catalog=True`. + +###### Returns + +[`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse) + +--- + +# [Compaction](https://pydantic.dev/docs/ai/harness/compaction/) + +> A menu of strategies -- clear, dedupe, trim, or summarize -- for keeping an agent's conversation history within the model's context window. + +# Compaction + +Compaction is a menu of strategies for keeping an agent's conversation history within a model's context window. Each strategy is a Pydantic AI `Capability` that edits the message history just before each request goes out. The edits **persist** into the run's message history, so a trim, clear, or summary carries forward to later steps -- it is not recomputed from the full history every turn. + +All strategies preserve tool-call / tool-return **pairing**. Core does not validate this, and a provider rejects an orphaned pair, so the pairing guarantee is what makes these safe to drop into an agent. The zero-LLM strategies never call a model; only `SummarizingCompaction` (and `TieredCompaction` when it escalates that far) spends tokens. + +[Source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/compaction/) + +> The API may change between releases. Where practical, breaking changes ship with a deprecation warning. + +## The problem + +An agent that runs for many turns accumulates history: tool outputs, file reads, model reasoning, repeated content. Left unchecked, that history outgrows the model's context window and the next request fails. Compaction keeps the history bounded, and the right strategy depends on where the bloat lives and how much you can afford to spend reclaiming it. + +## The menu + +Capability + +Cost + +What it does + +Reach for it when + +`ClampOversizedMessages` + +zero-LLM + +Head/tail-truncates a single oversized part (response text, tool-call args) + +One runaway generation blew past the context cap and no other strategy can reach it + +`SlidingWindow` + +zero-LLM + +Drops the oldest whole messages down to a tail + +You only need the recent turns and can discard old context entirely + +`ClearToolResults` + +zero-LLM + +Blanks the content of old tool _results_ in place, keeping the last `keep_pairs` + +Tool outputs dominate context and can be re-fetched on demand (the cheap first tier) + +`DeduplicateFileReads` + +zero-LLM + +Blanks every file read superseded by a newer read of the same file + +The agent re-reads files and only the latest version matters + +`SummarizingCompaction` + +one LLM call + +Summarizes older messages into a structured summary, keeping the recent tail + +Old context still matters but must be compressed; use behind the cheap tiers + +`TieredCompaction` + +escalates + +Runs cheap passes first, summarizes only if still over `target_tokens` + +You want a sensible default: spend the expensive summary only when needed + +`LimitWarner` + +zero-LLM + +Injects an URGENT/CRITICAL warning as limits approach + +You want the agent to wrap up rather than have its history rewritten + +## Triggers + +Every size-based strategy triggers on `max_messages` and/or `max_tokens` (estimated). Token counts use a ~4-chars-per-token heuristic by default; pass a `tokenizer` callable (for example `tiktoken`) for accuracy. `DeduplicateFileReads` runs on every request when no trigger is set (it is cheap and near-lossless). `TieredCompaction` triggers and stops on a single `target_tokens` budget. `ClampOversizedMessages` triggers per _part_ (`max_part_tokens` / `max_part_chars`), not on the whole history -- the failure it targets is one oversized part, not a large total. + +## The recommended default: `TieredCompaction` + +The field consensus (Anthropic, OpenCode, Letta) is to clear and dedupe first, and summarize only when that is not enough. Summarization turns input tokens into output tokens, which are billed at a premium and generated serially, so it is genuinely expensive. The zero-LLM strategies touch only the cheaper input side. + +`TieredCompaction` encodes that escalation: it runs each tier in order, re-measures the token count after each, and stops as soon as the conversation fits `target_tokens`. Order the tiers cheap-to-expensive so the expensive summarization tier is only reached when the cheap passes cannot reclaim enough. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.compaction import ( + ClearToolResults, + DeduplicateFileReads, + SummarizingCompaction, + TieredCompaction, +) +from pydantic_ai.messages import ToolCallPart + + +def my_file_key(call: ToolCallPart) -> str | None: + if call.tool_name != 'read_file': + return None + return call.args_as_dict().get('path') + + +agent = Agent( + 'openai:gpt-4o', + capabilities=[ + TieredCompaction( + tiers=[ + DeduplicateFileReads(file_key=my_file_key), + ClearToolResults(max_tokens=1, keep_pairs=3), + SummarizingCompaction(max_messages=1, keep_messages=20), # model inherits the run's + ], + target_tokens=120_000, + ) + ], +) +``` + +A tier inside `TieredCompaction` is driven directly by the orchestrator, which re-measures after each tier and stops once under `target_tokens`. A tier's own `max_*` trigger is therefore irrelevant when it runs inside `TieredCompaction` -- set it to anything valid (for example `ClearToolResults(max_tokens=1)`). Any object with `async def compact(messages, ctx) -> list[ModelMessage]` (the `CompactionStrategy` protocol) can be a tier, so you can plug in your own. + +## `ClampOversizedMessages`: surviving a runaway generation + +A single model response of repeated whitespace, or a single tool call with a giant payload, can produce one part so large the _next_ request exceeds the provider's context cap. None of the other strategies can reach it: `SlidingWindow` drops the oldest messages but the offender is the newest; `ClearToolResults` only touches tool _results_; `LimitWarner` never edits history; and feeding the history to `SummarizingCompaction` hits the same cap. + +`ClampOversizedMessages` truncates the offending part in place, keeping a head slice and a tail slice with a `[clamped: removed N of M characters]` marker between them. Degenerate generations are low-entropy repetition, so a head/tail slice loses little. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.compaction import ClampOversizedMessages + +agent = Agent( + 'openai:gpt-4o', + capabilities=[ + ClampOversizedMessages(max_part_tokens=50_000, keep_head_chars=2_000, keep_tail_chars=2_000) + ], +) +``` + +A part is clamped only when it is oversized _and_ the clamp actually shrinks it, so keep `keep_head_chars + keep_tail_chars` well below your per-part threshold. + +It clamps two kinds of part inside each `ModelResponse`: + +- **Response text** (`TextPart`) -- the critical case, a runaway model-response text part. +- **Tool-call args** (`ToolCallPart`), when `clamp_tool_call_args=True` (the default) -- the same failure shape for a giant payload (for example a runaway `write_plan`). The args are replaced with a small JSON object `{"_clamped": "..."}` so they stay valid function arguments; the original call already executed, so this only shrinks the history copy. Set `clamp_tool_call_args=False` to clamp response text only. + +Request-side parts (user prompts, tool _returns_, system prompts) are deliberately out of scope: user input should not be silently rewritten, and oversized tool returns are the job of `ClearToolResults`. + +Use it as the first tier of `TieredCompaction`, before `ClearToolResults`: + +```python +from pydantic_ai_harness.compaction import ( + ClampOversizedMessages, + ClearToolResults, + TieredCompaction, +) + +TieredCompaction( + tiers=[ + ClampOversizedMessages(max_part_tokens=50_000), + ClearToolResults(max_tokens=1, keep_pairs=3), + ], + target_tokens=120_000, +) +``` + +## `ClearToolResults`: the cheap first tier + +Tool outputs typically dominate an agent's context, and the agent can usually re-run a tool if it needs the data again. `ClearToolResults` replaces the content of the oldest tool _results_ with a short placeholder while keeping the most recent `keep_pairs` tool-call / tool-return pairs intact. The tool calls stay paired with their now-blanked results, so the history stays valid. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.compaction import ClearToolResults + +agent = Agent( + 'openai:gpt-4o', + capabilities=[ClearToolResults(max_tokens=100_000, keep_pairs=3)], +) +``` + +Set `clear_tool_inputs=True` to also blank the arguments of the cleared calls, and `exclude_tools` to a set of tool names whose results are never cleared. + +## `DeduplicateFileReads`: drop superseded reads + +When the same file is read more than once, only the latest read keeps its content; earlier reads are blanked with a placeholder, with pairing preserved. + +There is no default `file_key`: identifying a file read is agent-specific, and a wrong guess would drop live data. Supply a callable mapping a `ToolCallPart` to a stable file key, or `None` when the call is not a file read: + +```python +from pydantic_ai import Agent +from pydantic_ai.messages import ToolCallPart +from pydantic_ai_harness.compaction import DeduplicateFileReads + + +def file_key(call: ToolCallPart) -> str | None: + if call.tool_name != 'read_file': + return None + return call.args_as_dict().get('path') + + +agent = Agent('openai:gpt-4o', capabilities=[DeduplicateFileReads(file_key=file_key)]) +``` + +With no `max_messages` or `max_tokens` trigger set, `DeduplicateFileReads` runs on every request. It is cheap and near-lossless, so that default is usually what you want. + +## `SlidingWindow`: keep only the recent tail + +When the conversation exceeds the configured threshold, `SlidingWindow` discards the oldest whole messages down to a tail, preserving tool-call / tool-return pairs. Reach for it when you only need the recent turns and can discard old context entirely. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.compaction import SlidingWindow + +agent = Agent( + 'openai:gpt-4o', + capabilities=[SlidingWindow(max_messages=80, keep_messages=40)], +) +``` + +By default `preserve_first_user_message=True` keeps the first user turn (in addition to system prompts) even when it falls outside the window, so the agent does not lose the original task. Pass `keep_tokens` instead of `keep_messages` to trim to a token budget rather than a message count. + +## `SummarizingCompaction`: compress, do not discard + +When old context still matters but must be compressed, `SummarizingCompaction` summarizes the older messages with a dedicated model call and replaces them with a single structured summary, preserving the recent tail and tool-call integrity. It is the expensive tier, so it is best used behind the cheaper passes (see `TieredCompaction`). + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.compaction import SummarizingCompaction + +agent = Agent( + 'openai:gpt-4o', + capabilities=[ + SummarizingCompaction( + model='openai:gpt-4o-mini', + max_messages=60, + keep_messages=20, + ) + ], +) +``` + +`model` accepts a model name or a `Model`; when left `None` it inherits the running agent's model. No token caps are imposed on the summary call. By default `incremental=True` extends any existing summary from a prior compaction rather than regenerating it from scratch. + +### Usage accounting + +The summary call is a real request to the model, so its full usage -- tokens **and** the request itself -- is folded into the run's `ctx.usage`. This is deliberate: it keeps cost honest, keeps the request count consistent (a model request that did not count as one would be the surprise), and lets a `UsageLimits` request limit catch a runaway compaction. A run-request or iteration limiter will therefore see compaction calls among its requests. + +## `LimitWarner`: warn instead of rewrite + +`LimitWarner` never edits history. As the run approaches a configured limit, it injects an URGENT (then CRITICAL) warning as a trailing user turn, so the model wraps up rather than having its context rewritten under it. Models tend to pay more attention to user messages than system messages, which is why the warning is a user turn. Previous warnings from this capability are stripped before deciding whether to inject a new one. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.compaction import LimitWarner + +agent = Agent( + 'openai:gpt-4o', + capabilities=[ + LimitWarner( + max_iterations=40, + max_context_tokens=100_000, + ) + ], +) +``` + +Warnings begin at `warning_threshold` (default `0.7`, a fraction of the limit) and become CRITICAL for iterations once the remaining request count drops to `critical_remaining_iterations` (default `3`). It watches three kinds of limit -- `max_iterations`, `max_context_tokens`, and `max_total_tokens` -- and by default warns on whichever are configured; narrow that with `warn_on`. + +## Cache tradeoff + +Clearing, deduplicating, clamping, and summarizing all rewrite message content, which invalidates the provider's prompt cache from the edit point onward -- the next request pays a cache-write. For `ClearToolResults`, use `min_clear_tokens` to skip clearing that reclaims too little to be worth busting the cache. For `ClampOversizedMessages` the cache bust is unavoidable, because the alternative is a failed request. + +## Tracing + +When core instrumentation is active (the `Instrumentation` capability, `agent.instrument`, or `Agent.instrument_all()`), each strategy emits a `compact_messages` span on the run's tracer the moment it actually compacts -- that is, in `before_model_request`, once the strategy's threshold is exceeded (`ClampOversizedMessages` emits only when a part is actually clamped). `TieredCompaction` emits a single span for the whole escalation rather than one per tier, because it drives each tier's `compact` directly. Without instrumentation the tracer is a no-op, so the span adds no overhead. + +The span name is the static `compact_messages`; the strategy is an attribute, not part of the name, to keep span cardinality low. Attributes: + +Attribute + +Type + +Meaning + +`gen_ai.conversation.compacted` + +bool + +Always `true`; the OpenTelemetry GenAI convention's flag for a compacted context + +`compaction.strategy` + +str + +Strategy class name (for example `SlidingWindow`, `SummarizingCompaction`) + +`compaction.messages_before` + +int + +Message count before compaction + +`compaction.messages_after` + +int + +Message count after compaction + +`compaction.tokens_before` + +int + +Estimated token count before compaction + +`compaction.tokens_after` + +int + +Estimated token count after compaction + +`gen_ai.conversation.compacted` is the GenAI semantic convention's flag; the rest is harness-specific. Token counts use the strategy's `tokenizer` when set, otherwise the ~4-chars-per-token heuristic. Raw message content is not recorded. + +## Out of scope + +These strategies compress or drop context _inside_ the window. Moving large tool outputs _out_ of the window -- overflowing them to a file the agent (or a subagent) can query on demand -- is a separate capability ([overflowing tool output](/docs/ai/harness/overflowing-tool-output)), not lossy truncation. Prefer it over capping individual tool outputs. + +## API reference + +The recommended default is `TieredCompaction`; the other strategies below can be used standalone or plugged in as its tiers. + +### TieredCompaction + +**Bases:** `AbstractCapability[AgentDepsT]` + +Escalation orchestrator over a sequence of compaction strategies. + +Runs each tier in order, re-measuring the token count after each, and stops as soon as the conversation fits `target_tokens`. Order tiers cheap-to-expensive (e.g. clear tool results, deduplicate reads, then summarize) so the expensive summarization tier is only reached when the cheap passes cannot reclaim enough. + +Each tier's own trigger is bypassed -- `TieredCompaction` drives the tiers directly via their `compact` method and decides when to stop. + +#### Attributes + +##### tiers + +Strategies to apply in order, cheap-to-expensive. The last is typically a summarizer. + +**Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[`CompactionStrategy`\[`AgentDepsT`\]\] + +##### target\_tokens + +Stop escalating once the estimated token count is at or below this value. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) + +##### tokenizer + +Optional tokenizer for accurate token counting. + +A callable that returns the token count for a given string. When `None`, uses a ~4 characters-per-token heuristic. + +**Type:** [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\], [`int`](https://docs.python.org/3/library/functions.html#int)\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +#### Methods + +##### compact + +`@async` + +```python +def compact( + messages: list[ModelMessage], + ctx: RunContext[AgentDepsT], +) -> list[ModelMessage] +``` + +Apply tiers in order until the history fits `target_tokens` or tiers run out. + +###### Returns + +[`list`](https://docs.python.org/3/glossary.html#term-list)\[[`ModelMessage`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelMessage)\] + +##### before\_model\_request + +`@async` + +```python +def before_model_request( + ctx: RunContext[AgentDepsT], + request_context: ModelRequestContext, +) -> ModelRequestContext +``` + +Escalate through the tiers when the conversation exceeds `target_tokens`. + +###### Returns + +`ModelRequestContext` + +### ClampOversizedMessages + +**Bases:** `AbstractCapability[AgentDepsT]` + +Zero-cost head/tail truncation of any single oversized message part. + +A runaway generation -- a model response of repeated whitespace, a giant tool-call payload -- can produce one part so large the next request exceeds the provider's context cap. The size-based strategies cannot help: `SlidingWindow` drops the _oldest_ messages (the offender is the newest), `ClearToolResults` only touches tool _results_, and feeding the history to `SummarizingCompaction` hits the same cap. This strategy truncates the offending part in place: it keeps a head slice and a tail slice and inserts a marker for the removed middle. Degenerate generations are low-entropy repetition, so a head/tail slice loses little. No LLM calls are made. + +What it clamps, in each `ModelResponse`: + +- `TextPart` content (the critical case -- a runaway model-response text part). +- `ToolCallPart` args, when `clamp_tool_call_args` is set (the same failure shape for a giant tool-call payload). The args are replaced with a small JSON object so they stay valid function arguments; the original call already executed, so this only shrinks the history copy. + +Request-side parts (user prompts, tool returns, system prompts) are out of scope: user input should not be silently rewritten, and oversized tool _returns_ are the job of `ClearToolResults`. + +Clamping rewrites message content, so it invalidates the provider's prompt cache from the clamped message onward. That is unavoidable here -- the alternative is a failed request. + +A part is clamped only when it is oversized _and_ the clamp actually shrinks it, so set `keep_head_chars` + `keep_tail_chars` well below your per-part threshold. + +Composes as the first tier of a `TieredCompaction` (run it before `ClearToolResults`): it is the only zero-LLM way to keep a run alive after a runaway generation. + +#### Attributes + +##### max\_part\_tokens + +Clamp a part whose estimated token count exceeds this value. `None` disables this trigger. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### max\_part\_chars + +Clamp a part whose character count exceeds this value. `None` disables this trigger. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### keep\_head\_chars + +Characters of the part's head to retain. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `2000` + +##### keep\_tail\_chars + +Characters of the part's tail to retain. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `2000` + +##### clamp\_tool\_call\_args + +When `True`, also clamp oversized `ToolCallPart` args, not just response text. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `True` + +##### tokenizer + +Optional tokenizer for accurate token counting. + +A callable that returns the token count for a given string. When `None`, uses a ~4 characters-per-token heuristic. + +**Type:** [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\], [`int`](https://docs.python.org/3/library/functions.html#int)\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +#### Methods + +##### compact + +`@async` + +```python +def compact( + messages: list[ModelMessage], + ctx: RunContext[AgentDepsT], +) -> list[ModelMessage] +``` + +Clamp every oversized response text part (and tool-call args, if enabled). + +###### Returns + +[`list`](https://docs.python.org/3/glossary.html#term-list)\[[`ModelMessage`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelMessage)\] + +##### before\_model\_request + +`@async` + +```python +def before_model_request( + ctx: RunContext[AgentDepsT], + request_context: ModelRequestContext, +) -> ModelRequestContext +``` + +Clamp any oversized response part before the request is sent. + +###### Returns + +`ModelRequestContext` + +### ClearToolResults + +**Bases:** `AbstractCapability[AgentDepsT]` + +Zero-cost in-place clearing of old tool results. + +Replaces the content of the oldest tool _results_ with a short placeholder while keeping the most recent `keep_pairs` tool-call / tool-return pairs intact. Tool calls remain paired with their (now-blanked) results, so the history stays valid. No LLM calls are made. + +This is the cheap first tier of compaction -- tool results typically dominate context, and the agent can re-run a tool if it needs the data again. + +Cache tradeoff: clearing rewrites message content, which invalidates the provider's prompt cache from the clear point onward (the next request pays a cache-write). Use `min_clear_tokens` to skip clearing that reclaims too little to be worth busting the cache. + +#### Attributes + +##### max\_messages + +Trigger clearing when message count reaches this value. `None` disables. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### max\_tokens + +Trigger clearing when estimated token count reaches this value. `None` disables. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### keep\_pairs + +Number of most-recent tool-call / tool-return pairs left untouched. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `3` + +##### placeholder + +Replacement content for a cleared tool result. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) **Default:** `'[tool result cleared]'` + +##### exclude\_tools + +Tool names whose results are never cleared. + +**Type:** [`frozenset`](https://docs.python.org/3/library/stdtypes.html#frozenset)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `frozenset()` + +##### clear\_tool\_inputs + +When `True`, also blank the arguments of the cleared tool calls. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `False` + +##### min\_clear\_tokens + +Only clear if doing so reclaims at least this many estimated tokens. + +Protects the prompt cache from being invalidated for a trivial gain. `None` always clears. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### tokenizer + +Optional tokenizer for accurate token counting. + +A callable that returns the token count for a given string. When `None`, uses a ~4 characters-per-token heuristic. + +**Type:** [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\], [`int`](https://docs.python.org/3/library/functions.html#int)\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +#### Methods + +##### compact + +`@async` + +```python +def compact( + messages: list[ModelMessage], + ctx: RunContext[AgentDepsT], +) -> list[ModelMessage] +``` + +Blank the oldest tool results beyond the most recent `keep_pairs`. + +###### Returns + +[`list`](https://docs.python.org/3/glossary.html#term-list)\[[`ModelMessage`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelMessage)\] + +##### before\_model\_request + +`@async` + +```python +def before_model_request( + ctx: RunContext[AgentDepsT], + request_context: ModelRequestContext, +) -> ModelRequestContext +``` + +Clear old tool results if the conversation exceeds the configured threshold. + +###### Returns + +`ModelRequestContext` + +### DeduplicateFileReads + +**Bases:** `AbstractCapability[AgentDepsT]` + +Zero-cost in-place clearing of superseded file reads. + +When the same file is read more than once, only the latest read keeps its content; earlier reads are blanked with a placeholder. Tool-call pairing is preserved. No LLM calls are made. + +File identity is supplied by the `file_key` seam -- given a `ToolCallPart` it returns a stable key for the file being read, or `None` if the call is not a file read. There is no default: file-read identification is agent-specific, and a wrong guess would drop live data. + +#### Attributes + +##### file\_key + +Map a tool call to a stable file key, or `None` if it is not a file read. + +**Type:** [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[[`ToolCallPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ToolCallPart)\], [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None)\] + +##### placeholder + +Replacement content for a superseded file read. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) **Default:** `'[superseded file read]'` + +##### max\_messages + +Optional message-count trigger. When both triggers are `None`, runs whenever invoked. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### max\_tokens + +Optional token-count trigger. When both triggers are `None`, runs whenever invoked. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### tokenizer + +Optional tokenizer for accurate token counting. + +A callable that returns the token count for a given string. When `None`, uses a ~4 characters-per-token heuristic. + +**Type:** [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\], [`int`](https://docs.python.org/3/library/functions.html#int)\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +#### Methods + +##### compact + +`@async` + +```python +def compact( + messages: list[ModelMessage], + ctx: RunContext[AgentDepsT], +) -> list[ModelMessage] +``` + +Blank every file read that is later superseded by a newer read of the same file. + +###### Returns + +[`list`](https://docs.python.org/3/glossary.html#term-list)\[[`ModelMessage`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelMessage)\] + +##### before\_model\_request + +`@async` + +```python +def before_model_request( + ctx: RunContext[AgentDepsT], + request_context: ModelRequestContext, +) -> ModelRequestContext +``` + +Deduplicate file reads, optionally gated on a size threshold. + +###### Returns + +`ModelRequestContext` + +### SlidingWindow + +**Bases:** `AbstractCapability[AgentDepsT]` + +Zero-cost sliding-window trimmer. + +When the conversation exceeds a configurable threshold (message count or estimated token count), the oldest messages are discarded while preserving tool-call / tool-return pairs. No LLM calls are made. + +Trimming happens in `before_model_request` so it is transparent to the rest of the agent run. + +#### Attributes + +##### max\_messages + +Trigger trimming when message count reaches this value. `None` disables. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### max\_tokens + +Trigger trimming when estimated token count reaches this value. `None` disables. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### keep\_messages + +Number of tail messages to retain after trimming (message-count trigger). + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `40` + +##### keep\_tokens + +Target token budget after trimming (token-count trigger). + +When `None`, falls back to `keep_messages`. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### tokenizer + +Optional tokenizer for accurate token counting. + +A callable that returns the token count for a given string. When `None`, uses a ~4 characters-per-token heuristic. + +**Type:** [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\], [`int`](https://docs.python.org/3/library/functions.html#int)\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### preserve\_first\_user\_message + +When `True`, the first `ModelRequest` containing a `UserPromptPart` is always kept after trimming, in addition to system prompts. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `True` + +#### Methods + +##### compact + +`@async` + +```python +def compact( + messages: list[ModelMessage], + ctx: RunContext[AgentDepsT], +) -> list[ModelMessage] +``` + +Drop the oldest messages down to the configured tail. + +###### Returns + +[`list`](https://docs.python.org/3/glossary.html#term-list)\[[`ModelMessage`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelMessage)\] + +##### before\_model\_request + +`@async` + +```python +def before_model_request( + ctx: RunContext[AgentDepsT], + request_context: ModelRequestContext, +) -> ModelRequestContext +``` + +Trim the message list if it exceeds the configured threshold. + +###### Returns + +`ModelRequestContext` + +### SummarizingCompaction + +**Bases:** `AbstractCapability[AgentDepsT]` + +LLM-powered conversation compaction. + +When the conversation exceeds a configurable threshold, older messages are summarized using a dedicated model call and replaced with a compact, structured summary message, preserving recent context and tool-call integrity. + +This is the expensive tier -- summarization turns input tokens into (pricier) output tokens -- so it is best used behind cheaper passes (see `TieredCompaction`). + +The summary call's usage is folded into the parent run's usage (it counts as a real request), so cost accounting stays honest; note this also increments the run's request count, which a request-count limiter would see. + +#### Attributes + +##### model + +Model used to generate summaries. When `None`, inherits the running agent's model. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | `Model` | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### max\_messages + +Trigger compaction when message count exceeds this value. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### max\_tokens + +Trigger compaction when estimated token count exceeds this value. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### keep\_messages + +Number of tail messages to preserve after compaction (message-count trigger). + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `20` + +##### keep\_tokens + +Target token budget to preserve after compaction (token-count trigger). + +When `None`, falls back to `keep_messages`. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### summary\_prompt + +Prompt template for generating summaries. + +Must contain a `{messages}` placeholder. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) **Default:** `_DEFAULT_SUMMARY_PROMPT` + +##### tokenizer + +Optional tokenizer for accurate token counting. + +A callable that returns the token count for a given string. When `None`, uses a ~4 characters-per-token heuristic. + +**Type:** [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\], [`int`](https://docs.python.org/3/library/functions.html#int)\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### preserve\_first\_user\_message + +When `True`, the first `ModelRequest` containing a `UserPromptPart` is always kept after compaction, in addition to system prompts. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `True` + +##### incremental + +When `True`, include any existing summary from a prior compaction in the summarization prompt so that it is extended rather than regenerated from scratch. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `True` + +#### Methods + +##### compact + +`@async` + +```python +def compact( + messages: list[ModelMessage], + ctx: RunContext[AgentDepsT], +) -> list[ModelMessage] +``` + +Summarize older messages, replacing them with a single summary message. + +###### Returns + +[`list`](https://docs.python.org/3/glossary.html#term-list)\[[`ModelMessage`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelMessage)\] + +##### before\_model\_request + +`@async` + +```python +def before_model_request( + ctx: RunContext[AgentDepsT], + request_context: ModelRequestContext, +) -> ModelRequestContext +``` + +Summarize older messages when the threshold is exceeded. + +###### Returns + +`ModelRequestContext` + +### LimitWarner + +**Bases:** `AbstractCapability[AgentDepsT]` + +Injects a warning message when the agent approaches configured limits. + +The warning is appended as a trailing `ModelRequest` with a `UserPromptPart` so that the model treats it as a distinct user turn (models tend to pay more attention to user messages than system messages). + +Previous warnings injected by this capability are stripped before deciding whether to inject a new one. + +#### Attributes + +##### max\_iterations + +Maximum allowed requests for the run. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### max\_context\_tokens + +Maximum context-window size to warn against. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### max\_total\_tokens + +Maximum cumulative run token budget to warn against. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### warn\_on + +Which limits should emit warnings. Defaults to all configured limits. + +**Type:** [`list`](https://docs.python.org/3/glossary.html#term-list)\[`WarningKind`\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### warning\_threshold + +Fraction of a limit at which warnings begin (between 0 and 1). + +**Type:** [`float`](https://docs.python.org/3/library/functions.html#float) **Default:** `0.7` + +##### critical\_remaining\_iterations + +Remaining request count at which iteration warnings become CRITICAL. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `3` + +#### Methods + +##### before\_model\_request + +`@async` + +```python +def before_model_request( + ctx: RunContext[AgentDepsT], + request_context: ModelRequestContext, +) -> ModelRequestContext +``` + +Strip old warnings, then inject a new one if thresholds are exceeded. + +###### Returns + +`ModelRequestContext` + +--- + +# [Context](https://pydantic.dev/docs/ai/harness/context/) + +> Discover and load a repo's accumulated coding-assistant context engineering -- instruction files, skills, sub-agents, and hooks. + +# Context + +`RepoContext` discovers and loads a repo's accumulated coding-assistant context engineering (CE): the instruction files (`CLAUDE.md`/`AGENTS.md`) scattered across the tree and the assets under `.claude`/`.agents`/`.codex`/`.grok` (skills, sub-agents, hooks). + +[Source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/context/) + +> The API may change between releases. Where practical, breaking changes ship with a deprecation warning. + +## The problem + +A repo accumulates CE for whatever coding assistant worked in it: instruction files (`CLAUDE.md`/`AGENTS.md`) scattered across the tree, and assets under `.claude`/`.agents`/`.codex`/`.grok` (skills, sub-agents, hooks). An agent that loads only the top-level instruction file misses the ancestor context and has no idea the rest of the setup exists, so it can neither honor it nor translate it. + +## The solution + +`RepoContext` bundles three strategies, each independently toggleable. Construct it with `RepoContext(...)` in an `Agent`'s `capabilities`, anchored at the deepest directory the agent works in: + +```python +from pathlib import Path + +from pydantic_ai import Agent +from pydantic_ai_harness.context import RepoContext + +agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[RepoContext(workspace_dir=Path('.'), home_dir=Path.home())], +) + +result = agent.run_sync('Summarize the coding-assistant setup in this repo.') +print(result.output) +``` + +### 1\. Walk-up instruction autoload (on by default) + +Loads `CLAUDE.md`/`AGENTS.md` from `workspace_dir` and every ancestor up to `home_dir` (inclusive). Precedence is ancestor-first, workspace-last: broadest context first, most specific last. Files are deduped by resolved real path and by content hash, so a symlinked `AGENTS.md -> CLAUDE.md` or two ancestors sharing identical content load once. + +When `home_dir` is `None` (the default), only `workspace_dir` is scanned -- no walk-up. Pass `home_dir=Path.home()` to walk up to your home directory. + +### 2\. Asset inventory (on by default) + +Exposes one tool, `inventory_agent_context()`, that reports where the repo's CE assets live -- the `.claude`/`.agents`/`.codex`/`.grok` roots and, within each, the `skills/` (`SKILL.md`), `agents/` (`.md`), and `settings.json` (hooks) it contains. It returns a structured `AgentContextInventory`; it locates assets and does not parse them, leaving translation to the orchestrator. + +Rename the tool with `inventory_tool_name`, or scope which roots it scans with `asset_roots`. + +### 3\. Nested-on-traversal (off by default) + +When the model lists or reads a directory, surface that directory's `CLAUDE.md`/`AGENTS.md`. This couples to the host's list/read tools, so it is opt-in and configurable: + +```python +from pathlib import Path + +from pydantic_ai import Agent +from pydantic_ai_harness import FileSystem +from pydantic_ai_harness.context import RepoContext + +agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[ + FileSystem(root_dir='.'), + RepoContext( + workspace_dir=Path('.'), + nested_traversal=True, + traversal_tool_names=frozenset({'list_directory', 'read_file'}), # the FileSystem tool names to hook + traversal_path_arg='path', # the path arg key + nested_inject='pointer', # or 'contents' + ) + ], +) +``` + +`nested_inject='pointer'` (default) appends a one-line note pointing at the file; `'contents'` inlines the file body. Each directory is surfaced at most once per run. + +## Cache cost + +Injecting file contents into the system prompt costs prompt-cache stability: a changed prefix re-bills the whole cached region. `RepoContext` keeps the two cache-relevant paths separate: + +- Strategy 1 reads its files once at run start and injects them as static system instructions, so the cached prefix stays byte-identical across turns. +- Strategy 3 is volatile (it depends on which directory was just touched), so its note is appended to the tool result in the message tail -- never to the system prompt -- and cannot invalidate the cached prefix. + +## Configuration + +```python +RepoContext( + workspace_dir, # Path -- the deepest dir the agent works in (required) + home_dir=None, # Path | None -- shallowest dir to stop walk-up at, inclusive + filenames=('CLAUDE.md', 'AGENTS.md'), + autoload_instructions=True, # Strategy 1 + expose_inventory_tool=True, # Strategy 2 + inventory_tool_name='inventory_agent_context', + nested_traversal=False, # Strategy 3 + nested_inject='pointer', # 'pointer' | 'contents' + traversal_tool_names=frozenset({'list_directory', 'read_file'}), + traversal_path_arg='path', + asset_roots=('.claude', '.agents', '.codex', '.grok'), +) +``` + +## Scope + +`RepoContext` locates and loads CE; it does not parse skill/sub-agent frontmatter or hook bodies, and it does not rewrite or translate assets. Strategy 1 reads its files once per run, so mid-run edits to those files are not reloaded. + +## Further reading + +- [Pydantic AI capabilities](/docs/ai/core-concepts/capabilities/) +- [Pydantic AI hooks](/docs/ai/core-concepts/hooks/) + +## API reference + +### RepoContext + +**Bases:** `AbstractCapability[AgentDepsT]` + +Discover and load a repo's accumulated coding-assistant context engineering. + +Three strategies, each independently toggleable: + +1. Walk-up instruction autoload (`autoload_instructions`, on by default): load `CLAUDE.md`/`AGENTS.md` from `workspace_dir` and every ancestor up to `home_dir`, deduped, ancestor-first. These are read once at run start and injected as **static system instructions** via `get_instructions`, so they stay in the cached prefix and never re-read per turn. + +2. Asset inventory (`expose_inventory_tool`, on by default): a tool that reports where the repo's CE assets live (`.claude`/`.agents`/`.codex`/ `.grok` and their `skills/`, `agents/`, `settings.json`). It locates assets; it does not parse them. + +3. Nested-on-traversal (`nested_traversal`, off by default): when the model lists or reads a directory (via a tool named in `traversal_tool_names`), surface that directory's `CLAUDE.md`/`AGENTS.md`. The note is appended to the **tool result** (message tail), not to system instructions, so it does not invalidate the cached prefix. `nested_inject='pointer'` (default) appends a one-line pointer; `'contents'` inlines the file body. + + +Cache note: injecting file contents into the system prompt costs prompt-cache stability. Strategy 1 is safe because its files are static; the volatile Strategy 3 content rides in the message tail instead. + +```python +from pathlib import Path + +from pydantic_ai import Agent +from pydantic_ai_harness.context import RepoContext + +agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[RepoContext(workspace_dir=Path('.'), home_dir=Path.home())], +) +``` + +#### Attributes + +##### workspace\_dir + +The deepest directory the agent works in. The walk-up and asset scan are anchored here. + +**Type:** `Path` + +##### home\_dir + +The shallowest directory to stop the walk-up at, inclusive. `None` (the default) scans only `workspace_dir` -- no walk-up. + +**Type:** `Path` | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### filenames + +Instruction filenames to look for, in within-directory precedence order. + +**Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `('CLAUDE.md', 'AGENTS.md')` + +##### autoload\_instructions + +Strategy 1: load instruction files into the system prompt. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `True` + +##### expose\_inventory\_tool + +Strategy 2: expose the asset-inventory tool. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `True` + +##### inventory\_tool\_name + +Name of the inventory tool exposed to the model. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) **Default:** `'inventory_agent_context'` + +##### nested\_traversal + +Strategy 3: surface a directory's instruction file when the model lists or reads that directory. Off by default -- it couples to the list/read tools. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `False` + +##### nested\_inject + +For Strategy 3: append a one-line `pointer`, or inline the file `contents`. + +**Type:** [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\['pointer', 'contents'\] **Default:** `'pointer'` + +##### traversal\_tool\_names + +Tool names that trigger Strategy 3. Override to match the host's list/read tools (e.g. `frozenset({'list_dir', 'read_file'})`). + +**Type:** [`frozenset`](https://docs.python.org/3/library/stdtypes.html#frozenset)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `frozenset({'list_directory', 'read_file'})` + +##### traversal\_path\_arg + +The tool argument key holding the listed/read path. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) **Default:** `'path'` + +##### asset\_roots + +Root directories the inventory tool scans, relative to `workspace_dir`. + +**Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `('.claude', '.agents', '.codex', '.grok')` + +#### Methods + +##### for\_run + +`@async` + +```python +def for_run(ctx: RunContext[AgentDepsT]) -> RepoContext[AgentDepsT] +``` + +Return a fresh per-run instance with isolated traversal/cache state. + +###### Returns + +`RepoContext`\[`AgentDepsT`\] + +##### get\_instructions + +```python +def get_instructions() -> AgentInstructions[AgentDepsT] | None +``` + +Static, cache-stable instructions: loaded files plus the inventory hint. + +###### Returns + +`AgentInstructions`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### get\_toolset + +```python +def get_toolset() -> AgentToolset[AgentDepsT] | None +``` + +The asset-inventory toolset, or `None` when the tool is disabled. + +###### Returns + +`AgentToolset`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### after\_tool\_execute + +`@async` + +```python +def after_tool_execute( + ctx: RunContext[AgentDepsT], + *, + call: ToolCallPart, + tool_def: ToolDefinition, + args: dict[str, Any], + result: Any, +) -> Any +``` + +Strategy 3: append a directory's instruction file to a list/read result. + +###### Returns + +[`Any`](https://docs.python.org/3/library/typing.html#typing.Any) + +##### get\_serialization\_name + +`@classmethod` + +```python +def get_serialization_name(cls) -> str | None +``` + +Serialization name for agent-spec support. + +###### Returns + +[`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) + +### AgentContextInventory + +**Bases:** `BaseModel` + +A map of where a repo's CE assets live, for an orchestrator to read or translate. + +### AssetRoot + +**Bases:** `BaseModel` + +Where CE assets live under a single root directory (e.g. `.claude`). + +--- + +# [Dynamic Workflow](https://pydantic.dev/docs/ai/harness/dynamic-workflow/) + +> Let an orchestrator agent coordinate a catalog of sub-agents by writing one sandboxed Python script -- fan-out, chaining, voting, and retry loops in a single tool call. + +# Dynamic Workflow + +`DynamicWorkflow` is for the case where the coordination _between_ sub-agents is the actual work. Say you have a few specialists -- one reviews code, one summarizes findings, one writes the final note. Each is easy to call on its own; the hard part is the choreography: review three files at once, keep only the reports that found something, summarize those, and hand the summary to the writer. Reach for this capability when that orchestration involves fan-out, chaining, voting, or retry loops that you do not want to run one model turn at a time, with every intermediate result flowing back through the orchestrator's context. + +Import path + +Import this capability from its submodule -- there is no top-level `pydantic_ai_harness` re-export: + +```python +from pydantic_ai_harness.dynamic_workflow import DynamicWorkflow +``` + +> The API may change between releases. Where practical, breaking changes ship with a deprecation warning. + +## The idea + +The usual way to coordinate sub-agents is one tool call per step. The agent calls the reviewer and waits, reads the result, calls the reviewer again, waits again, and so on. Every intermediate result travels back into the agent's context, and every step that depends on the previous one is a separate model turn. + +`DynamicWorkflow` takes a different route. You hand it a catalog of named sub-agents, and it gives the model a single tool, `run_workflow`. Inside that tool the model writes ordinary Python: each of your sub-agents is an `async` function it can call, loop over, and combine. The script runs to completion in one tool call, and only its final value comes back to the model. The choreography moves out of the conversation and into code. + +If you have met [Code Mode](/docs/ai/harness/code-mode), this will feel familiar -- the same [Monty](https://github.com/pydantic/monty) sandbox and the same idea: write a script instead of many tool calls. The difference is what the script gets to call. In Code Mode it calls the agent's own tools; here it calls whole sub-agents. + +## How this relates to Subagents + +The harness has two delegation capabilities. They trade in the same currency -- named, isolated sub-agent runs -- but at different altitudes: + +- [`SubAgents`](/docs/ai/harness/subagents) exposes one `delegate_task(agent_name, task)` tool. Each delegation is its own tool call and its own model turn. It is the right fit when delegations are occasional, or when each result needs the parent's judgment before the next one. +- `DynamicWorkflow` moves the choreography into a script. Fan-out, chaining, voting, and retry loops all run inside one tool call, and intermediate results never enter the parent's context. + +Start with `SubAgents` if you are not sure. A `delegate_task` orchestrator converts to a workflow catalog without changing the sub-agents themselves. + +## Installation + +The script runs inside the Monty sandbox, so install the extra: + +Terminal + +```bash +uv add "pydantic-ai-harness[dynamic-workflow]" +``` + +## Your first workflow + +Two sub-agents, one orchestrator: + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.dynamic_workflow import DynamicWorkflow + +reviewer = Agent('openai:gpt-5', name='reviewer', description='Reviews code for bugs.') +summarizer = Agent('openai:gpt-5', name='summarizer', description='Summarizes findings.') + +orchestrator = Agent( + 'openai:gpt-5', + capabilities=[DynamicWorkflow(agents=[reviewer, summarizer])], +) +``` + +`reviewer` and `summarizer` are plain agents -- the same `Agent` you already know. Their `name` becomes the function name the model calls in the script, so pick names that are valid Python identifiers. Their `description` tells the model what each one is for; write it the way you would document a function. `DynamicWorkflow(agents=[...])` bundles them into one capability and hands the orchestrator a single `run_workflow` tool. + +## What the model does with it + +When the orchestrator decides to use the tool, it does not call your sub-agents one at a time. It writes a script: + +```python +import asyncio + +reports = await asyncio.gather( + reviewer(task="Review auth.py for bugs:\n"), + reviewer(task="Review parser.py for bugs:\n"), +) +await summarizer(task="Summarize these review findings:\n" + "\n\n".join(reports)) +``` + +The parts that matter: + +- Each sub-agent is an `async` function. You call it with `await`. +- You pass the work as a single keyword argument, `task`. Always by keyword -- `reviewer(task="...")`, not `reviewer("...")`. +- `asyncio.gather(...)` runs the two reviews concurrently instead of one after the other. +- The last expression's value becomes the result the model sees. The intermediate `reports` list never leaves the sandbox. + +Each call is a full `Agent.run`, with its own model loop, message history, tools, and typed output. Two things follow: calls are **isolated** (a sub-agent remembers nothing from an earlier call, so put everything it needs into `task`), and calls **cost tokens and take time** (which is why this capability gives you budgets, below). + +## Sub-agents can return structured data + +A sub-agent returns whatever its `output_type` produces. The default is a string, but give a sub-agent a Pydantic model and the script receives a `dict`: + +```python +from pydantic import BaseModel + +class Score(BaseModel): + value: int + reason: str + +critic = Agent('openai:gpt-5', name='critic', description='Scores an answer 0-10.', output_type=Score) +``` + +Inside the script the model reads the fields by subscript, the way it would read a JSON object: + +```python +result = await critic(task="Score this answer: ...") +result["value"] # not result.value +``` + +The catalog the model sees renders each output type as a `TypedDict`, so it knows the fields and reads them by subscript on its own. + +## How results come back + +The value of the script's last expression becomes the tool result -- the model does not `print()` it. + +The script... + +The model receives + +ends in a value, no print + +that value directly (or `{}` if it is `None`) + +prints and ends in a value + +`{"output": "", "result": }` + +prints and ends in `None` + +`{"output": ""}` + +`print()` is for debug logging; it stringifies, so let the last expression carry the real result. + +## Choosing sub-agent models + +By default each sub-agent uses the model it was constructed with. Set `inherit_model=True` when the host passes a per-run model override to the parent agent (for example from a `/model` command) and every sub-agent dispatch should follow that resolved parent model. Leave it `False` when a sub-agent is deliberately pinned to a different model. + +## Keeping it safe: budgets + +A sub-agent is non-deterministic, costs tokens, and can fan out into more sub-agents. `DynamicWorkflow` gives you a hard count ceiling, token budgets, and a guard against runaway sandbox scripts. + +### `max_agent_calls` -- an exact count + +```python +DynamicWorkflow(agents=[...], max_agent_calls=50) # 50 is the default +``` + +A hard, host-enforced ceiling on the number of sub-agent runs in one parent run. It is one budget shared across every `run_workflow` call in that run, and it holds exactly even when the script fans out with `asyncio.gather`. When the budget runs out, the workflow stops calling sub-agents and returns a terminal result that includes the sub-agent results that did complete, so nothing you already paid for is wasted. This is the only knob that bounds the number of runs exactly. + +### `sub_agent_usage_limits` and `forward_usage` -- bounding cost + +`sub_agent_usage_limits` is a `UsageLimits` applied to each sub-agent run. `forward_usage` controls whether the whole tree shares one usage counter: + +`forward_usage` + +Counter + +What the limit means + +`True` (default) + +the parent's `usage` is shared across the tree + +a tree-wide cap. Under concurrent fan-out it is best-effort: several sub-agents can pass the check before any of them adds to the count. + +`False` + +each sub-agent run counts on its own + +per-run limits. A per-run `total_tokens_limit` of `T` with `max_agent_calls` of `N` bounds the tree to roughly `N * T` tokens. + +The parent `run()` usage limit is not forwarded + +The `usage_limits` you pass to the parent `run()` is not forwarded into sub-agents -- it is re-checked only at the parent's own request boundaries. To bound sub-agents, set `sub_agent_usage_limits`; for an exact ceiling on the number of runs, use `max_agent_calls`. + +### `resource_limits` -- guarding the script itself + +These limits guard the orchestration script's own memory and allocations, not the sub-agents it calls. The default backstop is 256 MB and 50 million allocations, with no time limit. + +```python +DynamicWorkflow(agents=[...], resource_limits={'max_duration_secs': 30}) +``` + +`max_duration_secs` measures the time your script spends running sandbox code, not wall-clock time. While the script waits on a sub-agent it is suspended and that time does not count, so the cap will not fire on a normal workflow no matter how long the sub-agents take. Its one job is catching a pure-CPU runaway -- a `while True:` loop that never awaits, which none of the sub-agent budgets can stop because it never calls a sub-agent. Pass `'unlimited'` to remove every limit, or a partial dict that merges onto the backstop so you override only the caps you name. + +### Workflows do not nest + +A sub-agent cannot start its own workflow; a nested `run_workflow` call returns a terminal error instead of running. The practical rule: do not give the sub-agents in your catalog the `DynamicWorkflow` capability. They are the leaves of the orchestration, not orchestrators. + +## Renaming a sub-agent: `WorkflowAgent` + +By default a sub-agent shows up under its own `name` and `description`. To give it a different name or description for one workflow without editing the agent itself, wrap it in a `WorkflowAgent`: + +```python +from pydantic_ai_harness.dynamic_workflow import WorkflowAgent + +DynamicWorkflow( + agents=[ + WorkflowAgent( + reviewer, + name='check', + description='Checks one code change and returns actionable review findings.', + ), + ], +) +``` + +Now the model calls `check(task=...)`. Passing a bare agent is shorthand for wrapping it in a `WorkflowAgent` with no overrides. + +## Adding sub-agents mid-run: `reveal()` + +The catalog is fixed when a run starts, which keeps it in the prompt-cache prefix across turns. To make a new sub-agent available during a run (say once a fixer agent has been provisioned), keep a reference to the `DynamicWorkflow` instance and call `reveal()`: + +```python +workflow = DynamicWorkflow(agents=[reviewer]) +orchestrator = Agent('openai:gpt-5', deps_type=MyDeps, capabilities=[workflow]) + +# later, from the host or from another tool: +workflow.reveal(fixer) +``` + +The revealed sub-agent becomes callable on the next step; the model learns about it through a short announcement message that carries the new function's signature. The `run_workflow` description itself stays frozen at the agents present when the run started, so a runtime reveal never moves the prompt-cache prefix. `reveal()` is append-only and validates immediately -- a missing name, an invalid identifier, a reserved keyword, or a name collision raises `UserError` at the call site. + +## Loading it only when needed: `defer_loading` + +`DynamicWorkflow` carries a fair amount of instruction text, and most turns do not need it. Keep it collapsed to a one-line entry until the model actually loads it: + +```python +DynamicWorkflow( + agents=[reviewer, summarizer], + id='workflow', + defer_loading=True, +) +``` + +`defer_loading=True` needs a stable `id`. See [on-demand capabilities](/docs/ai/core-concepts/capabilities/#on-demand-capabilities) for the full picture. + +## What runs in the sandbox + +The script runs in Monty, a subset of Python. Knowing the edges matters: + +- No class definitions, and no third-party libraries. +- Useful standard-library modules: `asyncio`, `math`, `json`, `re`, `typing`. Import what you use; other modules are unavailable or stubbed. +- No wall-clock or timing primitives -- no `asyncio.sleep`, no `datetime.now()`, no `time`. +- `asyncio.gather(...)` runs sub-agents concurrently but does not support `return_exceptions=True`. + +Before a script runs it is statically type-checked against the sub-agent signatures. A misspelled function, a positional `task`, or a wrong-typed argument costs one retry, but no sub-agent budget and no sandbox execution. + +Errors abort the whole script + +A sub-agent that raises cannot be caught inside the script -- one failure aborts the whole script and the model retries it. Write scripts where sub-agents do not depend on catching each other's errors. If a script fails after some sub-agents already finished, the retry prompt lists those completed results, so the model can reuse them as plain values instead of paying for the same calls again. + +## Observability + +The [Logfire](https://pydantic.dev/logfire) trace is the best way to see what a workflow did. Each sub-agent run appears nested under the `run_workflow` span, and the span carries the exact `code` argument the model wrote, so you can read the script it actually ran. Until first-class progress streaming ships, set `event_stream_handler` on each sub-agent `Agent` to watch sub-agent runs inside the one tool call. + +## API + +```python +DynamicWorkflow( # all parameters are keyword-only + agents=[...], # Sequence[AbstractAgent | WorkflowAgent], required + tool_name='run_workflow', + max_agent_calls=50, + max_retries=3, + forward_usage=True, + inherit_model=False, # True -> sub-agents run with the parent run's resolved model + sub_agent_usage_limits=None, # UsageLimits per sub-agent run; None -> pydantic-ai default + resource_limits=None, # None -> backstop (256 MB, 50M allocs, no time cap); + # 'unlimited' -> off; a dict is merged onto the backstop + id=None, # required when defer_loading=True + description=None, # one-line catalog entry shown while deferred + defer_loading=False, +) + +workflow.reveal(agent) # AbstractAgent | WorkflowAgent; validates before appending + +WorkflowAgent( + agent, # Agent, required, positional + name=None, # sandbox function name; falls back to agent.name + description=None, # function docstring; falls back to agent.description +) +``` + +`DynamicWorkflowToolset` and `WorkflowResourceLimits` are also exported from the module for advanced use. + +Source: [`pydantic_ai_harness/dynamic_workflow/`](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/dynamic_workflow/). + +## Further reading + +- [Code Mode](/docs/ai/harness/code-mode) -- the same sandbox, calling the agent's own tools instead of sub-agents. +- [Subagents](/docs/ai/harness/subagents) -- one-delegation-per-tool-call sub-agents, without the scripted choreography. +- [Rewriting Bun in Rust](https://bun.com/blog/bun-in-rust) (Bun) -- the same pattern at scale, via Claude Code's dynamic workflows. +- [Capabilities](/docs/ai/core-concepts/capabilities/) and [on-demand capabilities](/docs/ai/core-concepts/capabilities/#on-demand-capabilities). + +--- + +# [FileSystem](https://pydantic.dev/docs/ai/harness/filesystem/) + +> Give a Pydantic AI agent sandboxed, glob-filtered file access scoped to a single directory tree, with symlink-safe containment checks. + +# FileSystem + +`FileSystem` gives an agent a fixed set of file tools -- read, write, edit, list, search, find, create, and inspect -- all scoped to a single `root_dir`. Every path is resolved and containment-checked (symlinks included) before any I/O, and access is filtered through allow / deny / protected glob patterns. + +[Source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/filesystem/) + +## The problem + +Letting an agent touch the filesystem directly is risky: path traversal (`../../etc/passwd`), symlinks that escape the project, clobbering `.git`, or leaking `.env` secrets. Hand-rolling the guards around every tool call is repetitive and easy to get subtly wrong. + +`FileSystem` centralizes those guards. It exposes one bounded, sandboxed toolset so you configure the boundary once and reuse it across agents. + +## Usage + +Add `FileSystem` to your agent's `capabilities` with a `root_dir`. Everything the agent reads or writes is confined to that directory. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness import FileSystem + +agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[FileSystem(root_dir='./workspace')], +) + +result = agent.run_sync('Read config.toml and tell me the package name.') +print(result.output) +``` + +`root_dir` defaults to the current directory (`.`), but passing an explicit workspace path is the recommended practice -- the sandbox is only as tight as the root you give it. + +## Tools + +`FileSystem` contributes eight tools, all path-scoped to `root_dir`: + +Tool + +Purpose + +`read_file` + +Read a text file with line numbers and a content hash. Binary files are detected and not dumped. Supports `offset`/`limit` paging. + +`write_file` + +Create or overwrite a file. Optional `expected_hash` rejects stale writes (optimistic concurrency). + +`edit_file` + +Exact-string replacement; `old_text` must match exactly once. Optional `expected_hash`. + +`list_directory` + +List a directory's entries with type indicators and sizes. + +`search_files` + +Regex search over file contents, optionally narrowed by an `include_glob`. + +`find_files` + +Glob search over file names (e.g. `*.py`, `**/*.json`). + +`create_directory` + +Create a directory and any missing parents. + +`file_info` + +Metadata for a file or directory (size, type, line count, hash, symlink target). + +Tool errors the model can correct -- a missing file, a denied path, a stale edit -- are surfaced as [`ModelRetry`](/docs/ai/core-concepts/agent/#reflection-and-self-correction), so the agent gets the error message back and can adjust rather than aborting the run. + +## Security model + +- **Containment.** Paths resolve relative to `root_dir`; anything resolving outside -- via `..`, an absolute path, or a symlink -- is rejected. Symlinks are resolved with `os.path.realpath` _before_ the containment check, closing the TOCTTOU window. +- **Binary detection.** `read_file` returns a placeholder instead of dumping binary bytes into the model context. +- **Optimistic concurrency.** `write_file`/`edit_file` accept an `expected_hash` so an agent operating on a stale read is told to re-read rather than silently overwriting newer content. + +## Pattern filtering + +Three independent glob lists control access. Patterns are matched with `fnmatch`, whose `*` spans `/`, so `*.py` matches `src/main.py` and you rarely need `**`. + +Field + +Effect + +`allowed_patterns` + +If non-empty, only matching paths are accessible (allowlist). + +`denied_patterns` + +Matching paths are always rejected (denylist). + +`protected_patterns` + +Matching paths are read-only -- reads succeed, writes are rejected. + +`protected_patterns` defaults to `.git/*`, `.env`, `.env.*`, `*.pem`, `*.key`, and `**/secrets*`. Pass an empty list to disable protection. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness import FileSystem + +agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[ + FileSystem( + root_dir='./workspace', + allowed_patterns=['*.py', '*.toml'], + denied_patterns=['**/node_modules/*'], + ), + ], +) +``` + +### Direct access vs. walkers + +The three rules apply at two different granularities: + +- **Direct access** (`read_file`, `write_file`, `edit_file`, `file_info`, `create_directory`) gates the operation's target path. You must name a path that the patterns permit. +- **Walkers** (`list_directory`, `search_files`, `find_files`) gate their root by deny/protected patterns, but **not** by `allowed_patterns` -- a directory root like `.` never matches a file pattern such as `src/*.py`, so requiring it to would make every listing fail. Instead, the root is always walked and each **entry** is filtered against all three lists. A directory listing can never surface a path the agent couldn't otherwise read or write. + +So with `allowed_patterns=['*.py']`, `list_directory('.')` succeeds and shows only the `.py` entries; `read_file('notes.md')` is rejected. + +Note that the walkers filter entries with write-level access, so `protected_patterns` matches are omitted from `list_directory`, `search_files`, and `find_files` output even though those exact paths remain directly readable via `read_file`/`file_info`. + +Note + +Dotfiles and dot-directories (`.git`, `.env`, `.github`, ...) are skipped by all three walkers -- `list_directory`, `search_files`, and `find_files` -- regardless of patterns. + +## Configuration + +```python +from pydantic_ai_harness import FileSystem + +FileSystem( + root_dir='.', # str | Path -- sandbox root + allowed_patterns=[], # allowlist globs (empty = allow all) + denied_patterns=[], # denylist globs + protected_patterns=[...], # read-only globs (defaults to secrets/.git) + max_read_lines=2000, # cap for a single read_file + max_search_results=1000, # cap for search_files + max_find_results=1000, # cap for find_files +) +``` + +The three integer limits must be positive; they are validated at construction and raise `ValueError` otherwise. + +## Agent spec (YAML/JSON) + +`FileSystem` works with Pydantic AI's [agent spec](/docs/ai/core-concepts/agent-spec/): + +```yaml +model: anthropic:claude-sonnet-4-6 +capabilities: + - FileSystem: + root_dir: ./workspace + allowed_patterns: ['*.py', '*.toml'] +``` + +```python +from pydantic_ai import Agent +from pydantic_ai_harness import FileSystem + +agent = Agent.from_file('agent.yaml', custom_capability_types=[FileSystem]) +``` + +Pass `custom_capability_types` so the spec loader knows how to instantiate `FileSystem`. + +## Further reading + +- [Pydantic AI capabilities](/docs/ai/core-concepts/capabilities/) +- [Toolsets](/docs/ai/tools-toolsets/toolsets/) +- [the capabilities overview](/docs/ai/harness/) + +## API reference + +### FileSystem + +**Bases:** `AbstractCapability[AgentDepsT]` + +File system access scoped to a root directory. + +All paths are resolved relative to `root_dir`. Traversal above the root is rejected. Symlinks are resolved before authorization. + +#### Attributes + +##### root\_dir + +Root directory for all file operations. Defaults to the current directory. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | `Path` **Default:** `'.'` + +##### allowed\_patterns + +If non-empty, only paths matching at least one glob pattern are accessible. + +**Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `field(default_factory=(list[str]))` + +##### denied\_patterns + +Paths matching any of these glob patterns are rejected. + +**Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `field(default_factory=(list[str]))` + +##### protected\_patterns + +Paths matching these patterns are read-only (writes are rejected). + +Defaults to protecting `.git/`, `.env`, key files, and secrets. Set to an empty list to disable protection. + +**Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `field(default_factory=(lambda: list(_DEFAULT_PROTECTED)))` + +##### max\_read\_lines + +Maximum number of lines returned by a single `read_file` call. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `2000` + +##### max\_search\_results + +Maximum number of matches returned by `search_files`. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `1000` + +##### max\_find\_results + +Maximum number of matches returned by `find_files`. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `1000` + +#### Methods + +##### get\_toolset + +```python +def get_toolset() -> FileSystemToolset[AgentDepsT] +``` + +Build and return the filesystem toolset. + +###### Returns + +`FileSystemToolset`\[`AgentDepsT`\] + +--- + +# [Guardrails](https://pydantic.dev/docs/ai/harness/guardrails/) + +> Validate the user prompt before it reaches the model and the model output before it reaches the caller, with allow/block/replace/retry verdicts and optional parallel execution. + +# Guardrails + +Guardrails put a validation layer on the two edges of an agent run: the prompt on its way _in_ to the model, and the output on its way _out_ to the caller. Reach for them when unstructured input or output must be screened before it is acted on -- a prompt-injection attempt you never want to send, PII you must redact, an off-topic request you want to refuse cheaply, or an answer that must cite its sources before you show it. Without a guardrail the framework sends whatever the user typed and returns whatever the model produced, verbatim; a guardrail interposes a callable you control that gets the final say. + +> The API may change between releases. Where practical, breaking changes ship with a deprecation warning. + +## The problem + +Agents take unstructured input from users and return unstructured output to callers. On its own the framework does not reason about "this is unsafe to send" or "this is unsafe to show" -- a prompt-injection attempt reaches the model as-is, and any output the model produces is returned untouched. You need a place to inspect the value and decide what happens next. + +## The solution + +Two capabilities -- `InputGuard` and `OutputGuard` -- each wrap a `guard` callable you supply. The guard inspects a value (the prompt, or the output) and returns one of four outcomes: + +Outcome + +`InputGuard` + +`OutputGuard` + +**allow** + +send the prompt to the model + +return the output to the caller + +**block** + +skip the model call; a refusal message becomes the response + +raise `OutputBlocked` + +**replace** + +rewrite the prompt sent to the model (redaction) + +substitute a sanitized output + +**retry** + +-- (not valid for input) + +send the output back to the model to try again + +The asymmetry between input `block` and output `block` is deliberate. Blocking the input spends no tokens, so a graceful refusal is almost always right. Blocking the output means the model already produced something you do not want exposed, so raising forces the caller to decide what to do next. + +Both `InputGuard`, `OutputGuard`, and their supporting types are top-level exports: + +```python +from pydantic_ai import Agent +from pydantic_ai_harness import GuardResult, InputGuard, OutputGuard + + +def no_secrets(prompt: str) -> bool: + return 'api_key' not in prompt.lower() + + +def no_pii(output: object) -> GuardResult: + if 'SSN' in str(output): + return GuardResult.block('The response contained personal data.') + return GuardResult.allow() + + +agent = Agent( + 'openai:gpt-5.4', + capabilities=[ + InputGuard(guard=no_secrets), + OutputGuard(guard=no_pii), + ], +) +``` + +A guard returns a bare `bool` (`True` = allow, `False` = block) for the simple case, or a `GuardResult` for the richer outcomes. Guards may also be async -- return an awaitable `bool`/`GuardResult`, for example to call a moderation API. + +`OutputGuard` receives the output unchanged -- no automatic stringification. For a string output the guard reads it directly; for a typed (Pydantic model) output the guard gets the model instance, so pick the serialization that fits the check (read a field, or call `output.model_dump_json()` for JSON text). This avoids the trap of `str(MyModel(...))` producing a `MyModel(field=...)` repr that hides field contents from regex-based checks. + +## `GuardResult` + +Construct a `GuardResult` with its classmethods, not the raw fields: + +```python +from pydantic_ai_harness import GuardResult + +GuardResult.allow() # let the value through +GuardResult.block('reason') # refuse; `reason` is optional (a default is used otherwise) +GuardResult.replace(cleaned_value) # substitute a sanitized value and continue +GuardResult.retry('instruction') # OutputGuard only: ask the model to redo the output +``` + +The block/retry message is produced at the moment the guard decides, so it can carry the guard's own reasoning rather than a string frozen at construction time. + +## Redaction (`replace`) + +Return `GuardResult.replace(value)` to sanitize rather than refuse. `InputGuard` rewrites the prompt sent to the model; `OutputGuard` substitutes the output returned to the caller. + +```python +def scrub_emails(text: str) -> GuardResult: + cleaned = EMAIL_RE.sub('[email]', text) + return GuardResult.replace(cleaned) if cleaned != text else GuardResult.allow() + + +agent = Agent( + 'openai:gpt-5.4', + capabilities=[ + InputGuard(guard=scrub_emails), # strip PII before it reaches the model + OutputGuard(guard=scrub_emails), # strip PII before it reaches the caller + ], +) +``` + +Input redaction requires sequential mode -- it is incompatible with `parallel=True`, since a parallel guard runs alongside a model call that has already started with the original prompt. + +## Retry (`retry`) + +`OutputGuard` can send a bad output back to the model instead of blocking it. Return `GuardResult.retry(instruction)` -- the instruction is the retry prompt the model sees. This reuses pydantic-ai's normal retry machinery and counts against the run's output-retry budget. + +```python +def must_cite_sources(output: object) -> GuardResult: + if not has_citations(output): + return GuardResult.retry('Include at least one source citation.') + return GuardResult.allow() + + +OutputGuard(guard=must_cite_sources) +``` + +## Accessing run context + +A guard may take a `RunContext` as its first parameter when it needs run state -- `deps` for tenant- or role-aware policy, message history for conversation-aware checks. The parameter is detected from the signature, so prompt-only guards need not declare it: + +```python +from pydantic_ai import RunContext +from pydantic_ai_harness import InputGuard + + +def tenant_policy(ctx: RunContext[MyDeps], prompt: str) -> bool: + return ctx.deps.tier == 'pro' or 'advanced-feature' not in prompt + + +InputGuard(guard=tenant_policy) +``` + +## Parallel input guards + +A slow guard (an LLM classifier, a network call) run sequentially adds its latency to every turn. Set `parallel=True` to run the guard concurrently with the model call instead, overlapping the two so the guard adds no latency on the pass path. The model call is cancelled the moment the guard reports a violation. + +```python +InputGuard(guard=slow_async_classifier, parallel=True) +``` + +Parallel mode trades tokens for latency: sequential mode never calls the model when the guard blocks, but parallel mode has already started the model call -- if the guard trips only after the model has responded, those tokens were spent. For fast local checks (regex, keyword lookup) sequential is the better default. `replace` is not available under `parallel=True`. + +## Hard-fail path + +`block` is the graceful path. To make the caller see an exception instead, raise from the guard: + +```python +from pydantic_ai_harness import InputBlocked + + +def strict_guard(prompt: str) -> bool: + if contains_credentials(prompt): + raise InputBlocked('credentials detected') + return True +``` + +Any exception raised by the guard propagates as-is -- use `InputBlocked` / `OutputBlocked` from this module, or your own exception types. + +## Streaming + +`OutputGuard` inspects the **final** output only -- during `run_stream()` partial chunks reach the caller before the guard runs, so a `block` or `replace` verdict cannot un-send content already streamed. Use `run()` / `run_sync()` when the output must be screened before any of it is exposed. `GuardResult.retry()` is **not** supported under `run_stream()` and surfaces there as `UnexpectedModelBehavior`. `InputGuard` (including `parallel=True`) works the same in streamed and non-streamed runs. + +## Tracing + +`replace` and `block` are recorded as spans on the active OpenTelemetry tracer, so a redaction or refusal shows up in [Logfire](https://pydantic.dev/logfire) traces (`guardrail redacted input`, `guardrail blocked output`, and so on) with `guardrail.*` attributes. Content attributes -- the original/replacement values for a redaction and the refusal `message` for a block -- are attached **only** when `RunContext.trace_include_content` is enabled, since these can quote the very content the guard exists to keep out of traces. + +`OutputGuard` positions its block/redact spans so they are always captured by an enclosing `Instrumentation` span regardless of capability order, while `InputGuard` runs innermost so any capability that morphs messages (a prompt rewriter, a context manager) runs first and the guard sees the final prompt the model will receive. + +## Relationship to `pydantic-ai-shields` + +[`pydantic-ai-shields`](https://github.com/vstorm-co/pydantic-ai-shields) provides opinionated implementations on top of these primitives (prompt-injection detectors, PII scrubbers, keyword blocklists). Use the guardrails here when you want to plug in your own validation logic; reach for shields when you need a batteries-included detector. + +## API + +```python +InputGuard( + guard, # Callable[..., bool | GuardResult | Awaitable[bool | GuardResult]] + parallel=False, # run concurrently with the model call +) + +OutputGuard( + guard, # Callable[..., bool | GuardResult | Awaitable[bool | GuardResult]] +) +``` + +The guard callable takes the inspected value -- the prompt for `InputGuard`, the output for `OutputGuard` -- optionally preceded by a `RunContext`. `InputGuardFunc` and `OutputGuardFunc` are the exported signature aliases; `GuardrailError` is the base for `InputBlocked` and `OutputBlocked`. + +Source: [`pydantic_ai_harness/guardrails/`](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/guardrails/). + +--- + +# [Overview](https://pydantic.dev/docs/ai/harness/) + +> The official capability library for Pydantic AI -- pick-and-choose batteries that turn your agent into a coding agent, research assistant, or anything else. + +# Overview + +**The batteries for your [Pydantic AI](/docs/ai/) agent.** + +Pydantic AI's [capabilities](/docs/ai/core-concepts/capabilities/) and [hooks](/docs/ai/core-concepts/hooks/) API is how you give an agent its harness -- bundles of tools, lifecycle hooks, instructions, and model settings that extend what the agent can do without any framework changes. + +**Pydantic AI Harness** is the official capability library for Pydantic AI, maintained by the [Pydantic AI](https://github.com/pydantic/pydantic-ai) team. Pydantic AI core ships the capabilities that require model or framework support, plus the ones fundamental to every agent -- [web search](/docs/ai/core-concepts/capabilities/#provider-adaptive-tools), [tool search](/docs/ai/tools-toolsets/deferred-tools/), [thinking](/docs/ai/core-concepts/capabilities/#thinking). Everything else lives here: standalone building blocks you pick and choose to turn your agent into a coding agent, a research assistant, or anything else. This is also where new capabilities start -- as they stabilize and prove themselves broadly essential, they can graduate into core. + +## What goes where? + +Pydantic AI core ships the agent loop, model providers, the capabilities/hooks abstraction, and two kinds of capabilities: + +- **Capabilities that require model or framework support** -- anything backed by provider native tools (like [image generation](/docs/ai/core-concepts/capabilities/#provider-adaptive-tools)), provider-specific APIs (like compaction via the OpenAI or Anthropic APIs), or deep agent graph integration. These go hand-in-hand with model class code and need to ship together. +- **Capabilities that are fundamental to the agent experience** -- things nearly every agent benefits from, like [web search](/docs/ai/core-concepts/capabilities/#provider-adaptive-tools), [tool search](/docs/ai/tools-toolsets/deferred-tools/), and [thinking](/docs/ai/core-concepts/capabilities/#thinking). These feel like qualities of the agent itself, not accessories. + +**Pydantic AI Harness** is where everything else lives: standalone capabilities that make specific categories of agents powerful, or that are still finding their final shape. Context management, memory, guardrails, file system access, code execution, multi-agent orchestration -- these are the building blocks you pick and choose based on what your agent needs to do. + +The harness is also where new capabilities _start_. It ships as a separate package so capabilities can iterate faster without the strict backward-compatibility requirements of core. As a capability stabilizes and proves itself broadly essential, it can graduate into core -- [code mode](/docs/ai/harness/code-mode) is an early candidate. + +Many capabilities benefit from a "fall up" pattern: they typically start as a local implementation that works with every model, then gain provider-native support that uses the provider's built-in API when available -- auto-switching between the two. This is how [web search](/docs/ai/core-concepts/capabilities/#provider-adaptive-tools), [web fetch](/docs/ai/core-concepts/capabilities/#provider-adaptive-tools), and [image generation](/docs/ai/core-concepts/capabilities/#provider-adaptive-tools) already work in core, and the same approach is coming for skills, code mode, and context compaction. + +## Installation + +Terminal + +```bash +uv add pydantic-ai-harness +``` + +Some capabilities need an extra to pull in their optional dependencies: + +Terminal + +```bash +uv add "pydantic-ai-harness[codemode]" # Code Mode (adds the Monty sandbox) +uv add "pydantic-ai-harness[dynamic-workflow]" # Dynamic Workflow (adds the Monty sandbox) +uv add "pydantic-ai-harness[logfire]" # Managed Prompt (Logfire-managed prompts) +uv add "pydantic-ai-harness[acp]" # ACP (Agent Client Protocol SDK) +``` + +The `code-mode` extra is also supported as an alias for `codemode`. + +Requires Python 3.10+ and `pydantic-ai-slim>=2.1.0`. + +## Quick start + +Install the harness alongside the Pydantic AI extras this example uses: + +Terminal + +```bash +uv add "pydantic-ai-slim[anthropic,mcp,duckduckgo,logfire]" "pydantic-ai-harness[code-mode]" +``` + +```python +import logfire +from pydantic_ai import Agent +from pydantic_ai.capabilities import MCP, WebSearch +from pydantic_ai_harness import CodeMode + +# See https://pydantic.dev/docs/ai/integrations/logfire/ for setup details. +logfire.configure() +logfire.instrument_pydantic_ai() + +agent = Agent( + 'anthropic:claude-opus-4-7', + capabilities=[ + # Wraps every tool into a single run_code tool, sandboxed by Monty + # (https://github.com/pydantic/monty -- pulled in by the [code-mode] extra). + # The model writes Python that calls multiple tools with loops, conditionals, + # asyncio.gather, and local filtering -- one model round-trip for N tool calls. + CodeMode(), + # Connect to any MCP server -- here, the open-source Hacker News server + # (https://github.com/cyanheads/hn-mcp-server). native=False forces the + # local MCP toolset so CodeMode can wrap the tools; without it, + # providers that natively support MCP server connectors execute the tools + # server-side and bypass the sandbox. + MCP('https://hn.caseyjhand.com/mcp', native=False), + # Provider-adaptive web search; native=False routes through the local + # DuckDuckGo fallback (the [duckduckgo] extra above) so CodeMode can batch + # web searches alongside the HN calls in a single run_code. + WebSearch(native=False), + ], +) + +result = agent.run_sync( + "Across the top, best, and 'show HN' Hacker News feeds, find the most-discussed " + "story with at least 100 points. Pull its comment thread, its submitter's profile, " + "and any web coverage. Summarize what you find in one paragraph." +) +print(result.output) +""" +The most-discussed HN story across top/best/show clearing 100 points is "Vibe coding +and agentic engineering are getting closer than I'd like" by Simon Willison (748 points, +853 comments, on the Best feed), submitted by long-time HNer e12e. The piece argues +that the two modes Willison once kept mentally separate -- throwaway "vibe coding" and +disciplined "agentic engineering" -- are blurring, since agents like Claude Code now +reliably handle non-trivial tasks like "build a JSON API endpoint that runs a SQL query" +with tests and docs on the first pass. The HN thread is unusually substantive, with +commenters debating whether LLMs created or merely *exposed* sloppy engineering +practices and warning of a "normalization of deviance" as engineers stop reviewing diffs. +""" +``` + +[![Logfire trace from the Quick start run](/docs/ai/harness/img/quick-start-trace.png)](https://logfire-us.pydantic.dev/public-trace/84bcf123-2106-49da-9f6f-5c26395339bb?spanId=7650806a0785b946) + +**[See this run as a public Logfire trace ->](https://logfire-us.pydantic.dev/public-trace/84bcf123-2106-49da-9f6f-5c26395339bb?spanId=7650806a0785b946)** Each `run_code` span fans out into the tool calls the model issued from inside the sandbox -- it's the easiest way to understand what code mode actually did. + +## Capabilities + +Each capability is a self-contained battery you drop into an agent's `capabilities=[...]` list. They compose with each other and with Pydantic AI's [built-in capabilities](/docs/ai/core-concepts/capabilities/). + +Capability + +What it does + +Extra + +[Code Mode](/docs/ai/harness/code-mode) + +Wraps the agent's tools into a single `run_code` tool, sandboxed by [Monty](https://github.com/pydantic/monty). The model writes Python that calls the tools as functions -- with loops, conditionals, `asyncio.gather`, and local filtering -- collapsing N tool calls into one model round-trip. + +`codemode` + +[FileSystem](/docs/ai/harness/filesystem) + +Sandboxed file access scoped to a root directory: read, write, edit, search, and find files. Rejects path traversal above the root, resolves symlinks before authorizing, and keeps `.git/`, `.env`, key files, and secrets read-only by default. + +-- + +[Shell](/docs/ai/harness/shell) + +Command execution in a subprocess rooted at a working directory, gated by allowlists, denylists, timeouts, and optional environment-variable stripping (including a preset for common LLM provider credentials). + +-- + +[Context](/docs/ai/harness/context) + +Auto-loads repo context -- `CLAUDE.md`/`AGENTS.md` and repository structure -- so the agent starts a run already oriented in the project. + +-- + +[Pydantic AI Docs](/docs/ai/harness/pydantic-ai-docs) + +An on-demand `read_pyai_docs` tool that pulls Pydantic AI documentation into the run when the agent needs it, instead of preloading it. + +-- + +[Compaction](/docs/ai/harness/compaction) + +Keeps a run within token limits: sliding-window trimming, LLM-powered summarization of older messages, and warnings before the context or iteration ceiling is hit. + +-- + +[Overflowing Tool Output](/docs/ai/harness/overflowing-tool-output) + +Reduces an oversized tool return when it is produced -- truncate, spill to a queryable file, or summarize -- so a large payload does not persist in history and get re-sent every request. + +-- + +[Step Persistence](/docs/ai/harness/step-persistence) + +Saves and restores full conversation state; snapshot, resume (`continue_run`), and fork (`fork_run`) a run. + +-- + +[Media](/docs/ai/harness/media) + +Offloads large `BinaryContent` to content-addressed stores (local or S3) so big media does not bloat message history. + +-- + +[Subagents](/docs/ai/harness/subagents) + +Delegates subtasks to specialized child agents through a delegate tool. + +-- + +[Dynamic Workflow](/docs/ai/harness/dynamic-workflow) + +Orchestrates sub-agents from a model-written Python script -- fan-out, chaining, and voting in a single tool call. + +`dynamic-workflow` + +[Planning](/docs/ai/harness/planning) + +Breaks a complex task into a structured plan before execution and tracks progress against it. + +-- + +[Memory](/docs/ai/harness/memory) + +Gives an agent a persistent, namespaced notebook with bounded prompt injection, on-demand search, and concurrency-safe stores. + +-- + +[Runtime Authoring](/docs/ai/harness/runtime-authoring) + +Lets an agent author, validate, and load real capabilities at runtime. + +-- + +[Guardrails](/docs/ai/harness/guardrails) + +Validates user input before a run starts and model output after it completes -- block or redact, with structured results. + +-- + +[Managed Prompt](/docs/ai/harness/managed-prompt) + +Backs an agent's instructions with a [Logfire-managed prompt](https://logfire.pydantic.dev/docs/reference/advanced/prompt-management/), so you can version, label, and roll out prompt changes from the Logfire UI without redeploying -- with a code default that keeps the agent working when no remote value is available. + +`logfire` + +[ACP](/docs/ai/harness/acp) _(experimental)_ + +Serves an agent to editors (Zed, etc.) over the [Agent Client Protocol](https://agentclientprotocol.com) -- streamed text, diff-rendered edits, and tool approval. + +`acp` + +Most capabilities are stable within the [version policy](#version-policy) below. [ACP](/docs/ai/harness/acp) is the exception -- it is still experimental, imported from `pydantic_ai_harness.experimental.acp`, and may change or be removed in a future release. + +## Build your own + +[Capabilities](/docs/ai/core-concepts/capabilities/#building-custom-capabilities) are the primary extension point for Pydantic AI. Any of the capabilities in this library can serve as a reference for building your own. + +Publishing as a standalone package? Use the `pydantic-ai-` naming convention -- see [Publishing capability packages](/docs/ai/guides/extensibility/#publishing-capability-packages). + +## Version policy + +Pydantic AI Harness uses **0.x versioning** to signal that APIs are still stabilizing. During 0.x, minor releases (0.1 -> 0.2) may include breaking changes -- renamed parameters, changed defaults, restructured APIs -- while patch releases (0.1.0 -> 0.1.1) will not intentionally break existing behavior. All breaking changes are documented in release notes with migration guidance. This is why the harness is a separate package from [Pydantic AI](https://github.com/pydantic/pydantic-ai), which has a [stricter version policy](/docs/ai/project/version-policy/). As the core capabilities stabilize, the library will move toward 1.0 with matching stability guarantees. + +## Pydantic AI references + +- [Capabilities](/docs/ai/core-concepts/capabilities/) -- what capabilities are, built-in capabilities, building your own +- [Hooks](/docs/ai/core-concepts/hooks/) -- lifecycle hooks reference, ordering, error handling +- [Extensibility](/docs/ai/guides/extensibility/) -- publishing packages, third-party ecosystem +- [Toolsets](/docs/ai/tools-toolsets/toolsets/) -- building tools for capabilities +- [API reference](/docs/ai/api/pydantic-ai/capabilities/) -- full API docs + +--- + +# [Managed Prompt](https://pydantic.dev/docs/ai/harness/managed-prompt/) + +> Back a Pydantic AI agent's instructions with a Logfire-managed prompt so you can version, label, and roll it out without redeploying. + +# Managed Prompt + +`ManagedPrompt` backs an agent's instructions with a [Logfire-managed prompt](https://logfire.pydantic.dev/docs/reference/advanced/prompt-management/), so you can iterate on your system prompt from the Logfire UI -- versioned, labelled, and rolled out -- without touching code or redeploying. It's a Pydantic AI [capability](/docs/ai/harness/), so you wire it in through the `capabilities=` parameter on `Agent`. + +[Source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/logfire/) + +Install the `logfire` extra: + +Terminal + +```bash +uv add "pydantic-ai-harness[logfire]" +``` + +A first-party `Managed` capability is in flight + +A broader, first-party `Managed` capability is being built in [pydantic-ai#5107](https://github.com/pydantic/pydantic-ai/pull/5107) and will eventually be importable as `pydantic_ai.managed.logfire.Managed` -- covering instructions, model settings, and whole-spec variables. Until then, `ManagedPrompt` is the supported path for backing an agent's instructions with a Logfire-managed prompt. + +## The problem it solves + +Prompts are critical to agent behavior, but iterating on them through the normal edit -> review -> deploy loop is slow. You can't easily A/B test a change, and you can't roll it back the moment it misbehaves in production without shipping a new build. + +`ManagedPrompt` moves the prompt out of your codebase and into Logfire's managed-variable store. It declares the backing managed variable for you and resolves it **once per run**, feeding the resolved value into the agent's instructions. Resolution happens inside the run's [`wrap_run`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.AbstractCapability.wrap_run) hook, using the [`ResolvedVariable`](https://logfire.pydantic.dev/docs/reference/advanced/managed-variables/) as a context manager that stays open for the whole run -- so the selected label and version are attached as baggage to every child span of the agent run. You get a direct correlation between a run's behavior and the exact prompt version that produced it, plus instant iteration and rollback from the Logfire UI. + +## Usage + +Pass the prompt name and a default value. The name `support_agent` is declared as the managed variable `prompt__support_agent` -- the naming Logfire's Prompt management uses (hyphens in a name become underscores). The `default` keeps the agent working until a remote value is published, so your code always runs even before you create the prompt in Logfire. + +```python +import logfire +from pydantic_ai import Agent + +from pydantic_ai_harness.logfire import ManagedPrompt + +logfire.configure() + +agent = Agent( + 'openai:gpt-5', + capabilities=[ + ManagedPrompt( + 'support_agent', + default='You are a helpful customer support agent. Be friendly and concise.', + label='production', + ) + ], +) + +result = agent.run_sync('My order never arrived.') +print(result.output) +``` + +Pinning `label='production'` is the recommended default: the resolved value only changes on a deliberate prompt rollout, which keeps the provider prompt cache hot (see [Prompt-cache trade-off](#prompt-cache-trade-off) below). + +## Targeting + +For deterministic A/B assignment (the same user always sees the same label), pass a `targeting_key`. It can be a static string or a callable that derives the key from the [`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext) -- handy when the key lives in your agent's `deps`: + +```python +from dataclasses import dataclass + +from pydantic_ai import Agent + +from pydantic_ai_harness.logfire import ManagedPrompt + + +@dataclass +class Deps: + user_id: str + + +agent = Agent( + 'openai:gpt-5', + deps_type=Deps, + capabilities=[ + ManagedPrompt( + 'support_agent', + default='You are a helpful customer support agent.', + targeting_key=lambda ctx: ctx.deps.user_id, + ), + ], +) +``` + +Pass `attributes` (a mapping, or a callable returning one) for condition-based targeting rules. When `label` is omitted, the variable's rollout and targeting rules pick the label. When both `targeting_key` and `attributes` are omitted, Logfire falls back to its own targeting context and then to the active trace id. + +For Logfire-side targeting that lives outside the agent (e.g. set once per request handler), use Logfire's [`targeting_context`](https://logfire.pydantic.dev/docs/reference/advanced/managed-variables/) in an outer scope; `ManagedPrompt` only needs `targeting_key` / `attributes` when the key comes from the agent's `RunContext`. + +## Templating with deps + +By default the resolved prompt is used verbatim. Pass `render_template=True` to render it as a Handlebars template against the agent's `deps` -- the same mechanism as [`TemplateStr`](/docs/ai/api/pydantic-ai/agent/) -- so `{{field}}` is filled from `deps`: + +```python +from dataclasses import dataclass + +from pydantic_ai import Agent + +from pydantic_ai_harness.logfire import ManagedPrompt + + +@dataclass +class Deps: + customer_name: str + + +agent = Agent( + 'openai:gpt-5', + deps_type=Deps, + capabilities=[ + ManagedPrompt( + 'support_agent', + default='You are helping {{customer_name}}. Be friendly and concise.', + render_template=True, + ), + ], +) +``` + +Rendering requires `pydantic-handlebars` (install `pydantic-ai-slim[spec]`). It is off by default. + +## Prompt-cache trade-off + +The resolved value lands in the agent's **system instructions**. Provider prompt caches (Anthropic, OpenAI, etc.) key strictly by prefix -- `tools -> system -> messages` -- so any change to the system block invalidates the cached prefix for the affected runs. + +Mode + +Cache impact + +Pinned `label='production'`, no rollout split + +**Cache-stable.** The value only changes on a deliberate prompt rollout, which is the same cost as a redeploy. + +Percentage rollout across labels (no `label=`) + +Different runs land on different labels -> splits the cache into one lane per label. + +`targeting_key` per user/tenant with multiple labels in play + +Cache lanes per assigned label; deterministic per key but still N lanes overall. + +Mid-traffic label flip in the Logfire UI + +One-shot cold-invalidation for everyone on that label. + +In short: pinning a `label` keeps the cache hot; using `ManagedPrompt` as an A/B platform is opt-in cache cost. If you don't need rollouts, `label='production'` is the recommended default. + +## Bringing your own variable + +Declaring the same name more than once is fine -- each `ManagedPrompt` builds its own backing variable, so sharing a prompt across several agents just works. Pass an existing [`logfire.variables.Variable`](https://logfire.pydantic.dev/docs/reference/advanced/managed-variables/) as the first argument instead of a name when you want to declare the variable yourself -- for example a template variable, or one registered for `variables_push`: + +```python +import logfire +from pydantic_ai import Agent + +from pydantic_ai_harness.logfire import ManagedPrompt + +logfire.configure() + +support_prompt = logfire.var( + name='prompt__support_agent', + type=str, + default='You are a helpful customer support agent. Be friendly and concise.', +) + +agent = Agent('openai:gpt-5', capabilities=[ManagedPrompt(support_prompt, label='production')]) +``` + +When `name` is a prompt name (not a `Variable`), pass `logfire_instance=` to declare the variable on a specific Logfire instance instead of the module-level default. `default` is required when `name` is a prompt name and is ignored when you pass a `Variable` (which already carries its own default and instance). + +## How it composes + +- **Resolves once per run.** A label flip or rollout change that lands in Logfire mid-run is not picked up until the next run starts -- the trade-off for run-stable instructions and a single baggage scope across all child spans. +- **Runs outermost.** The capability wraps [`Instrumentation`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.Instrumentation) so the resolved variable's baggage covers the agent run span as well as its children. On recent Logfire versions both the selected label and the version are propagated as separate baggage attributes. +- **Concurrency-safe.** Resolution is isolated per run via a context variable, so a single capability instance is safe to share across concurrent runs. +- **Inspectable mid-run.** `ManagedPrompt.resolved` exposes the active run's `ResolvedVariable` (`value`, `label`, `version`, `reason`) for inspection -- e.g. from inside a tool. It is `None` outside a run. + +## API reference + +The resolved prompt is a `str`. Pass the bare prompt name (the `prompt__` prefix and hyphen-to-underscore normalization are applied for you) and a `default`, then use `label`, `targeting_key`, `attributes`, `render_template`, and `logfire_instance` to control resolution. + +### ManagedPrompt + +**Bases:** `AbstractCapability[AgentDepsT]` + +Back an agent's instructions with a Logfire-managed prompt. + +**Prompt-cache trade-off:** the resolved value lands in the system instructions block, so any Logfire-side change to the prompt (new version rollout, label flip, A/B targeting) invalidates the provider's prompt cache for the affected runs. Pin a `label` (e.g. `'production'`) for the cache-stable path; treat percentage rollouts and per-user targeting as opt-in cache cost. See the README's "Prompt-cache trade-off" section for the full picture. + +Pass the managed prompt name and a default value and the capability declares the backing [managed variable](https://logfire.pydantic.dev/docs/reference/advanced/managed-variables/) for you -- a name of `support_agent` resolves the variable `prompt__support_agent`, matching the naming Logfire's [Prompt management](https://logfire.pydantic.dev/docs/reference/advanced/prompt-management/) uses. You can iterate on the prompt from the Logfire UI -- versioned, labelled, and rolled out -- without redeploying, while the code default keeps the agent working when no remote value is available. + +```python +import logfire +from pydantic_ai import Agent + +from pydantic_ai_harness.logfire import ManagedPrompt + +logfire.configure() + +agent = Agent( + 'openai:gpt-5', + capabilities=[ + ManagedPrompt( + 'support_agent', + default='You are a helpful customer support agent. Be friendly and concise.', + label='production', + ) + ], +) +result = agent.run_sync('My order never arrived.') +``` + +The prompt value is resolved **once per run**, inside the run's [`wrap_run`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.AbstractCapability.wrap_run) hook, using the `ResolvedVariable` as a context manager that stays open for the whole run -- so the selected label and version are attached as baggage to every child span of the agent run. + +Declaring the same name more than once is fine -- each `ManagedPrompt` constructs its own backing variable, so sharing a prompt across several agents just works. Pass an existing `logfire.variables.Variable` as `name` instead of a prompt name when you want to use a variable you defined yourself (for example a `template_var`, or one registered for [`variables_push`](https://logfire.pydantic.dev/docs/api/logfire/#logfire.Logfire.variables_push)). + +#### Attributes + +##### name + +The managed prompt name (declared as the variable `prompt__`), or a pre-built `logfire.Variable`. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | `Variable`\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] + +##### default + +Code-default prompt text. Required when `name` is a prompt name; ignored when `name` is a `Variable`. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### label + +Explicit targeting label on the Logfire managed prompt to resolve (e.g. `'production'`). When `None`, the targeting rules on the managed variable select the label. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### targeting\_key + +Stable key that seeds Logfire's deterministic rollout assignment -- the same key always lands in the same percentage bucket, so a given user keeps the same label across runs. Accepts a static value or a callable that derives it from the [`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext). When `None`, Logfire falls back to its own targeting context and then the active trace id. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[[`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext)\[`AgentDepsT`\]\], [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None)\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### attributes + +Attributes for condition-based targeting rules, or a callable that derives them from the [`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext). + +**Type:** [`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] | [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[[`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext)\[`AgentDepsT`\]\], [`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] | [`None`](https://docs.python.org/3/library/constants.html#None)\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### render\_template + +When `True`, render the resolved prompt as a Handlebars template against the agent's `deps` (the same mechanism as `TemplateStr`); `{{field}}` is filled from `deps`. Requires `pydantic-handlebars` (install `pydantic-ai-slim[spec]`). Defaults to `False`, so the resolved prompt is used verbatim. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `False` + +##### logfire\_instance + +Logfire instance to resolve the variable on. When `None`, the global default instance (the one backing the module-level `logfire.var`) is used. Ignored when `name` is a `Variable`. + +**Type:** `Logfire` | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### resolved + +The prompt resolution for the active run, or `None` outside a run. + +Exposes the full `ResolvedVariable` (`value`, `label`, `version`, `reason`, ...) so callers can inspect which prompt version is in play. + +**Type:** `ResolvedVariable`\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +#### Methods + +##### get\_ordering + +```python +def get_ordering() -> CapabilityOrdering +``` + +Run outermost so the prompt's baggage envelops the whole run, including the run span. + +###### Returns + +`CapabilityOrdering` + +##### get\_instructions + +```python +def get_instructions() -> Callable[[RunContext[AgentDepsT]], str | None] +``` + +Provide the resolved prompt to the agent's system prompt. + +###### Returns + +[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[[`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext)\[`AgentDepsT`\]\], [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None)\] + +##### wrap\_run + +`@async` + +```python +def wrap_run( + ctx: RunContext[AgentDepsT], + *, + handler: WrapRunHandler, +) -> AgentRunResult[Any] +``` + +Resolve the prompt once and keep its baggage active for the duration of the run. + +###### Returns + +[`AgentRunResult`](/docs/ai/api/pydantic-ai/run/#pydantic_ai.run.AgentRunResult)\[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] + +--- + +# [Media](https://pydantic.dev/docs/ai/harness/media/) + +> Content-addressed stores and walker helpers that move large BinaryContent payloads out of message history into deduplicated storage and put them back on demand. + +# Media + +A conversation that carries images, audio, or other `BinaryContent` inlines those bytes into every message. Persist that history and each snapshot re-serializes the payloads; the same image referenced by ten messages is ten copies of the bytes. Media externalization solves that: content-addressed stores write each payload once, keyed by its own hash, and leave a short `media+sha256://` URI in its place. Reach for it whenever binary payloads would otherwise balloon what you store or send. + +Import path + +Import these helpers from their submodule -- there is no top-level `pydantic_ai_harness` re-export: + +```python +from pydantic_ai_harness.media import ( + DiskMediaStore, + S3MediaStore, + SqliteMediaStore, + externalize_media, + restore_media, +) +``` + +> The API may change between releases. Where practical, breaking changes ship with a deprecation warning. + +## Building blocks, not a capability + +These are building blocks. There is no class you add to `Agent(capabilities=[...])` yet. [`StepPersistence`](/docs/ai/harness/step-persistence) already uses them to keep snapshots small when messages carry `BinaryContent`, and a forthcoming `MediaExternalizer` capability will reuse the same stores to rewrite `BinaryContent` into URL parts before the model sees them. + +## Why content-addressing + +The URI is derived from the payload hash, so identical bytes deduplicate automatically. The same bytes are stored once no matter how many messages or snapshots reference them, and moving the underlying storage is a one-line swap because the URI does not change. + +## Stores + +Every store implements the `MediaStore` protocol -- `put`, `get`, `exists`, `public_url`, and `get_metadata`, all async and content-addressed. + +Store + +Backed by + +Use when + +`DiskMediaStore(directory=...)` + +A directory on disk + +Local runs and tests + +`SqliteMediaStore(database=...)` + +A SQLite database + +A single-file store that travels with the data + +`S3MediaStore(bucket=, endpoint=, region=, ...)` + +S3 or an S3-compatible bucket + +Shared or production storage + +`S3MediaStore` uses path-style URLs plus handrolled SigV4, so it is compatible with AWS S3, Cloudflare R2 (`region='auto'`), MinIO, and other S3-compatible providers. `SqliteMediaStore` also accepts `connection=` instead of `database=` to share a `sqlite3.Connection`. + +## Walker helpers + +`externalize_media` and `restore_media` walk a message node and swap payloads for URIs and back: + +```python +from pydantic_ai_harness.media import DiskMediaStore, externalize_media, restore_media + +store = DiskMediaStore(directory='./media') + +# Replace BinaryContent larger than the threshold with media+sha256:// URIs. +lean = await externalize_media(message, media_store=store, threshold_bytes=32_000) + +# Later, rehydrate the URIs back into BinaryContent. +full = await restore_media(lean, media_store=store) +``` + +`externalize_media` only externalizes payloads over `threshold_bytes`; smaller ones stay inline. Round-trip is transparent -- `restore_media` returns `BinaryContent` with the original bytes. If you need to key media yourself, `media_uri_for` and `parse_media_uri` give you the raw URI round-trip. + +## Public URLs + +When a store is fronted by a CDN, a local HTTP server, or a signed-URL service, pass a `public_url=` resolver (or use `make_static_public_url`) to turn a stored `media+sha256://` URI into a URL the model can fetch directly. Without a resolver, `public_url(...)` returns `None`. + +A static base URL, for a public bucket or CDN: + +```python +from pydantic_ai_harness.media import S3MediaStore, make_static_public_url + +store = S3MediaStore( + bucket='my-bucket', + endpoint='https://.r2.cloudflarestorage.com', + region='auto', + access_key_id=..., secret_access_key=..., + key_prefix='media/', + public_url=make_static_public_url('https://pub-abc.r2.dev', key_prefix='media/'), +) +``` + +A presigned or rotating-signature URL -- pass any async callable that takes `(uri, MediaContext)`: + +```python +from pydantic_ai_harness.media import MediaContext, S3MediaStore + + +async def presign(uri: str, ctx: MediaContext) -> str: + key = 'media/' + uri.removeprefix('media+sha256://') + '.bin' + return await my_signer.generate(key, ttl=3600, content_type=ctx.media_type) + + +store = S3MediaStore(..., public_url=presign) +``` + +This is what the forthcoming `MediaExternalizer` will use to swap `BinaryContent` parts for `ImageUrl` / `AudioUrl` / other URL parts before the model sees the message, letting providers fetch big media over the wire without re-encoding bytes into the request body. Emitting a URL is always safe: pydantic-ai providers transparently download the bytes when the target model does not natively accept that URL type, so you only ever lose wire savings, never correctness. + +## `MediaContext` + +Every store method and both user-supplied callables (`PublicUrlResolver`, `KeyStrategy`) accept a `MediaContext` -- an extensible per-operation bag: + +```python +from collections.abc import Mapping +from dataclasses import dataclass, field + + +@dataclass(frozen=True, kw_only=True) +class MediaContext: + media_type: str | None = None # e.g. 'image/png' + filename: str | None = None # original filename, when known + metadata: Mapping[str, str] = field(default_factory=dict) # user-supplied tags +``` + +All fields default, so you pass what you have and ignore the rest; new fields are added non-breakingly as use cases emerge. `get_metadata(uri)` round-trips the user-supplied `metadata` mapping on all three stores; `media_type` is persisted separately (as the byte payload's `Content-Type`). + +## `KeyStrategy` + +The default on-store key layout is `.bin`. `DiskMediaStore` and `S3MediaStore` accept a `key_strategy=` override to fit an existing layout; `SqliteMediaStore` does not, since its primary key is the digest: + +```python +from pydantic_ai_harness.media import DiskMediaStore, MediaContext + + +def by_media_type(uri: str, ctx: MediaContext) -> str: + digest = uri.removeprefix('media+sha256://') + ext = {'image/png': '.png', 'image/jpeg': '.jpg'}.get(ctx.media_type or '', '.bin') + return f'images/{digest}{ext}' + + +store = DiskMediaStore('runs', key_strategy=by_media_type) +``` + +If your strategy depends on `ctx.media_type`, the same context must be supplied at read time for `get`/`exists` to find the blob. `DiskMediaStore` rejects strategies that produce absolute paths or `..` segments, to keep writes inside the store directory. `default_key_strategy` is exported if you want to build on it. + +## API + +Symbol + +Purpose + +`MediaStore` + +Async content-addressed store protocol (`put` / `get` / `exists` / `public_url` / `get_metadata`) + +`DiskMediaStore`, `SqliteMediaStore`, `S3MediaStore` + +Concrete stores + +`MediaContext` + +Per-operation context (media type, filename, tags) threaded through store operations + +`KeyStrategy`, `default_key_strategy` + +On-store key layout + +`PublicUrlResolver`, `make_static_public_url` + +Resolve a stored URI to a public URL + +`externalize_media`, `restore_media` + +Walk a message node to externalize / rehydrate payloads + +`media_uri_for`, `parse_media_uri` + +Compute and parse a `media+sha256://` URI + +Source: [`pydantic_ai_harness/media/`](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/media/). + +## Related + +- [Step Persistence](/docs/ai/harness/step-persistence) -- the first consumer of these stores, externalizing `BinaryContent` in run snapshots. + +--- + +# [Memory](https://pydantic.dev/docs/ai/harness/memory/) + +> Persistent, namespaced agent notebooks with bounded prompt injection, on-demand search, and concurrency-safe stores. + +# Memory + +Give an agent a persistent notebook that it can update, search, and reuse across runs without loading every stored file into every prompt. + +> \[!NOTE\] Import this capability from its submodule. It is not re-exported from `pydantic_ai_harness`: +> +> ```python +> from pydantic_ai_harness.memory import Memory +> ``` + +Memory is a released, non-experimental capability. Pydantic AI Harness is still on 0.x releases, so the API may change between minor releases. See the [version policy](/docs/ai/harness/#version-policy). + +## Notebook model + +Memory gives each agent a notebook made of Markdown files: + +- `MEMORY.md` is the main notebook. By default, a bounded excerpt and the names of other files are added to the current request as delimited user-role context. +- Other files hold longer or focused notes. The model reads them on demand or finds them with bounded text search. + +The model gets four tools: + +Tool + +Purpose + +`write_memory` + +Append to a file or replace one unique text fragment. Writes use optimistic concurrency and an idempotency identifier derived from the run and tool call. + +`read_memory` + +Read a bounded prefix of one memory file. + +`delete_memory` + +Delete a file. The main notebook is protected. + +`search_memory` + +Search across notebook files, subject to configured result, character, and file-scan limits. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.memory import FileStore, Memory + +agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[Memory(FileStore('.agent-memory'))], + defer_model_check=True, +) +``` + +The namespace is resolved by application code, not supplied to the tools. The model therefore cannot select another user's namespace in a tool call. + +## Injection modes and limits + +Automatic injection is enabled by default. Trusted usage guidance remains in model instructions, while model-written memory is enclosed in `` delimiters in a user-role part on the current request. Together, the guidance, main notebook, and file listing share a finite `max_tokens` budget, estimated at four characters per token. The default is 2,000 approximate tokens. `max_lines` is an additional limit on the main notebook. Backend reads are limited by `max_memory_size`, and the number of requested paths is derived from the prompt budget, so the capability never requests an unbounded file or listing. Content that does not fit is omitted with a prompt directing the model to use `read_memory` or `search_memory`. + +```python +from pydantic_ai_harness.memory import FileStore, Memory + +memory = Memory( + FileStore('.agent-memory'), + max_tokens=2_000, + max_lines=200, +) +``` + +Only the current request retains the injected user-role part, so copies do not accumulate in message history. Each model request receives the latest bounded snapshot, including after `write_memory` or an external update changes `MEMORY.md`. + +Set `inject_memory=False` for cache-stable prompts or durable workflows. The tools remain available, and the model can fetch memory only when it needs it: + +```python +from pydantic_ai_harness.memory import FileStore, Memory + +memory = Memory(FileStore('.agent-memory'), inject_memory=False) +``` + +With `injection_errors='ignore'` (the default), a store failure skips automatic injection and emits content-safe telemetry. Spans record the backend type and a hash of the resolved scope; successful injection records counts, and failures record the exception type. They do not record memory content. Set `injection_errors='raise'` when a run must fail rather than proceed without injected memory. Namespace and store resolver failures always propagate. Tool failures are still returned as tool errors; this setting controls automatic injection only. + +## Persistence and concurrency + +The store contract includes optimistic compare-and-swap mutations and idempotency. A write based on a stale revision fails with a conflict instead of overwriting a concurrent edit. Replaying the same run and tool call does not apply its mutation twice; reusing that operation identifier with different arguments raises `MemoryOperationConflictError`. These guarantees belong to the mutation operation, so custom stores must implement them atomically rather than composing separate read and write calls. + +Every `MemoryStore.read` call includes a finite `max_chars`, and every `list_paths` call includes a finite `limit`. A store returns the bounded prefix plus `MemoryFile.truncated=True` when more content exists, while its version still represents the complete file. `read_memory` marks that bounded result as truncated. `write_memory` refuses to append or edit an oversized externally supplied file because doing so would derive new content from a partial read; remediate or replace it through the backing store first. A custom `SearchableMemoryStore.search` must likewise honor `max_file_chars` as well as the result limits. + +Store + +Persistence and concurrency boundary + +`InMemoryStore()` + +Process lifetime; atomic across tasks using that store instance. + +`FileStore(directory)` + +Local filesystem; atomic Markdown replacement plus a hidden SQLite journal provide recovery, cross-process compare-and-swap, and durable idempotency receipts. + +`SqliteMemoryStore(database=...)` + +Durable single-host storage; compare-and-swap and idempotency are enforced in database transactions. + +`PostgresMemoryStore(pool)` + +Durable shared storage; compare-and-swap and idempotency are enforced in database transactions. The caller owns the pool lifecycle. + +```python +from pydantic_ai_harness.memory import FileStore, Memory, SqliteMemoryStore + +local_memory = Memory(FileStore('.agent-memory')) +sqlite_memory = Memory(SqliteMemoryStore(database='.agent-memory.db')) +``` + +`SqliteMemoryStore` can instead use a caller-owned `sqlite3.Connection`. Because operations run off the event loop, create that connection with `check_same_thread=False` and manage its lifecycle in the application. The connection must be dedicated to the store and idle at the start of every operation; a call fails rather than commit or roll back an active caller transaction. + +`FileStore` keeps the journal at `.memory-store.sqlite3` inside its root. Keep it with the Markdown files when copying or backing up the store. Editing a Markdown file outside the capability changes its content version and can produce a conflict with a prepared operation; the journal recovers operations interrupted between transaction preparation and filesystem replacement. + +`PostgresMemoryStore` accepts the driver-neutral `PostgresPool` protocol, so the harness does not require a particular PostgreSQL driver. Install and manage the driver in your application (for example, `uv add asyncpg`): + +```python +import asyncpg + +from pydantic_ai_harness.memory import Memory, PostgresMemoryStore + + +async def build_memory() -> tuple[Memory[None], asyncpg.Pool]: + pool = await asyncpg.create_pool('postgres://localhost/app') + memory = Memory(PostgresMemoryStore(pool)) + return memory, pool +``` + +Call `build_memory` during application startup and close the returned pool during shutdown. The store does not manage it. + +## Namespaces + +Use a namespace resolver when one `Agent` serves multiple users. It runs once per run from your typed dependencies, and its result is hidden from the model-facing tool schema. + +```python +from dataclasses import dataclass + +from pydantic_ai import Agent +from pydantic_ai_harness.memory import FileStore, Memory + + +@dataclass +class AppDeps: + user_id: str + + +agent = Agent( + 'anthropic:claude-sonnet-4-6', + deps_type=AppDeps, + capabilities=[ + Memory( + FileStore('/var/lib/myapp/memory'), + namespace=lambda ctx: ctx.deps.user_id, + ) + ], + defer_model_check=True, +) +``` + +Namespace isolation controls which records the capability addresses. It is not an authorization system for a custom or shared backing store. Validate the identity in application dependencies, restrict backend credentials, and ensure custom stores cannot escape the resolved namespace. + +## Search + +`search_memory` performs literal text search and always applies three bounds: + +- `max_search_results` limits returned matches, default 10. +- `max_search_result_chars` limits the combined scope-relative filename and snippet text, default 4,000 characters. +- `max_search_files` limits how many files a fallback scan may inspect, default 1,000. + +The bundled stores implement `SearchableMemoryStore`. For a custom store that implements only `MemoryStore`, `search_memory` requests at most `max_search_files + 1` paths, scans at most `max_search_files`, and performs bounded reads. Lexical scoring uses only each scope-relative filename and its bounded content; tenant namespaces and agent names never affect relevance. Implement the optional search protocol for an indexed or semantic backend while preserving the same tenant boundary and result limits. Semantic ranking is not built in. + +Before backend dispatch, queries are limited to 1,000 characters and 32 unique whitespace-separated terms. Repeated case-insensitive terms are collapsed so they cannot inflate scoring or scan work. + +## Configuration + +```python +from pydantic_ai_harness.memory import FileStore, Memory + +Memory( + FileStore('.agent-memory'), + store_resolver=None, # optional per-run store resolver + agent_name='main', # agent segment inside the namespace + namespace='', # string or per-run resolver + inject_memory=True, # False keeps prompts cache-stable + max_tokens=2_000, # finite approximate total injection budget + max_lines=200, # additional main-notebook line limit + max_memory_size=65_536, # per-file read, search, and write boundary + max_search_results=10, + max_search_result_chars=4_000, + max_search_files=1_000, + injection_errors='ignore', # or 'raise' + guidance=None, # None uses the default notebook guidance +) +``` + +## Agent specs + +Register `Memory` as a custom capability type when constructing an agent from a Python spec: + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.memory import Memory + +agent = Agent.from_spec( + { + 'model': 'anthropic:claude-sonnet-4-6', + 'capabilities': [ + {'Memory': {'backend': 'file', 'directory': '.agent-memory'}}, + ], + }, + custom_capability_types=[Memory], + defer_model_check=True, +) +``` + +The serializable backends are `memory`, `file`, and `sqlite`. A namespace callable and a live PostgreSQL pool must be configured in Python. + +## Durable execution compatibility + +Execution mode + +Support + +Normal `Agent.run` calls + +Supported with automatic injection or on-demand tools. + +Temporal and Prefect + +Use `inject_memory=False` with a statically configured store and on-demand tools. Automatic injection performs backend I/O in a model-request hook and is not workflow-safe. + +DBOS + +Normal execution works, but ordinary `FunctionToolset` calls are not DBOS-durable. Wrap memory operations in application-provided DBOS steps when durability is required. + +The memory backend and the workflow state backend are independent. Durable execution does not make an in-memory notebook persistent. + +## Security and provenance + +Memory is model-written, untrusted content that can re-enter future prompts. Keeping it in a delimited user-role part lowers its authority relative to model instructions, but this is not a hard prompt-injection boundary. Use `inject_memory=False` when less-trusted actors can write to the store, and expose memory only through application-controlled retrieval when stronger isolation is required. Do not store secrets unless the backend, retention policy, and access controls are appropriate. Sanitize content before rendering it into another trust domain. + +Memory records do not carry source citations or verified provenance. If an application needs auditable facts, store provenance in the note itself or implement a custom store and schema. Optimistic concurrency prevents lost updates; it does not establish that a remembered claim is true. + +## API reference + +- [`pydantic_ai_harness.memory` source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/memory/) +- [Pydantic AI capabilities](/docs/ai/core-concepts/capabilities/) +- [Pydantic AI hooks](/docs/ai/core-concepts/hooks/) + +The public module exports `Memory`, `MemoryToolset`, the bundled stores, the store protocols, mutation and search result models, and conflict exceptions. Import them from `pydantic_ai_harness.memory`. + +### Memory + +**Bases:** `AbstractCapability[AgentDepsT]` + +Persistent agent memory across sessions. + +`MEMORY.md` is injected as user-role context and longer topic files are available through `read_memory` and `search_memory`. Store access performed by automatic injection is not workflow-safe durable I/O. With Temporal or Prefect, use `inject_memory=False`; the static, idempotent `memory` toolset can then be wrapped by those integrations. DBOS does not currently wrap an ordinary `FunctionToolset` as a durable step, so this capability's tools are not DBOS-durable without an application-provided DBOS step wrapper. + +#### Attributes + +##### store + +Storage backend. The default persists only for the process lifetime. + +**Type:** `MemoryStore` **Default:** `field(default_factory=InMemoryStore)` + +##### store\_resolver + +Optional per-run store resolver. Resolver failures always propagate. + +**Type:** [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[[`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext)\[`AgentDepsT`\]\], `MemoryStore`\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### agent\_name + +Agent segment used to isolate memory within a namespace. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) **Default:** `'main'` + +##### namespace + +Static or per-run tenant namespace, never exposed as a tool argument. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[[`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext)\[`AgentDepsT`\]\], [`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `''` + +##### inject\_memory + +Inject stored memory when true; otherwise inject static tool guidance only. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `True` + +##### max\_tokens + +Approximate total token ceiling for the complete injected memory section. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `2000` + +##### max\_lines + +Maximum number of `MEMORY.md` content lines considered for injection. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `200` + +##### max\_memory\_size + +Per-file character boundary for backend reads, search, and writes. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `65536` + +##### max\_search\_results + +Maximum matches returned by one search. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `10` + +##### max\_search\_result\_chars + +Maximum combined snippet characters returned by one search. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `4000` + +##### max\_search\_files + +Maximum files scanned by one search. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `1000` + +##### guidance + +Override injected usage guidance; `''` disables guidance. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### injection\_errors + +Whether store failures during automatic injection are ignored or raised. + +**Type:** [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\['ignore', 'raise'\] **Default:** `'ignore'` + +#### Methods + +##### for\_run + +`@async` + +```python +def for_run(ctx: RunContext[AgentDepsT]) -> Memory[AgentDepsT] +``` + +Return a clone with scope resolution isolated to this run. + +###### Returns + +`Memory`\[`AgentDepsT`\] + +##### resolve\_scope + +```python +def resolve_scope(ctx: RunContext[AgentDepsT]) -> tuple[MemoryStore, str] +``` + +Return the cached run scope, or resolve one for direct toolset use. + +###### Returns + +[`tuple`](https://docs.python.org/3/library/stdtypes.html#tuple)\[`MemoryStore`, [`str`](https://docs.python.org/3/library/stdtypes.html#str)\] + +##### get\_toolset + +```python +def get_toolset() -> AgentToolset[AgentDepsT] | None +``` + +Provide the stable `memory` toolset. + +###### Returns + +`AgentToolset`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### get\_instructions + +```python +def get_instructions() -> AgentInstructions[AgentDepsT] | None +``` + +Provide trusted static guidance about using memory. + +Stored memory is added separately as user-role context by `before_model_request` so model-written content is not placed in the instruction channel. + +###### Returns + +`AgentInstructions`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### before\_model\_request + +`@async` + +```python +def before_model_request( + ctx: RunContext[AgentDepsT], + request_context: ModelRequestContext, +) -> ModelRequestContext +``` + +Add a bounded memory snapshot to only the current user request. + +###### Returns + +`ModelRequestContext` + +##### from\_spec + +`@classmethod` + +```python +def from_spec( + cls, + *, + backend: Literal['memory', 'file', 'sqlite'] = 'memory', + directory: str = '.agent-memory', + database: str = '.agent-memory.db', + agent_name: str = 'main', + namespace: str = '', + inject_memory: bool = True, + max_tokens: int = 2000, + max_lines: int = 200, + max_memory_size: int = 65536, + max_search_results: int = 10, + max_search_result_chars: int = 4000, + max_search_files: int = 1000, + guidance: str | None = None, + injection_errors: Literal['ignore', 'raise'] = 'ignore', +) -> Memory[AgentDepsT] +``` + +Construct a memory capability from serializable options. + +###### Returns + +`Memory`\[`AgentDepsT`\] + +##### get\_serialization\_name + +`@classmethod` + +```python +def get_serialization_name(cls) -> str | None +``` + +Return the name used by custom capability specs. + +###### Returns + +[`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) + +### MemoryToolset + +**Bases:** `FunctionToolset[AgentDepsT]` + +Scoped read/write/delete/search tools with CAS and durable idempotency. + +The stable `memory` ID lets Temporal and Prefect wrap this static toolset. DBOS does not currently turn an ordinary `FunctionToolset` into a durable step, so applications requiring DBOS durability must provide that wrapper. + +#### Methods + +##### write\_memory + +`@async` + +```python +def write_memory( + ctx: RunContext[AgentDepsT], + content: str, + file: str = MAIN_FILENAME, + old_text: str | None = None, +) -> MemoryWriteResult +``` + +Write persistent memory by appending or uniquely replacing text. + +Omit `old_text` to append, creating the file when necessary. Pass `old_text` to replace exactly one matching passage; use an empty `content` to remove that passage. Keep short durable facts in `MEMORY.md`, and longer or evolving topics in separate files. Update stale entries rather than adding contradictory duplicates. + +###### Returns + +`MemoryWriteResult` + +###### Parameters + +**`ctx`** : [`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext)\[`AgentDepsT`\] + +Framework-provided run context. + +**`content`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) + +Text to append, or replacement text for `old_text`. + +**`file`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) _Default:_ `MAIN_FILENAME` + +Memory filename; defaults to `MEMORY.md`. + +**`old_text`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) _Default:_ `None` + +Exact passage to replace, which must occur once. + +##### read\_memory + +`@async` + +```python +def read_memory(ctx: RunContext[AgentDepsT], file: str) -> str +``` + +Read a bounded prefix of one memory file. + +Memory may be stale background context, so verify volatile facts before relying on them. + +###### Returns + +[`str`](https://docs.python.org/3/library/stdtypes.html#str) + +###### Parameters + +**`ctx`** : [`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext)\[`AgentDepsT`\] + +Framework-provided run context. + +**`file`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) + +Memory filename returned by injection or search. + +##### delete\_memory + +`@async` + +```python +def delete_memory(ctx: RunContext[AgentDepsT], file: str) -> MemoryDeleteResult +``` + +Delete a non-main memory file that is no longer useful. + +`MEMORY.md` cannot be deleted; remove or correct its text with `write_memory` instead. + +###### Returns + +`MemoryDeleteResult` + +###### Parameters + +**`ctx`** : [`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext)\[`AgentDepsT`\] + +Framework-provided run context. + +**`file`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) + +Memory filename to delete. + +##### search\_memory + +`@async` + +```python +def search_memory(ctx: RunContext[AgentDepsT], query: str) -> MemorySearchResponse +``` + +Search memory files in the current tenant and agent scope. + +Results contain bounded snippets; call `read_memory` when a larger bounded excerpt is relevant. + +###### Returns + +`MemorySearchResponse` + +###### Parameters + +**`ctx`** : [`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext)\[`AgentDepsT`\] + +Framework-provided run context. + +**`query`** : [`str`](https://docs.python.org/3/library/stdtypes.html#str) + +Terms to find in memory filenames and content. + +--- + +# [Overflowing Tool Output](https://pydantic.dev/docs/ai/harness/overflowing-tool-output/) + +> Reduce oversized tool returns when they are produced -- truncate, spill to a queryable file, or summarize -- so a large payload does not persist in history. + +# Overflowing Tool Output + +`OverflowingToolOutput` reduces a tool return that is large enough to dominate the context window. Tool returns persist in history as `ToolReturnPart`s, so an oversized one is re-sent on every later model request, paying its token cost for the rest of the run. This capability intercepts a return when it is produced, reduces it once, and lets the reduced form persist -- the reduction is not recomputed per request. + +[Source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/overflowing_tool_output/) + +> The API may change between releases. Where practical, breaking changes ship with a deprecation warning. + +## The problem + +A tool can return a payload large enough to dominate the context window: a big file read, a verbose log, a large JSON document. Because tool returns persist in history, an oversized one is re-sent on every later model request, paying its token cost for the rest of the run. + +This is the overflow-to-file follow-up the [compaction](/docs/ai/harness/compaction) capability names as out of scope: it moves large tool outputs _out_ of the window at production time, rather than compressing or dropping context already inside it. + +## The three modes + +Mode + +Cost + +Lossy? + +What the model gets + +`Truncate` + +zero-LLM + +yes + +A head / tail / head+tail clamp of the text + +`Spill` + +zero-LLM + +no + +A handle + preview + shape sketch; full payload read back on demand + +`Summarize` + +one LLM call + +yes + +A size-gated summary (inherits the run's model by default) + +`Spill` is lossless: the full payload is persisted and the model reads slices of it through the registered `read_tool_result(handle, offset, limit, from_end, pattern)` tool (the Claude Code pattern, the core [#4352](https://github.com/pydantic/pydantic-ai/issues/4352) design). That tool is bounded: `offset >= 0`, `limit` clamped to a built-in line cap, the joined output capped, and `pattern` is a literal substring (not a regex), so a model-supplied value cannot hang the host with catastrophic backtracking. + +## Usage + +Construct an `Agent` with `OverflowingToolOutput()` in its `capabilities`. With no arguments it uses the default band: spill returns of 10,000 characters or more, with a bounded truncation fallback if the store cannot accept the write. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.overflowing_tool_output import OverflowingToolOutput + +agent = Agent('openai:gpt-4o', capabilities=[OverflowingToolOutput()]) +``` + +The capability registers a single `read_tool_result` tool so the model can page back into any spilled payload. Its own returns are exempt from reduction. + +## Bands: combine the modes + +Configure an ordered list of size `bands`. Each band is a `(over, action)` pair: when a return's measured size reaches `over`, its action runs. The band with the largest threshold that fits wins; anything below the smallest threshold passes through. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.overflowing_tool_output import ( + Band, + OverflowingToolOutput, + Spill, + Summarize, + Truncate, +) + +agent = Agent( + 'openai:gpt-4o', + capabilities=[ + OverflowingToolOutput( + bands=[ + Band(over=100_000, action=Spill()), # huge: keep losslessly, read back on demand + Band(over=20_000, action=Summarize()), # large: compress with the run's model + Band(over=5_000, action=Truncate()), # medium: cheap clamp + ], + # below 5,000: passthrough + ) + ], +) +``` + +The default band, when you pass no `bands`, is `Spill(then=Truncate())` at a 10,000-character threshold: lossless when a store accepts the write, a bounded truncation otherwise -- zero LLM cost and no silent drop. + +`Passthrough()` is an explicit no-op action for `bands` or `per_tool` lists, leaving matching returns untouched. + +### Fallbacks with `then` + +Every action takes an optional `then`, applied when the action cannot run: a `Spill` whose store errors, a `Truncate` / `Summarize` on a binary payload, a `Summarize` whose model call raises. `then` chains, so `Summarize(then=Spill(then=Truncate()))` degrades summarize -> spill -> truncate. + +### Per-tool overrides and filtering + +`per_tool` replaces the global band list for named tools (file reads to `head`, logs to `tail`); `tool_filter` (a `ToolSelector`) scopes which tools the capability touches at all. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.overflowing_tool_output import ( + Band, + OverflowingToolOutput, + Truncate, + TruncationStrategy, +) + +agent = Agent( + 'openai:gpt-4o', + capabilities=[ + OverflowingToolOutput( + per_tool={ + 'read_file': [Band(over=8_000, action=Truncate(strategy=TruncationStrategy.head))], + 'run_shell': [Band(over=8_000, action=Truncate(strategy=TruncationStrategy.tail))], + }, + tool_filter=['read_file', 'run_shell', 'search'], + ) + ], +) +``` + +`TruncationStrategy` has three members: `head` (keep the first characters, good for headers and schemas), `tail` (keep the last characters, good for build and test output where errors land last), and `head_tail` (keep both ends, elide the middle -- the default). + +## Both `return_value` and `content` are reduced + +A `ToolReturn` carries a `return_value` and an optional `content` that core renders as a separate, model-visible part which also persists in history. This capability measures and reduces both with the same band logic (they spill to distinct handles). Text `content` is reduced in place; non-text `content` (multimodal parts) that overflows is left unreduced with a `warnings.warn`, since it cannot be safely truncated. + +## Size unit + +Thresholds are measured in characters by default. Set `over_tokens=True` to measure in estimated tokens (the same ~4-chars-per-token heuristic as [compaction](/docs/ai/harness/compaction)); pass a `tokenizer` callable for accuracy. `Truncate.max_chars` is always characters -- truncation is a character operation regardless of the threshold unit. Set `strip_ansi=True` to strip ANSI escape sequences from text returns before measuring and reducing. + +## Spill store + +Spilled payloads go through the narrow `OverflowStore` protocol. The default `LocalFileStore` writes one file per `(run_id, tool_call_id, retry)` under a stable root directory and keeps it after the run, so a later `read_tool_result` -- in this run or a subsequent agent/run -- can still reach it. The handle is backend-addressable (a relative key), not an absolute local path, so a durable backend (Temporal, a blob store, or the core `ExecutionEnvironment` workspace once #4352 lands) can resolve the same handle in another process. Supply your own backend with `store=...`. + +```python +from typing import Protocol + + +class OverflowStore(Protocol): + async def write(self, key: str, data: bytes) -> str: ... # returns a handle + async def read(self, handle: str) -> bytes: ... +``` + +### Security model (shared root, not isolation) + +The store root is stable and shareable on purpose -- spilled files must be readable by a later agent or run -- so security does not come from per-instance isolation. It comes from two mechanisms: the root is created with `0700` (owner-only) permissions, and `read` resolves the target (following symlinks) and rejects anything that escapes the root via symlink, `..`, or an absolute path. Handle segments are also sanitized so a crafted handle cannot traverse out. + +### Cleanup: keep-forever by default, opt-in TTL pruning + +By default the store keeps spilled files forever -- deleting on run end would break a later agent that still wants to read a spill. To bound disk use, opt into age-based pruning: + +```python +from datetime import timedelta + +from pydantic_ai import Agent +from pydantic_ai_harness.overflowing_tool_output import LocalFileStore, OverflowingToolOutput + +store = LocalFileStore(cleanup_after=timedelta(hours=6)) # default: None = keep forever +agent = Agent('openai:gpt-4o', capabilities=[OverflowingToolOutput(store=store)]) +``` + +When set, a `write` schedules a background prune (a daemon thread, off the hot path) that deletes files whose modification time (`st_mtime`) is older than `cleanup_after`. Pruning is non-blocking and non-erroring: any failure is caught and surfaced via `warnings.warn`, never propagated into the agent run, so cleanup can never fail a run or block the hot path. Last-read time (`st_atime`) is unreliable on `noatime`/`relatime` mounts and is not used. + +Prefer external cleanup (cron, a sweeper) over the in-process TTL? Point it at the store root and delete by mtime: + +```python +import time +from pathlib import Path + +root = Path('/tmp/pyai_harness_overflow') # or your configured base_dir +cutoff = time.time() - 6 * 3600 +for path in root.rglob('*'): + if path.is_file() and path.stat().st_mtime < cutoff: + path.unlink(missing_ok=True) +``` + +## Usage accounting + +A `Summarize` call is a real request to the model, so its full usage -- tokens and the request itself -- folds into the run's `ctx.usage`, exactly like `SummarizingCompaction`. No token caps are imposed on the summary call. A `UsageLimits` request limit will see it. + +By default `Summarize` inherits the running agent's model (`ctx.model`). Pass a model id or instance to `Summarize(model=...)` to override, or a `summarize` callable to bypass the built-in prompt entirely. The `summary_prompt` template on the capability must contain both `{tool_name}` and `{output}` placeholders. + +## Edge cases + +- Binary returns spill verbatim and are never stringify-truncated; `Truncate` / `Summarize` on binary fall through to `then`. +- Structured / nested returns spill (or summarize) by preference -- truncating JSON produces invalid JSON. `Spill` includes a one-line shape sketch of the top level. +- `ModelRetry` and tool errors never reach this hook (they are raised, not returned), so the model always gets the full error it needs to recover. +- A large `ToolReturn.content` is reduced with the same bands as `return_value`; non-text content that overflows is left unreduced with a warning. +- Multiple oversized returns in one step get distinct handles (keyed per `tool_call_id`); retries get distinct handles too (keyed per `retry`), so a retried call never clobbers the earlier attempt's spill. + +## Relationship to other capabilities + +- Distinct from [compaction](/docs/ai/harness/compaction), which compresses or drops context already inside the window; this capability moves large tool outputs out of the window at production time. +- Consumes core [#4352](https://github.com/pydantic/pydantic-ai/issues/4352) (the canonical queryable-file primitive) through the `OverflowStore` seam once it lands. +- Distinct from `ClampOversizedMessages`, which clamps runaway model responses, not tool returns. + +## API reference + +### OverflowingToolOutput + +**Bases:** `AbstractCapability[AgentDepsT]` + +Reduce oversized tool returns when they are produced, persisting the reduction. + +A tool can return a payload large enough to dominate the context window. Tool returns persist in history, so an oversized one is re-sent on every later request. This capability intercepts a return in `after_tool_execute`, reduces it once, and lets the reduced form persist -- it is not recomputed per request. + +Three reduction modes, freely combined through an ordered list of size `bands`: + +- `Truncate`: clamp to a character budget. Lossy, zero-cost. +- `Spill`: persist the full payload, hand the model a `read_tool_result` handle plus a preview. Lossless. +- `Summarize`: size-gated LLM summary. Inherits the run's model by default. + +The first band whose `over` threshold the measured size meets wins; smaller returns pass through. `per_tool` replaces the band list for named tools; `tool_filter` scopes which tools are touched at all. The default is `Spill(then=Truncate())`: lossless when a store accepts the write, a bounded truncation otherwise. + +`ModelRetry` and other errors never reach this hook (they are raised, not returned), so error payloads the model needs to recover are never spilled or summarized. + +#### Attributes + +##### bands + +Ordered size bands. The first band whose `over` threshold is met wins. + +**Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[`Band`\] **Default:** `field(default_factory=_default_bands)` + +##### per\_tool + +Per-tool band lists that replace `bands` for the named tools. + +**Type:** [`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str), [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[`Band`\]\] **Default:** `field(default_factory=(dict[str, Sequence[Band]]))` + +##### tool\_filter + +Which tools this capability touches. Non-matching tools always pass through. + +**Type:** `ToolSelector`\[`AgentDepsT`\] **Default:** `'all'` + +##### over\_tokens + +Measure band thresholds in estimated tokens instead of characters. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `False` + +##### tokenizer + +Optional `(str) -> int` tokenizer for `over_tokens`. Defaults to a ~4-char heuristic. + +**Type:** [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)\[\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\], [`int`](https://docs.python.org/3/library/functions.html#int)\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### store + +Backend for spilled payloads. Defaults to a `LocalFileStore`. + +**Type:** `OverflowStore` | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### strip\_ansi + +Strip ANSI escape sequences from text returns before measuring and reducing. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `False` + +##### summary\_prompt + +Prompt template for `Summarize`. Must contain `{tool_name}` and `{output}`. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) **Default:** `_DEFAULT_SUMMARY_PROMPT` + +#### Methods + +##### get\_toolset + +```python +def get_toolset() -> AgentToolset[AgentDepsT] | None +``` + +Register the `read_tool_result` tool for reading spilled payloads on demand. + +###### Returns + +`AgentToolset`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### after\_tool\_execute + +`@async` + +```python +def after_tool_execute( + ctx: RunContext[AgentDepsT], + *, + call: ToolCallPart, + tool_def: ToolDefinition, + args: dict[str, Any], + result: Any, +) -> Any +``` + +Reduce the tool result -- both `return_value` and model-visible `content`. + +###### Returns + +[`Any`](https://docs.python.org/3/library/typing.html#typing.Any) + +--- + +# [Planning](https://pydantic.dev/docs/ai/harness/planning/) + +> Give an agent a structured, self-updating task plan through a single write_plan tool, without ever invalidating the prompt cache. + +# Planning + +`Planning` gives the model a structured, self-updating task plan through a single `write_plan` tool -- and surfaces the current plan back to the model every turn without ever invalidating the prompt cache. + +[Source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/planning/) + +> The API may change between releases. Where practical, breaking changes ship with a deprecation warning. + +## The problem + +Long agentic runs drift: the model loses track of what it set out to do and what's left. The usual fix -- keep a running plan and re-inject it into the system prompt each turn -- invalidates the prompt cache. The system prompt sits at the front of the request, so every plan edit changes the cached prefix and forces the whole conversation to be re-processed at full token price. + +## The solution + +`Planning` gives the model one tool, `write_plan`, that owns the plan (whole-plan replacement -- pass the full list every call, no indices). The current plan is surfaced back to the model as an ephemeral reminder appended to the tail of each request, behind a cache breakpoint: + +- The reminder is added in `wrap_model_request`, which runs _after_ the durable history is persisted, so it reaches the model but is never written to `message_history`. No reminders accumulate across turns. +- A `CachePoint` is placed immediately _before_ the reminder, so the cached prefix (tools + system + real conversation) stays byte-identical turn over turn. Only the reminder falls outside the cache. + +So the plan stays current in the model's view while the cached prefix is never invalidated; the only added cost is re-reading the reminder each turn. + +## Usage + +Construct an `Agent` with `Planning()` in its `capabilities`. The `write_plan` tool is registered automatically, and the static usage guidance is added to the system prompt: + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.planning import Planning + +agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[Planning()]) + +result = agent.run_sync('Refactor the auth module and add tests.') +print(result.output) +``` + +## The tool + +Tool + +Purpose + +`write_plan(items)` + +Create or replace the full plan. The model passes the entire ordered list every time, including unchanged, completed, and cancelled steps. + +Each item is a `content` string plus a `status` (`pending`, `in_progress`, `completed`, `cancelled`). The convention -- stated in the guidance and noted in the tool's reply -- is to keep exactly one step `in_progress`. + +There is no `get_plan` tool: the current plan is already in the model's context via the tail reminder every turn. + +## Why whole-plan replacement + +Addressing steps by mutable integer index (insert/remove/reorder) is error-prone for both the code (index bookkeeping) and the model (indices it just saw can go stale within a turn). Restating the whole plan each call removes that: there are no indices to track, and a later call can't corrupt partial state. For short plans the token cost is negligible. + +## Caching guarantee + +The plan is never injected into the system prompt or instructions. Static usage guidance goes there (cache-stable); only the mutable plan rides the ephemeral tail reminder. Across turns: + +- the durable history grows append-only and is replayed byte-identically, so the whole prefix is a cache hit; +- the reminder and its `CachePoint` live only in the per-request copy, so they can't invalidate anything and aren't persisted. + +`CachePoint` is supported on Anthropic and Amazon Bedrock; on providers without prompt caching it's simply ignored (nothing to bust). + +## Configuration + +```python +from pydantic_ai_harness.planning import Planning + +Planning( + guidance=None, # static system-prompt guidance; None = default, '' = omit + cache_ttl='5m', # TTL for the cache breakpoint before the reminder ('5m' | '1h') +) +``` + +- `guidance` -- static planning guidance added to the system prompt. It is identical on every request, so it stays cache-stable. Leave it as `None` for the built-in default, or set `''` to omit guidance entirely. +- `cache_ttl` -- TTL for the cache breakpoint placed before the plan reminder. One of `'5m'` or `'1h'`. + +## Observing the plan + +Plan state is per-run (a fresh, isolated plan each run via `for_run`), so it doesn't live on the `Planning()` instance you construct. To see the final plan, read the most recent `write_plan` tool return from the run's messages -- its content is the rendered plan: + +```python +from pydantic_ai import Agent +from pydantic_ai.messages import ToolReturnPart +from pydantic_ai_harness.planning import Planning + +agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[Planning()]) + +result = agent.run_sync('Refactor the auth module and add tests.') +plans = [ + part.content + for message in result.all_messages() + for part in message.parts + if isinstance(part, ToolReturnPart) and part.tool_name == 'write_plan' +] +latest_plan = plans[-1] if plans else None +print(latest_plan) +``` + +## Composition + +`Planning` contributes a single leaf toolset (`write_plan`), some static instructions, and a `wrap_model_request` hook. It does not wrap or intercept other toolsets, so it composes cleanly alongside other capabilities and your own tools in the same `Agent(..., capabilities=[...])`. + +The tail reminder is only appended when the last message in the request is a `ModelRequest` and the plan is non-empty, so an empty plan adds nothing to the request. Because the reminder and its `CachePoint` live only in the per-request copy and never enter the durable history, `Planning` is safe with message-history replay and does not accumulate stale reminders across turns. + +## Agent spec (YAML/JSON) + +`Planning` works with Pydantic AI's [agent spec](/docs/ai/core-concepts/agent-spec/) feature for defining agents in YAML or JSON: + +```yaml +# agent.yaml +model: anthropic:claude-sonnet-4-6 +capabilities: + - Planning: {} +``` + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.planning import Planning + +agent = Agent.from_file('agent.yaml', custom_capability_types=[Planning]) +result = agent.run_sync('...') +print(result.output) +``` + +Pass `custom_capability_types` so the spec loader knows how to instantiate `Planning`. Arguments can be passed in the YAML too: + +```yaml +capabilities: + - Planning: + cache_ttl: '1h' +``` + +## Further reading + +- [Pydantic AI capabilities](/docs/ai/core-concepts/capabilities/) +- [Hooks](/docs/ai/core-concepts/hooks/) -- `wrap_model_request` is the ephemeral injection point used here +- [Anthropic prompt caching](https://docs.claude.com/en/docs/build-with-claude/prompt-caching) +- [Code Mode](/docs/ai/harness/code-mode) -- another prompt-cache-aware harness capability + +## API reference + +### Planning + +**Bases:** `AbstractCapability[AgentDepsT]` + +Structured task planning that never invalidates the prompt cache. + +The plan is owned by the model through a single `write_plan` tool. The current plan is surfaced back as an _ephemeral_ reminder appended to the tail of each request (after the latest message), with a cache breakpoint placed in front of it. Because the reminder always sits after the breakpoint and is never written to the durable message history, the cached prefix stays byte-identical across turns -- only the small reminder is re-read each turn. + +Static usage guidance goes into the system prompt via `get_instructions`, which is cache-stable; the mutable plan is _never_ injected there. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.planning import Planning + +agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[Planning()]) +``` + +#### Attributes + +##### guidance + +Static planning guidance for the system prompt. Cache-stable (identical every request). Leave as `None` for the default, or set `''` to omit guidance entirely. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### cache\_ttl + +TTL for the cache breakpoint placed before the plan reminder. + +**Type:** [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)\['5m', '1h'\] **Default:** `'5m'` + +#### Methods + +##### for\_run + +`@async` + +```python +def for_run(ctx: RunContext[AgentDepsT]) -> Planning[AgentDepsT] +``` + +Return a fresh per-run instance with isolated plan state (config preserved). + +###### Returns + +`Planning`\[`AgentDepsT`\] + +##### get\_instructions + +```python +def get_instructions() -> AgentInstructions[AgentDepsT] | None +``` + +Static, cache-stable guidance on using the planning tool. + +###### Returns + +`AgentInstructions`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### get\_toolset + +```python +def get_toolset() -> AgentToolset[AgentDepsT] | None +``` + +Toolset providing `write_plan` over this run's plan state. + +###### Returns + +`AgentToolset`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### wrap\_model\_request + +`@async` + +```python +def wrap_model_request( + ctx: RunContext[AgentDepsT], + *, + request_context: ModelRequestContext, + handler: WrapModelRequestHandler, +) -> ModelResponse +``` + +Append the current plan as an ephemeral tail reminder behind a cache breakpoint. + +This runs _after_ core has persisted the durable history, and the per-request message list it mutates is never written back. So the reminder and its `CachePoint` reach the model but never enter `ctx.state.message_history` -- the cached prefix stays byte-identical across turns and no stale reminders accumulate. The `CachePoint` sits before the reminder text, so the reminder falls outside the cached region and cannot invalidate it. + +###### Returns + +[`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse) + +##### get\_serialization\_name + +`@classmethod` + +```python +def get_serialization_name(cls) -> str | None +``` + +Serialization name for agent-spec support. + +###### Returns + +[`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) + +--- + +# [Pydantic AI Docs](https://pydantic.dev/docs/ai/harness/pydantic-ai-docs/) + +> Give an agent a tool that locates and returns Pydantic AI documentation on demand instead of preloading it into the system prompt. + +# Pydantic AI Docs + +`PyaiDocs` gives an agent a single tool, `read_pyai_docs(topic)`, that locates a Pydantic AI documentation page and returns it verbatim. Nothing is bundled into context up front. Each call resolves the topic from a configured local checkout first, then falls back to fetching the page from `pydantic/pydantic-ai:main`, so it works whether or not you have a local checkout (the remote fallback needs network access). + +[Source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/docs/) + +> The API may change between releases. Where practical, breaking changes ship with a deprecation warning. + +## The problem + +An agent that authors Pydantic AI capabilities, hooks, tools, or toolsets needs the current docs for those APIs. Preloading the docs into the system prompt spends context the agent rarely needs in full, and pins a snapshot that drifts from `main`. + +## The solution + +`PyaiDocs` exposes one tool, `read_pyai_docs(topic)`, that locates the requested page and returns it verbatim. Each call resolves the topic from a configured local checkout first, then falls back to fetching the page from `pydantic/pydantic-ai:main`, so it works whether or not you have a local checkout (the remote fallback needs network access). + +The available topics are `capabilities`, `hooks`, `tools`, `tools-advanced`, `toolsets`, and `agent`. + +## Usage + +Construct an `Agent` with `PyaiDocs()` in its `capabilities`. Point `local_docs_path` at a local Pydantic AI docs checkout to read from disk first, or omit it to always fetch from the remote source: + +```python +from pathlib import Path + +from pydantic_ai import Agent +from pydantic_ai_harness.docs import PyaiDocs + +agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[PyaiDocs(local_docs_path=Path('~/pydantic/ai/base/docs').expanduser())], +) + +result = agent.run_sync('Read the toolsets docs, then explain how to build a FunctionToolset.') +print(result.output) +``` + +The capability also adds a short static instruction telling the model that the `read_pyai_docs` tool exists and to read the relevant topic before authoring or modifying a Pydantic AI capability, hook, tool, or toolset, rather than relying on memory. The instruction is cache-stable, so it does not invalidate the prompt-cache prefix between turns. + +## Resolution order + +Each call resolves in this order: + +1. **Local checkout** -- when `local_docs_path` (or the `PYDANTIC_AI_HARNESS_DOCS_PATH` env var) is set and `{path}/{topic}.md` exists, that file is read and returned. +2. **Remote fetch** -- otherwise the page is fetched from `https://raw.githubusercontent.com/pydantic/pydantic-ai/main/docs/{topic}.md`. +3. **Neither resolves** -- a descriptive error naming the local path tried and the URL. + +The capability never runs git. Keep the local checkout current yourself; the remote path always reads `main`, so it is the fresh fallback. + +`local_docs_path` takes precedence over the `PYDANTIC_AI_HARNESS_DOCS_PATH` env var. Both have `~` expanded, so a raw `~/...` path resolves to the local checkout instead of silently falling through to the remote source. With neither set, every call goes straight to the remote source. + +## Configuration + +Option + +Default + +Purpose + +`local_docs_path` + +`None` + +Local pyai docs checkout to read first. Falls back to the `PYDANTIC_AI_HARNESS_DOCS_PATH` env var, then to the remote source. + +`cache` + +`True` + +Memoize each returned doc in-process for the capability's lifetime, so a topic is read or fetched at most once. + +Caching lives on the capability instance and is shared across the toolsets it builds, so a memoized topic survives multiple agent runs that reuse the same `PyaiDocs`. Set `cache=False` to re-read or re-fetch on every call -- useful when the local checkout changes underneath a long-lived capability. + +## Agent spec (YAML/JSON) + +`PyaiDocs` works with Pydantic AI's [agent spec](/docs/ai/core-concepts/agent-spec/) feature for defining agents in YAML or JSON. Its serialization name is `PyaiDocs`: + +```yaml +# agent.yaml +model: anthropic:claude-sonnet-4-6 +capabilities: + - PyaiDocs: {} +``` + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.docs import PyaiDocs + +agent = Agent.from_file('agent.yaml', custom_capability_types=[PyaiDocs]) +result = agent.run_sync('...') +print(result.output) +``` + +Pass `custom_capability_types` so the spec loader knows how to instantiate `PyaiDocs`. + +## API reference + +### PyaiDocs + +**Bases:** `AbstractCapability[AgentDepsT]` + +Locate and return Pydantic AI documentation on demand. + +Exposes a single `read_pyai_docs(topic)` tool. Docs are located and returned when asked for -- never bundled into context. Each call resolves the topic from a configured local checkout first, then falls back to fetching the page from `pydantic/pydantic-ai:main`, so it works in any environment. + +The local checkout path comes from `local_docs_path`, or the `PYDANTIC_AI_HARNESS_DOCS_PATH` env var when that is unset; with neither set every call goes straight to the remote source. The capability never runs git -- keep the local checkout current yourself; the remote path always reads `main`. + +```python +from pathlib import Path + +from pydantic_ai import Agent +from pydantic_ai_harness.docs import PyaiDocs + +agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[PyaiDocs(local_docs_path=Path('~/pydantic/ai/base/docs').expanduser())], +) +``` + +#### Attributes + +##### local\_docs\_path + +Local pyai docs checkout to read first. When `None`, falls back to the `PYDANTIC_AI_HARNESS_DOCS_PATH` env var, then to the remote source. + +**Type:** `Path` | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### cache + +If `True`, each returned doc is memoized in-process for the capability's lifetime, so a topic is read or fetched at most once. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `True` + +#### Methods + +##### get\_instructions + +```python +def get_instructions() -> AgentInstructions[AgentDepsT] | None +``` + +Static, cache-stable guidance on using the docs tool. + +###### Returns + +`AgentInstructions`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### get\_toolset + +```python +def get_toolset() -> AgentToolset[AgentDepsT] | None +``` + +Toolset providing `read_pyai_docs` over the resolved local path and shared cache. + +###### Returns + +`AgentToolset`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### get\_serialization\_name + +`@classmethod` + +```python +def get_serialization_name(cls) -> str | None +``` + +Serialization name for agent-spec support. + +###### Returns + +[`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) + +--- + +# [Runtime Authoring](https://pydantic.dev/docs/ai/harness/runtime-authoring/) + +> Let an agent write, validate, and persist real pydantic-ai capabilities at runtime, live on the next run. + +# Runtime Authoring + +`RuntimeAuthoring` lets an agent author, validate, and persist real pydantic-ai capabilities while it runs. It exposes three tools that let the model write a capability class to disk as Python source, validate it immediately, and manage the set of authored capabilities. Each authored capability is a real `pydantic_ai.capabilities.AbstractCapability` subclass, so it can contribute instructions, model settings, a toolset, native tools, or a lifecycle hook. + +[Source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/runtime_authoring/) + +> The API may change between releases. Where practical, breaking changes ship with a deprecation warning. + +## The problem + +A coding agent often discovers, mid-task, that it wants a behavior its host does not yet have: a guardrail, an extra instruction, a tool, a request hook. The capability surface to express that already exists -- but normally only a developer can write a capability class, wire it into the agent, and restart. The agent itself cannot extend its own host while it runs. + +## The solution + +`RuntimeAuthoring` exposes three tools: + +- `author_capability(name, code)` -- write `code` to `/.py`, import it, and validate it. Validation requires exactly one `pydantic_ai.capabilities.AbstractCapability` subclass that constructs with no arguments; the side-effect-free static getters (`get_instructions`, `get_toolset`, `get_native_tools`, `get_model_settings`, `get_serialization_name`) are exercised. The async lifecycle hooks are not run -- they need a live `RunContext`. +- `list_authored_capabilities()` -- list authored capabilities with their status and any validation error. +- `disable_authored_capability(name)` -- stop a capability from being injected on the next run. + +A "hook" is not a standalone object in pydantic-ai -- it is a method on a capability. So authoring a hook means authoring a capability that overrides one lifecycle method. A single overridden hook is a valid capability. + +## Usage + +Construct `RuntimeAuthoring` with a `directory` for the authored files, then add it to the agent's `capabilities`: + +```python +from pathlib import Path + +from pydantic_ai import Agent +from pydantic_ai_harness.runtime_authoring import RuntimeAuthoring + +authoring = RuntimeAuthoring(directory=Path('.authored')) +agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[authoring]) +``` + +The agent can now call `author_capability`, `list_authored_capabilities`, and `disable_authored_capability`. `RuntimeAuthoring` also contributes static, cache-stable system-prompt guidance explaining these tools. Leave `guidance=None` for the default text, or pass your own string; set `guidance=''` to omit it entirely. + +## Activation boundary + +A capability **cannot** be added to a live, already-executing run. pydantic-ai resolves the effective capability set once at the start of each run (the run's root capability is fixed; there is no setter). So an authored capability is live on the **next** `agent.run(...)`, not the run that authored it. Authoring writes and validates the capability immediately, but its tools and hooks only exist once the next run's toolset and capability chain are assembled at run start. + +### Integration contract + +The orchestrator drives the loop, so it owns the one-line contract: thread the store's active capabilities into each run via `agent.run(..., capabilities=...)`. With that in place, the authored capability is live on the very next loop iteration -- no process restart: + +```python +from pathlib import Path + +from pydantic_ai import Agent +from pydantic_ai_harness.runtime_authoring import RuntimeAuthoring + +authoring = RuntimeAuthoring(directory=Path('.authored')) +agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[authoring]) + +history = None +done = False +next_prompt = 'Start the task.' +while not done: + extra = authoring.store.load_active() + result = await agent.run(next_prompt, message_history=history, capabilities=extra) + history = result.all_messages() + # ... decide `next_prompt` and `done` from `result` ... +``` + +`authoring.store` is the disk-backed `CapabilityStore` over the same `directory`. `store.load_active()` re-imports and re-constructs every active authored capability for injection into the next run. Entries that fail to load (corrupt source, construction error) are skipped, not raised, so one bad capability never blocks the rest. + +## Persistence + +Authored capabilities persist to disk: each is one `/.py` file, indexed by a sibling `manifest.json`. A fresh process picks them up by constructing a new `RuntimeAuthoring` over the same `directory` and calling `store.load_active()`. + +`manifest.json` records each capability's name, module file, class name, status (`active` or `disabled`), and last validation error. That is the surface a UI can read to show what the agent has authored. The manifest is written atomically (temp file plus `os.replace`), so a crash mid-write never leaves a partial file that reads back as "no capabilities". + +Capability names must be lowercase letters, digits, and underscores, starting with a letter. Reusing a name replaces the previous capability of that name. A code that imports but fails validation is still written to disk (so it can be inspected) and recorded with its `last_error` set; `load_active()` skips it. + +## Trust boundary + +`RuntimeAuthoring` executes arbitrary Python in-process at import, construction, and run time. That is the same trust boundary an agent that already runs shell commands and edits files operates under, which is the deliberate choice here. Do not point it at a directory whose contents you would not run yourself, and treat authored capabilities as code the agent is executing on your host. + +Because authored capabilities hold live code, they are not spec-serializable (`get_serialization_name()` returns `None`) and are persisted as source rather than as an [agent spec](/docs/ai/core-concepts/agent-spec/). + +## Typing + +Imported authored code is dynamic, but nothing typed `Any` crosses back into the harness: every value pulled from an authored module is narrowed with `isinstance`/`issubclass` before use, and loaded instances are typed `AbstractCapability[object]`. Because `AgentDepsT` is contravariant, an `AbstractCapability[object]` is accepted by any agent's `capabilities=` parameter. + +## API reference + +### RuntimeAuthoring + +**Bases:** `AbstractCapability[AgentDepsT]` + +Let an agent author, validate, and persist real pydantic-ai capabilities at runtime. + +Exposes `author_capability(name, code)`, `list_authored_capabilities`, and `disable_authored_capability`. Authoring writes a real `.py` to `directory`, imports it, and validates it (exactly one `AbstractCapability` subclass that constructs with no arguments and whose static getters run). Authored capabilities hold live code, so they are not spec-serializable and are persisted as source rather than as a spec. + +Activation boundary: a capability cannot be added to a live, already-executing run -- pydantic-ai resolves the capability set once at the start of each run. The authored capability becomes usable on the next `agent.run(...)`. The integration contract is one line on the orchestrator side: thread the store's active capabilities into the next run. + +```python +from pathlib import Path + +from pydantic_ai import Agent +from pydantic_ai_harness.runtime_authoring import RuntimeAuthoring + +authoring = RuntimeAuthoring(directory=Path('.authored')) +agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[authoring]) + +# Loop: each iteration injects whatever the agent has authored so far. +result = await agent.run('build a logging capability', capabilities=authoring.store.load_active()) +``` + +This executes authored Python in-process -- the same trust boundary an agent that already runs shell commands and edits files operates under. The dormant `pa` Monty hook-slot registration system is the sandboxed alternative; see the capability README. + +#### Attributes + +##### directory + +Directory holding the authored `.py` files and the `manifest.json` index. + +**Type:** `Path` + +##### guidance + +Static system-prompt guidance on authoring. Cache-stable. Leave `None` for the default, or set `''` to omit guidance entirely. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### store + +The disk-backed store. Call `store.load_active()` to inject authored capabilities into the next run. + +**Type:** `CapabilityStore` + +#### Methods + +##### get\_instructions + +```python +def get_instructions() -> AgentInstructions[AgentDepsT] | None +``` + +Static, cache-stable guidance on the authoring tools. + +###### Returns + +`AgentInstructions`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### get\_toolset + +```python +def get_toolset() -> AgentToolset[AgentDepsT] | None +``` + +Toolset providing the authoring tools over this capability's store. + +###### Returns + +`AgentToolset`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### get\_serialization\_name + +`@classmethod` + +```python +def get_serialization_name(cls) -> str | None +``` + +Not spec-serializable: the capability holds a live, disk-backed store. + +###### Returns + +[`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) + +### CapabilityStore + +Read/write index of authored capability `.py` files under `directory`. + +#### Methods + +##### write + +```python +def write(name: str, code: str) -> AuthoredCapability +``` + +Write `code` to `.py`, validate it, and upsert the manifest entry. + +Raises `ValueError` for an invalid name (before writing anything). A code that imports but fails validation is still written (so it can be inspected) and recorded with `last_error` set; `load_active` skips it. + +###### Returns + +`AuthoredCapability` + +##### disable + +```python +def disable(name: str) -> bool +``` + +Mark the named capability disabled so `load_active` stops returning it. Returns whether it existed. + +###### Returns + +[`bool`](https://docs.python.org/3/library/functions.html#bool) + +##### list\_all + +```python +def list_all() -> list[AuthoredCapability] +``` + +Return every manifest entry, in insertion order. + +###### Returns + +[`list`](https://docs.python.org/3/glossary.html#term-list)\[`AuthoredCapability`\] + +##### load\_active + +```python +def load_active() -> list[AbstractCapability[object]] +``` + +Construct every active authored capability for per-run injection. + +Re-imports and re-constructs each active entry. Entries that fail to load (corrupt source, construction error) are skipped, not raised, so one bad capability never blocks the rest. A load outcome that disagrees with the record's `last_error` is persisted back to the manifest: a newly broken entry records its error, a re-fixed entry clears it, so the manifest stays truthful about which capabilities are actually active. + +###### Returns + +[`list`](https://docs.python.org/3/glossary.html#term-list)\[`AbstractCapability`\[[`object`](https://docs.python.org/3/glossary.html#term-object)\]\] + +--- + +# [Shell](https://pydantic.dev/docs/ai/harness/shell/) + +> Give a Pydantic AI agent shell command execution with allow/deny controls, environment scrubbing, and managed background processes. + +# Shell + +`Shell` gives an agent the ability to run shell commands, with allow/deny controls, environment scrubbing, and managed background processes. It exposes command-execution tools rooted at a working directory and cleans up any background processes automatically when the agent run ends. + +[Source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/shell/) + +## The problem + +Agents frequently need to run a build, a test suite, a linter, or a quick `grep`. Wiring up subprocess handling -- streaming output, timeouts, truncation, killing runaway processes, and cleaning up background jobs at the end of a run -- is fiddly boilerplate that every agent reinvents. + +`Shell` bundles that plumbing into a single [capability](/docs/ai/core-concepts/capabilities/): configurable allow/deny lists, output truncation tuned to keep the useful tail, optional sticky working directory, environment control that can keep host secrets out of spawned commands, and automatic cleanup of background processes when the run finishes. + +## Usage + +Construct `Shell` with a working directory and pass it to an `Agent` via the `capabilities` parameter: + +```python +from pydantic_ai import Agent +from pydantic_ai_harness import Shell + +agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[Shell(cwd='./workspace', allowed_commands=['ls', 'cat', 'rg'])], +) + +result = agent.run_sync('List the Python files and summarize the largest one.') +print(result.output) +``` + +By default `Shell` runs in the current directory with the built-in destructive-command denylist active -- `Shell()` alone is a working (if permissive) configuration. + +## Tools + +`Shell` contributes four tools to the agent: + +Tool + +Purpose + +`run_command` + +Run a command synchronously and return labelled stdout/stderr plus exit code. Honors a per-call or default timeout. + +`start_command` + +Launch a long-running command (server, watcher) in the background; returns an ID. + +`check_command` + +Report the status and accumulated output of a background command. + +`stop_command` + +Terminate a background command and return its final output. + +`run_command` accepts an optional `timeout_seconds` argument that overrides `default_timeout` for a single call. `check_command` and `stop_command` take the `command_id` string returned by `start_command`. + +Output is labelled with `[stdout]` / `[stderr]` markers and an `[exit code: N]` line on non-zero exit. When it exceeds `max_output_chars` the **tail** is kept (the head is dropped), so errors, stack traces, and the `[stderr]` section -- which all land at the end -- survive truncation. + +## Command controls + +Two mutually exclusive lists decide which executables may run, plus filters for shell operators and interactive commands: + +Field + +Effect + +`allowed_commands` + +If non-empty, only these executables may run (allowlist). + +`denied_commands` + +These executables are always rejected (denylist). + +`denied_operators` + +Shell operators (e.g. `>`, `>>`, `|`) that are rejected when present. + +`allow_interactive` + +If `False` (default), commands that expect a TTY (`vi`, `sudo`, `ssh`, ...) are blocked. + +`allowed_commands` and `denied_commands` are mutually exclusive -- set one, not both. Setting both raises a `ValueError` at construction. `denied_commands` defaults to a list of destructive commands (`rm`, `rmdir`, `mkfs`, `dd`, `format`, `shutdown`, `reboot`, `halt`, `poweroff`, `init`); pass an empty list to disable it. The executable name is extracted with `shlex`, so arguments don't bypass the check. + +A denied command surfaces to the model as a [`ModelRetry`](/docs/ai/tools-toolsets/tools-advanced/#tool-retries), not a hard error: the run continues and the model can pick an allowed command instead. + +Best-effort, not a security boundary + +These command checks are best-effort. A sufficiently motivated agent can defeat them (e.g. `bash -c '...'`, env-var indirection). For hard guarantees, run the agent inside OS-level isolation -- a container or sandbox. + +## Environment control + +By default a spawned command inherits the agent process's full environment. In a sandbox that holds LLM API keys, tokens, or other secrets, a command the model writes can read them. Two fields control what the subprocess sees: + +Field + +Effect + +`env` + +Explicit environment that replaces inheritance entirely. The subprocess sees exactly these variables and nothing else. + +`denied_env_patterns` + +Glob patterns (`fnmatch`) for variable names stripped from the base environment. Mirrors `denied_commands`. + +`env` is a hard boundary for inherited environment variables: set it and inherited secrets cannot reach the subprocess at all (you supply `PATH` and anything else the command needs). `denied_env_patterns` is a denylist over the inherited environment -- lighter to configure when you only need to drop a few known-sensitive names. The two compose: when both are set, patterns also filter the explicit `env`. Leaving both unset preserves the inherit-everything default. + +```python +import os + +from pydantic_ai_harness import Shell +from pydantic_ai_harness.shell import LLM_API_KEY_ENV_PATTERNS + +# Strip provider credentials from the inherited environment. +Shell(cwd='./repo', denied_env_patterns=LLM_API_KEY_ENV_PATTERNS) + +# Or hand the subprocess a fixed environment, inheriting nothing. +Shell(cwd='./repo', env={'PATH': os.environ['PATH'], 'HOME': os.environ['HOME']}) +``` + +`LLM_API_KEY_ENV_PATTERNS` covers common provider prefixes (`ANTHROPIC_*`, `GATEWAY_*`, `GEMINI_*`, `GOOGLE_*`, `OPENAI_*`, `OPENROUTER_*`) plus `PYDANTIC_AI_GATEWAY_API_KEY`. It targets LLM credentials only -- it does not cover other host secrets (a `LOGFIRE_TOKEN`, a GitHub token, cloud credentials), and its prefixes are coarse, so `GOOGLE_*` also strips non-credential vars like `GOOGLE_APPLICATION_CREDENTIALS`. Treat it as a starting point and add your own patterns. It is not the default: stripping environment variables silently would break agents that rely on inherited credentials, so it is opt-in. + +`env` is enforced at spawn, not applied as a post-hoc filter on a running process: the subprocess starts with exactly the resolved environment (your `env`, minus anything `denied_env_patterns` removes from it). That makes it a real boundary for inherited environment variables, unlike the best-effort command denylist. It is not a full security boundary: a command running under the same OS identity can still read host files -- use OS-level isolation for that. The flip side is that a pattern broad enough to strip `PATH` or `HOME`, or an `env` that omits them, can break command resolution. External commands may still run via the shell's built-in default `PATH` on some systems, but don't rely on it -- set `PATH` explicitly when you replace the environment. + +## Background processes + +`start_command` writes stdout/stderr to temp files and returns a short ID. Use `check_command(command_id)` to poll and `stop_command(command_id)` to terminate and collect final output. Processes are launched in their own session (`start_new_session`) so the whole process group can be signalled -- `SIGTERM`, escalating to `SIGKILL` after a grace period. + +On run end, the toolset's cleanup terminates every still-running background process and deletes its temp files. The agent runtime enters toolsets via an `AsyncExitStack`, so this cleanup runs whether the run succeeds or raises -- an agent that forgets to call `stop_command` won't leak processes. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness import Shell + +agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[Shell(cwd='./app', allowed_commands=['npm', 'curl'])], +) + +result = agent.run_sync( + 'Start the dev server with `npm run dev`, wait for it to boot, ' + 'then curl http://localhost:3000/health and report the status.' +) +print(result.output) +``` + +## Working directory + +By default each command runs in `cwd` and `cd` has no lasting effect. Set `persist_cwd=True` to make `cd` sticky across calls: each command is wrapped so that after it runs, its final working directory is recorded to a private temp file, and that directory is carried into subsequent calls. The path is only updated when the command exits `0`, and the record is written out-of-band (not to stdout) so command output can never spoof the tracked directory. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness import Shell + +agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[Shell(cwd='.', persist_cwd=True, allowed_commands=['cd', 'ls', 'pwd'])], +) +``` + +Each run gets a fresh toolset instance, so the tracked directory and any background processes are isolated between concurrent runs and always start back at the configured `cwd`. + +## Configuration + +Every field of `Shell` with its default: + +```python +from pydantic_ai_harness import Shell + +Shell( + cwd='.', # str | Path -- working directory + allowed_commands=[], # allowlist (mutually exclusive with denied) + denied_commands=[...], # denylist (defaults to destructive commands) + denied_operators=[], # blocked shell operators + default_timeout=30.0, # seconds, per run_command + max_output_chars=50_000, # output cap returned to the model + persist_cwd=False, # make cd sticky across calls + allow_interactive=False, # allow TTY-style commands + env=None, # explicit env, replacing inheritance (None = inherit) + denied_env_patterns=[], # glob patterns stripped from the env +) +``` + +## Agent spec (YAML/JSON) + +`Shell` works with Pydantic AI's [agent spec](/docs/ai/core-concepts/agent-spec/), so you can declare it in a config file instead of Python: + +```yaml +# agent.yaml +model: anthropic:claude-sonnet-4-6 +capabilities: + - Shell: + cwd: ./workspace + allowed_commands: ['ls', 'cat', 'rg', 'pytest'] +``` + +```python +from pydantic_ai import Agent +from pydantic_ai_harness import Shell + +agent = Agent.from_file('agent.yaml', custom_capability_types=[Shell]) +``` + +Pass `custom_capability_types` so the spec loader knows how to instantiate `Shell`. + +## Further reading + +- [Pydantic AI capabilities](/docs/ai/core-concepts/capabilities/) +- [Toolsets](/docs/ai/tools-toolsets/toolsets/) + +## API reference + +### Shell + +**Bases:** `AbstractCapability[AgentDepsT]` + +Shell command execution for agents. + +Commands execute in a subprocess rooted at `cwd`. Use `allowed_commands` or `denied_commands` to control what the agent can invoke. + +#### Attributes + +##### cwd + +Working directory for command execution. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | `Path` **Default:** `'.'` + +##### allowed\_commands + +If non-empty, only these command names may be executed (allowlist). + +**Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `field(default_factory=(list[str]))` + +##### denied\_commands + +These command names are always rejected (denylist). + +Defaults to blocking destructive commands (rm, dd, shutdown, etc.). Set to an empty list to disable. + +**Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `field(default_factory=(lambda: list(_DEFAULT_DENIED_COMMANDS)))` + +##### denied\_operators + +Shell operators that are blocked (e.g. '>', '>>', '|' for restrictive mode). + +**Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `field(default_factory=(list[str]))` + +##### default\_timeout + +Default timeout in seconds for command execution. + +**Type:** [`float`](https://docs.python.org/3/library/functions.html#float) **Default:** `30.0` + +##### max\_output\_chars + +Maximum characters of output returned to the model. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) **Default:** `50000` + +##### persist\_cwd + +If True, track cd commands and adjust the working directory for subsequent calls. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `False` + +##### allow\_interactive + +If True, allow interactive commands (vi, nano, ssh, etc.). Blocked by default. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `False` + +##### env + +Explicit environment for spawned subprocesses, replacing inheritance. + +When `None` (default) the subprocess inherits the parent environment. Set this to a fixed mapping to start subprocesses with exactly these variables and nothing else -- a hard boundary that keeps host secrets (LLM API keys, tokens) out of commands the agent runs. + +**Type:** [`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str), [`str`](https://docs.python.org/3/library/stdtypes.html#str)\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### denied\_env\_patterns + +Glob patterns for environment variable names to strip before spawning. + +Follows the `denied_*` naming convention but matches by glob (`fnmatch`, e.g. `OPENAI_*`), since env secrets cluster by prefix -- unlike `denied_commands`, which matches executable names exactly. Names matching any pattern are removed from the base environment; applied on top of `env` when both are set, so patterns filter an explicit `env` too. See `LLM_API_KEY_ENV_PATTERNS` for a ready-made provider-credential denylist. + +**Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `field(default_factory=(list[str]))` + +#### Methods + +##### get\_toolset + +```python +def get_toolset() -> ShellToolset[AgentDepsT] +``` + +Build and return the shell toolset. + +###### Returns + +`ShellToolset`\[`AgentDepsT`\] + +--- + +# [Step Persistence](https://pydantic.dev/docs/ai/harness/step-persistence/) + +> Record what an agent did at each boundary, save provider-valid snapshots to resume or fork from, and track tool side effects across crashes. + +# Step Persistence + +`StepPersistence` records what an agent did at each boundary, separate from whether the run can be safely resumed. It is the persistence substrate for orchestrators that delegate to sub-agents -- for example, an AICA orchestrator that spawns a `code_librarian` to investigate one symbol, then continues that delegate's investigation with a follow-up question. + +It is not a full graph-state checkpoint. Capability-state restore, workspace snapshots, and graph-node resume are out of scope and tracked separately (see `pydantic-ai-harness` issues #149 and #196). + +[Source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/step_persistence/) + +> The API may change between releases. Where practical, breaking changes ship with a deprecation warning. + +## What it gives you + +1. **Append-only step events.** Every interesting boundary (run start/end, model request, tool call, failure) appends a `StepEvent`. A run killed mid-tool-call still leaves a usable event trail. +2. **Continuable snapshots.** A `ContinuableSnapshot` is saved only at boundaries where the message history is provider-valid: every `ToolCallPart` has a matching `ToolReturnPart` or `RetryPromptPart`, with no orphan, duplicate, or out-of-order returns. Pass the snapshot's `messages` back to `Agent.run(message_history=...)` to continue or fork. +3. **Tool-effect ledger.** Every tool call's lifecycle (`started`, `completed`, `failed`) is recorded against `(run_id, tool_call_id)`. After a crash, a tool with a `started` record and no terminal update should be treated as `unknown_after_crash`: the side effect may or may not have happened. +4. **Lineage metadata.** `conversation_id` (sequence) and `parent_run_id` (hierarchy) are independent axes. See [Three-level identity](#three-level-identity). + +## Quick start + +```python +import asyncio + +from pydantic_ai import Agent +from pydantic_ai_harness.step_persistence import StepPersistence, InMemoryStepStore + +store = InMemoryStepStore() +librarian = Agent( + 'openai:gpt-5', + capabilities=[StepPersistence(store=store, agent_name='code_librarian')], +) + + +async def main(): + await librarian.run('Find ThinkingPartDelta and confirm the callable allowance') + + +asyncio.run(main()) +``` + +That is the whole setup. `run_id` is always per-`Agent.run` call, matching pydantic\_ai's `RunContext.run_id`. For multi-turn logical grouping use `conversation_id=` -- that is the pydantic\_ai-native primitive for it (see [Three-level identity](#three-level-identity)). + +`run_id` resolution per call: + +- **Explicit `run_id='libr-1'`** becomes the id for this one call. This suits single-shot use cases (a deterministic id for testing, replay, debugging, or a one-off scripted run). Reusing one capability instance with the same explicit `run_id` across multiple `.run()` calls raises `ValueError` in `before_run`. The tool-effect ledger is keyed by `(run_id, tool_call_id)` and providers reuse deterministic tool-call ids, so a silent collision would erase the `unknown_after_crash` signal. Use `conversation_id=` for multi-turn grouping instead. +- **`agent_name` set, `run_id` unset** derives `'{agent_name}-{8-char-hex}'`, freshly materialised in `for_run` per `.run()` call. Reusing one capability instance across runs yields distinct ids (`code_librarian-a3b2`, `code_librarian-c9d1`, and so on). This is the recommended default for delegate capabilities. +- **Neither set** falls back to `ctx.run_id` (pydantic\_ai's auto-generated id) per `.run()` call, and to a UUID4 if that is absent. + +The orchestrator pattern -- one logical agent serving many turns -- uses `conversation_id`, not a shared `run_id`: + +```python +import asyncio + +from pydantic_ai import Agent +from pydantic_ai_harness.step_persistence import StepPersistence, InMemoryStepStore + +store = InMemoryStepStore() +orchestrator = Agent( + 'openai:gpt-5', + capabilities=[StepPersistence(store=store, agent_name='orchestrator')], +) + + +async def main(): + for turn in turns: + await orchestrator.run(turn, conversation_id='orch-conv') + + # All turns of this orchestrator, chronological: + records = await store.list_runs(conversation_id='orch-conv') + + +asyncio.run(main()) +``` + +## Three-level identity + +The capability mirrors pydantic\_ai's identity stack: + +Concept + +Definition + +Granularity + +`conversation_id` + +The dialogue. Resolved by pydantic\_ai from the `conversation_id=` argument to `Agent.run`, or the most recent `conversation_id` on `message_history`, or a fresh UUID7. + +sequence of runs + +`run_id` + +One `Agent.run` invocation. + +one step in the sequence + +`step_index` + +Graph-node count within a run (`ctx.run_step`). + +one node within one run + +`StepEvent.conversation_id` and `RunRecord.conversation_id` are populated from `ctx.conversation_id`. So three `.run()` calls sharing one `conversation_id` produce three distinct `run_id`s, all queryable as a group: + +```python +import asyncio + + +async def main(): + runs = await store.list_runs(conversation_id='conv-abc') # 3 records, chronological + + +asyncio.run(main()) +``` + +## Continuing a delegate's investigation + +pydantic\_ai already has `message_history=` for "carry on with this prior context". `StepPersistence` does not introduce a parallel mechanism. It exposes one helper that loads the most recent provider-valid snapshot: + +```python +import asyncio + +from pydantic_ai import Agent +from pydantic_ai_harness.step_persistence import ( + StepPersistence, + InMemoryStepStore, + continue_run, +) + +store = InMemoryStepStore() +librarian = Agent( + 'openai:gpt-5', + capabilities=[StepPersistence(store=store, agent_name='code_librarian')], +) + + +async def main(): + # Earlier: tag the first turn with a conversation id so the follow-up can find it. + await librarian.run( + 'Find ThinkingPartDelta and confirm the callable allowance', + conversation_id='libr-conv', + ) + + # Later (possibly a different process): + prior_run = (await store.list_runs(conversation_id='libr-conv'))[-1].run_id + history = await continue_run(store, run_id=prior_run) + await librarian.run( + 'Read _apply_provider_details_delta and check the path', + message_history=history, + conversation_id='libr-conv', # keep the conversation grouping + ) + + +asyncio.run(main()) +``` + +`fork_run(store, run_id=...)` returns the same shape but is intended when the caller wants a branched logical run from that snapshot point (the new run gets a fresh `run_id` and probably a fresh `conversation_id`). + +### What "safe to continue from" means + +`continue_run` only returns the messages of the latest provider-valid snapshot for that `run_id`. Snapshots are written at two boundaries: + +- after every `CallToolsNode` completes (all tool calls returned), and +- at `after_run`, as a fallback if the run reached no such boundary. + +A run that crashed mid-tool-call has events (`tool_call_started`) but no snapshot for that point. `continue_run` returns the snapshot from the previous safe boundary, not the failed step. If no continuable snapshot exists at all, `continue_run` raises `LookupError`. + +## Run lineage: `parent_run_id` + +`parent_run_id` is a lineage label, not a functional dependency. It does two things: + +- Every `StepEvent` and `RunRecord` carries it, so you can filter and group. +- `store.list_runs(parent_run_id='orch-1')` returns every delegate run pointing at that orchestrator. + +It is auto-inferred for in-process delegation: when an orchestrator's tool synchronously calls a delegate's `Agent.run(...)`, the delegate's `StepPersistence` picks up the orchestrator's `run_id` via a `ContextVar` that the orchestrator's `wrap_run` set. No threading required: + +```python +import asyncio + +from pydantic_ai import Agent +from pydantic_ai_harness.step_persistence import StepPersistence, InMemoryStepStore + +store = InMemoryStepStore() +orchestrator = Agent( + 'openai:gpt-5', + capabilities=[StepPersistence(store=store, agent_name='orchestrator')], +) +librarian = Agent( + 'openai:gpt-5', + capabilities=[StepPersistence(store=store, agent_name='code_librarian')], +) + + +@orchestrator.tool_plain +async def ask_librarian(question: str) -> str: + result = await librarian.run(question) # parent_run_id auto-filled + return result.output + + +async def main(): + # Tag the orchestrator turn so the lookup below can find its run_id. + await orchestrator.run( + 'Where is ThinkingPartDelta defined?', + conversation_id='orch-conv', + ) + + # All librarian runs now point at the orchestrator's run_id: + orch_run_id = (await store.list_runs(conversation_id='orch-conv'))[-1].run_id + delegates = await store.list_runs(parent_run_id=orch_run_id) + + +asyncio.run(main()) +``` + +Set `parent_run_id=` explicitly to override (for example, cross-process delegation where `ContextVar`s do not propagate). + +`parent_run_id` is distinct from `conversation_id`. The orchestrator and delegate usually live in different conversations (the orchestrator talks to a user; the delegate talks to itself). But they share a parent-child link. + +## Inspecting a run tree + +`list_runs` returns matches sorted by `started_at` ascending across all backends -- pick the most recent with `[-1]`. + +```python +import asyncio + + +async def main(): + # Every delegate of one orchestrator run (chronological) + delegates = await store.list_runs(parent_run_id='orch-3f2a') + + # Every run in one dialogue (multi-turn conversation across many .run() calls) + turns = await store.list_runs(conversation_id='conv-abc') + latest_turn = turns[-1] + + # Filters combine (AND): + focused = await store.list_runs( + parent_run_id='orch-3f2a', + conversation_id='libr-conv', + ) + + # Detail per run: + events = await store.list_events(run_id=delegates[0].run_id) + snapshot = await store.latest_snapshot(run_id=delegates[0].run_id) + unresolved = await store.list_unresolved_tool_effects(run_id=delegates[0].run_id) + + +asyncio.run(main()) +``` + +## Failure recovery + +```python +import asyncio + + +async def main(): + # An earlier delegate run died mid-investigation. + events = await store.list_events(run_id='libr-3f2a') + unresolved = await store.list_unresolved_tool_effects(run_id='libr-3f2a') + for record in unresolved: + # status == 'started' with no terminal update -- unknown_after_crash. + print(f'tool {record.tool_name} ({record.tool_call_id}) may or may not have run') + print(f' idempotency_key={record.idempotency_key} ' + f'effect_summary={record.effect_summary}') + + # Decide whether to resume or branch: + history = await continue_run(store, run_id='libr-3f2a') + # If the unresolved tools were read-only and safe to redo: + await librarian.run('continue investigating', message_history=history, + conversation_id='libr-conv') + # If side effects might have happened and the orchestrator wants a fresh attempt: + history = await fork_run(store, run_id='libr-3f2a') + # ... pass to a new delegate run with a different agent_name / conversation_id. + + +asyncio.run(main()) +``` + +Side-effect deduplication is the orchestrator's responsibility. Tools that write external state should annotate their in-flight `ToolEffectRecord` via `annotate_tool_effect`: + +```python +from pydantic_ai import RunContext +from pydantic_ai_harness.step_persistence import annotate_tool_effect + + +@orchestrator.tool +async def set_label(ctx: RunContext[Deps], issue: int, label: str) -> str: + await annotate_tool_effect( + store, + ctx, + idempotency_key=f'issue-{issue}::label::{label}', + effect_summary=f'set label {label!r} on issue #{issue}', + ) + await github.set_label(issue, label) # the actual side effect + return 'ok' +``` + +The helper reads the active `run_id` from the `StepPersistence` `ContextVar` and `tool_call_id` / `tool_name` from `ctx`, then merges the metadata into the prior record. It is a no-op when called outside a step-persistence-wrapped tool call. `after_tool_execute` preserves both fields when it writes the terminal `completed` / `failed` entry. + +## Backends + +- `InMemoryStepStore` -- process-local; great for tests. +- `FileStepStore(directory)` -- directory layout under `//`: + - `run.json` -- `RunRecord` (lineage) + - `events.jsonl` -- append-only `StepEvent`s + - `tool_effects.jsonl` -- append-only `ToolEffectRecord`s, scoped to this run + - `snapshots/{seq}.json` -- `ContinuableSnapshot`s, named by a per-run monotonic counter (not `step_index`, which would collide when the same `run_id` is reused across `Agent.run` calls, since `ctx.run_step` resets to 0 each call). +- `SqliteStepStore(database='runs.db')` -- single SQLite file with tables `runs`, `events`, `snapshots`, `tool_effects`, and a sibling `media` table for externalized blobs (see [Persisting media](#persisting-media) below). WAL mode is enabled; `tool_effects` upserts per `(run_id, tool_call_id)` so the latest state wins; snapshots use `AUTOINCREMENT seq` to mirror `FileStepStore._next_snapshot_seq`. Pass `connection=` instead of `database=` to share a `sqlite3.Connection` with the rest of your application; the connection must be opened with `check_same_thread=False` because hook calls are dispatched onto a worker thread. + +All three implement the same async `StepStore` protocol, so capability hooks never block the event loop on the file/sqlite backends (I/O is dispatched via `anyio.to_thread`). + +`FileStepStore` validates `run_id` against `[A-Za-z0-9_.-]{1,200}` (and rejects `..`) to prevent path traversal. Callers passing user-controlled IDs should still sanitise first. + +## Persisting media + +`BinaryContent` payloads (images, audio, documents, video) inlined as base64 inside a snapshot would balloon every file or row containing the message. Both `FileStepStore` and `SqliteStepStore` externalize any `BinaryContent.data` at or above **64 KiB** through a configured `MediaStore`, leaving a URI reference in the snapshot. Round-trip is transparent: `latest_snapshot(...).messages[*]` returns `BinaryContent` with the original bytes. + +StepStore + +Default `media_store` + +Where blobs live + +`InMemoryStepStore` + +not applicable + +bytes stay in the in-memory snapshot + +`FileStepStore` + +`DiskMediaStore(/media/)` + +`/media/.bin` + +`SqliteStepStore` + +`SqliteMediaStore(database=)` + +sibling `media` table in the same DB + +Override the destination by passing your own `MediaStore`: + +```python +from pydantic_ai_harness.step_persistence import FileStepStore +from pydantic_ai_harness.media import S3MediaStore + +store = FileStepStore( + 'runs', + media_store=S3MediaStore( + bucket='my-bucket', + endpoint='https://.r2.cloudflarestorage.com', + region='auto', + access_key_id=..., + secret_access_key=..., + ), + media_threshold_bytes=64 * 1024, # raise or lower if you want +) +``` + +Opt out entirely (keep bytes inline in the snapshot JSON/row): + +```python +from pydantic_ai_harness.step_persistence import FileStepStore, SqliteStepStore + +FileStepStore('runs', media_store=None) +SqliteStepStore(database='runs.db', media_store=None) +``` + +URIs are `media+sha256://`, content-addressed. The same blob written through any `MediaStore` resolves the same way, so dedup is automatic and moving the underlying storage is a one-line swap. The shipped implementations are: + +- `DiskMediaStore(directory)` -- one file per blob at `/.bin`. +- `SqliteMediaStore(database=...)` or `SqliteMediaStore(connection=...)` -- one row per blob (`INSERT OR IGNORE` for content-addressed dedup). +- `S3MediaStore(bucket=, endpoint=, region=, access_key_id=, secret_access_key=)` -- path-style URLs plus handrolled SigV4. Compatible with AWS S3, Cloudflare R2 (`region='auto'`), MinIO, and other S3-compatible providers. PUT/GET/HEAD only -- no multipart, lifecycle, or listing in v1. + +### Exposing externalized bytes as URLs + +Each store accepts a `public_url=` callable that turns the canonical `media+sha256://` URI into a URL the model can fetch directly. The forthcoming `MediaExternalizer` capability will use this to swap `BinaryContent` parts for `ImageUrl` / `AudioUrl` / other URL parts before the model sees the message, letting providers fetch big media over the wire without re-encoding bytes into the request body. + +Static base URL (public R2 bucket, CDN): + +```python +from pydantic_ai_harness.media import S3MediaStore, make_static_public_url + +store = S3MediaStore( + bucket='my-bucket', + endpoint='https://.r2.cloudflarestorage.com', + region='auto', + access_key_id=..., secret_access_key=..., + key_prefix='media/', + public_url=make_static_public_url('https://pub-abc.r2.dev', key_prefix='media/'), +) +``` + +Presigned or rotating-signature URL -- pass any async callable that takes `(uri, MediaContext)`: + +```python +from pydantic_ai_harness.media import MediaContext, S3MediaStore + + +async def presign(uri: str, ctx: MediaContext) -> str: + key = 'media/' + uri.removeprefix('media+sha256://') + '.bin' + return await my_signer.generate(key, ttl=3600, content_type=ctx.media_type) + + +store = S3MediaStore(..., public_url=presign) +``` + +### `MediaContext`, an extensible per-operation bag + +Every `MediaStore` method (`put`, `get`, `exists`, `public_url`, `get_metadata`) and both user-supplied callables (`PublicUrlResolver`, `KeyStrategy`) accept a `MediaContext`: + +```python +from collections.abc import Mapping +from dataclasses import dataclass, field + + +@dataclass(frozen=True, kw_only=True) +class MediaContext: + media_type: str | None = None # e.g. 'image/png' + filename: str | None = None # original filename, when known + metadata: Mapping[str, str] = field(default_factory=dict) # user-supplied tags +``` + +All fields default; new fields are added non-breakingly as use cases emerge. Pass what you have, ignore the rest. + +**Persistence by store.** `get_metadata(uri)` round-trips the user-supplied `metadata` mapping on all three stores. `media_type` is also persisted but is not part of what `get_metadata` returns (it is stored for the byte payload itself, for example as the `Content-Type`). + +- `SqliteMediaStore` writes `metadata` to a JSON column and `media_type` to a dedicated column. +- `S3MediaStore` sends `metadata` as signed `x-amz-meta-*` headers (ASCII alphanumeric plus dash key names) and `media_type` as `Content-Type`; `get_metadata` reads the `x-amz-meta-*` values back from the HEAD response. +- `DiskMediaStore` writes a sidecar JSON file (`.meta.json`) alongside each blob, atomic via tmp plus rename. Sidecars are absent only when the put carried no metadata. + +### `key_strategy`: controlling the backend storage path + +Default is `.bin`. `DiskMediaStore` and `S3MediaStore` accept overrides to fit existing layouts; `SqliteMediaStore` does not (its primary key is the digest, so a user-chosen key would either break dedup or be a no-op): + +```python +from pydantic_ai_harness.media import DiskMediaStore, MediaContext + + +def by_media_type(uri: str, ctx: MediaContext) -> str: + digest = uri.removeprefix('media+sha256://') + ext = {'image/png': '.png', 'image/jpeg': '.jpg'}.get(ctx.media_type or '', '.bin') + return f'images/{digest}{ext}' + + +store = DiskMediaStore('runs', key_strategy=by_media_type) +``` + +**Caveat**: if your strategy depends on `context.media_type` (for example, to pick an extension), `get(uri)` and `exists(uri)` will not find the blob unless the same context is supplied at read time. For pure path-organisation strategies (no context dependency) the constraint does not apply. + +`DiskMediaStore` rejects strategies that produce absolute paths or paths containing `..` segments, to prevent escaping the store directory. + +Separately, all three stores accept a `public_url=` resolver, useful when a CDN, local HTTP server, or signed-URL service fronts the bytes. Without it `public_url(...)` returns `None` (the model never sees a URL unless a resolver is configured and it returns a string). + +pydantic\_ai providers transparently download bytes from a URL when the target model does not natively accept that URL type, so emitting a URL is always safe: you only ever lose wire savings, never correctness. + +The future `MediaExternalizer` capability + +When it lands, the composition will be `Agent(capabilities=[MediaExternalizer(store), StepPersistence(...)])` and `StepPersistence` will see already-URL-ified messages, so the externalize walk becomes a no-op. The existing API does not change. + +### Persisting to unsupported backends + +DynamoDB, Postgres, Redis, GCS, and other backends are out of scope for this release. Write your own `StepStore` (about ten methods on a Protocol) or your own `MediaStore` (three methods) and pass it via `store=` / `media_store=`. Please open an issue if you ship one -- we want to feed the eventual shared adapter layer with N >= 3 real implementations before abstracting. + +## What this capability does not do + +- It does not restore capability per-run state, graph-node state, retry counters, or in-flight streaming responses. +- It does not deduplicate replayed side effects automatically. Tools that write artifacts, labels, PRs, or external state should call `annotate_tool_effect(store, ctx, ...)` (see [Failure recovery](#failure-recovery)) so the orchestrator can decide whether replay is safe. +- It does not clean up old snapshots or events. Retention is the caller's responsibility. +- It does not emit OpenTelemetry spans. pydantic\_ai's `Instrumentation` capability already spans `agent run` / `chat` / `running tool` and populates `gen_ai.agent.name`, `gen_ai.agent.call.id`, `gen_ai.conversation.id` via baggage. A future change may add step-persistence attributes to the active span; that is tracked as a follow-up issue. + +## Related + +- [Capabilities overview](/docs/ai/harness/) +- [Code Mode](/docs/ai/harness/code-mode) + +## API reference + +### StepPersistence + +**Bases:** `AbstractCapability[AgentDepsT]` + +Append-only step log + continuable snapshots + tool-effect ledger. + +The capability emits a `StepEvent` at every interesting boundary (run/model-request/tool-call start, completion, failure), records a `ToolEffectRecord` per tool call so the orchestrator can decide whether replay is safe, and saves a `ContinuableSnapshot` at every provider-valid boundary -- the end of each `CallToolsNode` -- plus a fallback save at `after_run` if the run reached no such boundary. + +A run that crashes between `before_tool_execute` and `after_tool_execute` leaves a visible event trail and a `started` tool-effect record, but no new continuable snapshot -- the latest snapshot reflects the last provider-valid state. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.step_persistence import StepPersistence, InMemoryStepStore + +store = InMemoryStepStore() +librarian = Agent( + 'openai:gpt-5', + capabilities=[StepPersistence(store=store, agent_name='code_librarian')], +) +await librarian.run('Find ThinkingPartDelta and confirm the callable allowance') +``` + +Use `continue_run(store, run_id=...)` / `fork_run(store, run_id=...)` to load a prior snapshot, then pass the result to `Agent.run(..., message_history=...)`. + +#### Attributes + +##### store + +Backend that records events, snapshots, and tool effects. + +**Type:** `StepStore` **Default:** `field(default_factory=InMemoryStepStore)` + +##### agent\_name + +Logical agent name (e.g. `code_librarian`, `reproducer`). + +Used as a stable prefix for the auto-derived `run_id` so store inspection shows readable IDs like `code_librarian-a3b2`. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### run\_id + +Identifier for this one `Agent.run` call. + +`run_id` is per-call, matching `pydantic_ai.RunContext.run_id`. For multi-turn logical grouping use `conversation_id` on `Agent.run(...)` -- that is the pyai-native primitive for it. + +Resolution order (materialised in `for_run`): + +1. **Explicit value** → used as-is. Single-shot use cases: deterministic id for testing, replay, debugging. Reusing the capability across multiple `.run()` calls with the same explicit `run_id` raises `ValueError` in `before_run` -- the tool-effect ledger keys on `(run_id, tool_call_id)` and providers reuse deterministic tool-call ids, so a silent collision would erase the `unknown_after_crash` signal. Use `conversation_id=` on `Agent.run` for multi-turn grouping. +2. **`agent_name` set, `run_id` unset** → `{agent_name}-{short-uuid}`, freshly materialised per `.run()`. Reusing the capability instance yields distinct ids. Recommended default for delegate capabilities. +3. **Neither set** → `ctx.run_id` per `.run()`, falling back to UUID4. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### parent\_run\_id + +Run that spawned this one. + +Auto-inferred from the enclosing `StepPersistence` `wrap_run` scope -- when an orchestrator's tool synchronously calls a delegate's `Agent.run(...)`, the delegate picks up the orchestrator's `run_id` here without manual threading. Set explicitly to override (e.g. for cross-process delegation where `ContextVar`s do not propagate). + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### metadata + +Free-form metadata stored on the `RunRecord` and on each event. + +**Type:** [`dict`](https://docs.python.org/3/reference/expressions.html#dict)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str), [`str`](https://docs.python.org/3/library/stdtypes.html#str)\] **Default:** `field(default_factory=_empty_metadata)` + +#### Methods + +##### from\_spec + +`@classmethod` + +```python +def from_spec(cls, *args: Any, **kwargs: Any) -> StepPersistence[Any] +``` + +Construct from a serialised spec. + +Supports `backend='memory'` (default), `backend='file'` (with `directory`), or `backend='sqlite'` (with `database`). Raises `ValueError` for any other `backend` value -- silently falling back to in-memory storage would turn a typo into accidental non-durability. + +###### Returns + +`StepPersistence`\[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] + +##### for\_run + +`@async` + +```python +def for_run(ctx: RunContext[AgentDepsT]) -> AbstractCapability[AgentDepsT] +``` + +Materialise `run_id` and `parent_run_id` for this `Agent.run` call. + +Reads the contextvar set by any enclosing `StepPersistence.wrap_run` before the local run overwrites it, so a delegate's `parent_run_id` ends up pointing at its orchestrator's `run_id`. + +A separate `ContextVar` is needed because pydantic\_ai's own cross-run signals (`RUN_ID_BAGGAGE_KEY` via OTel baggage, `RunContext.run_id`, and `_CURRENT_RUN_CONTEXT`) are single-slot: the inner `Instrumentation.wrap_run` overwrites them before any nested capability sees the parent. The harness-local contextvar lets us snapshot the parent here, _before_ the local `wrap_run` rebinds it. + +###### Returns + +`AbstractCapability`\[`AgentDepsT`\] + +##### wrap\_run + +`@async` + +```python +def wrap_run( + ctx: RunContext[AgentDepsT], + *, + handler: WrapRunHandler, +) -> AgentRunResult[Any] +``` + +Push this run's id onto the contextvar so nested delegates can read it. + +###### Returns + +[`AgentRunResult`](/docs/ai/api/pydantic-ai/run/#pydantic_ai.run.AgentRunResult)\[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] + +##### before\_run + +`@async` + +```python +def before_run(ctx: RunContext[AgentDepsT]) -> None +``` + +Register run lineage and emit `run_started`. + +When the caller pinned an explicit `run_id`, reject reuse -- the tool-effect ledger keys on `(run_id, tool_call_id)` and providers reuse deterministic tool-call ids, so a second `Agent.run` with the same explicit `run_id` would silently collide. The auto-derived cases cannot trigger this check because each call materialises a fresh id in `for_run`. + +###### Returns + +[`None`](https://docs.python.org/3/library/constants.html#None) + +##### after\_run + +`@async` + +```python +def after_run( + ctx: RunContext[AgentDepsT], + *, + result: AgentRunResult[Any], +) -> AgentRunResult[Any] +``` + +Emit `run_completed`, saving a final snapshot only as a fallback. + +The terminal `CallToolsNode` already saved the final provider-valid snapshot via `after_node_run`, carrying the correct `step_index`. By `after_run`, `ctx.run_step` is reset to 0, so re-saving here would both duplicate the tail and stamp a misleading `step_index`. We only save when the run produced no snapshot at all (no provider-valid node boundary was reached), as a last-resort capture of the final state. + +###### Returns + +[`AgentRunResult`](/docs/ai/api/pydantic-ai/run/#pydantic_ai.run.AgentRunResult)\[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] + +##### on\_run\_error + +`@async` + +```python +def on_run_error( + ctx: RunContext[AgentDepsT], + *, + error: BaseException, +) -> AgentRunResult[Any] +``` + +Emit `run_failed` so a killed run leaves a visible event trail. + +###### Returns + +[`AgentRunResult`](/docs/ai/api/pydantic-ai/run/#pydantic_ai.run.AgentRunResult)\[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] + +##### after\_node\_run + +`@async` + +```python +def after_node_run( + ctx: RunContext[AgentDepsT], + *, + node: AgentNode[AgentDepsT], + result: NodeResult[AgentDepsT], +) -> NodeResult[AgentDepsT] +``` + +Save a mid-run continuable snapshot after `CallToolsNode` succeeds. + +At that boundary every tool call from the preceding `ModelRequestNode` has a matching tool return, so the history is provider-valid. Snapshots are filtered through `is_provider_valid` defensively in case a custom node reshapes history. -See the [Code Mode README](https://github.com/pydantic/pydantic-ai-harness/blob/main/pydantic_ai_harness/code_mode/README.md) in the harness repo for selective tool sandboxing, metadata-based selection, return value handling, REPL state, observability, sandbox restrictions, the full API, and agent spec usage. +###### Returns + +`NodeResult`\[`AgentDepsT`\] --- -# [Overview](https://pydantic.dev/docs/ai/harness/overview/) +# [Subagents](https://pydantic.dev/docs/ai/harness/subagents/) -# Overview +> Let an agent delegate self-contained tasks to named child agents via a single delegate_task tool, with per-delegate budgets and failure handling. -[**Pydantic AI Harness**](https://github.com/pydantic/pydantic-ai-harness) is the official [capability](/docs/ai/core-concepts/capabilities) library for Pydantic AI -- the batteries for your agent. +# Subagents -Terminal +`SubAgents` lets an agent delegate self-contained tasks to named child agents. It takes a sequence of `SubAgent` entries and exposes a single `delegate_task(agent_name, task)` tool. Each delegation runs the chosen sub-agent in its own run -- with its own message history, so it never sees the parent conversation -- and returns its output to the parent. -```bash -uv add pydantic-ai-harness +[Source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/subagents/) + +> The API may change between releases. Where practical, breaking changes ship with a deprecation warning. + +## The problem + +A single agent that does everything accumulates a large tool set and a long context. Splitting the work across specialized sub-agents keeps each context focused, but wiring up delegation by hand means writing a tool per agent, forwarding deps, threading usage limits, and telling the model what it can delegate to. + +## The solution + +`SubAgents` takes a sequence of `SubAgent` entries and exposes a single `delegate_task(agent_name, task)` tool. Each delegation runs the chosen sub-agent in its own run -- with its own message history, so it never sees the parent conversation -- and returns its output to the parent. The available sub-agents are listed in the system prompt as a static instruction, so the listing stays in the cached prefix. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.subagents import SubAgent, SubAgents + +researcher = Agent('anthropic:claude-sonnet-4-6', name='researcher', description='Researches a topic and reports findings') +writer = Agent('anthropic:claude-sonnet-4-6', name='writer', description='Turns notes into polished prose') + +orchestrator = Agent( + 'anthropic:claude-opus-4-7', + capabilities=[SubAgents(agents=[SubAgent(researcher), SubAgent(writer)])], +) + +result = orchestrator.run_sync('Research the history of TLS and write a one-paragraph summary.') +print(result.output) ``` -## What goes where? +A delegate's name -- how the parent model refers to it, and how it is listed in the prompt -- is the agent's own `name`, or a `SubAgent(name=...)` override. Two delegates resolving to the same name is an error, and an agent with no name and no override is rejected. -Pydantic AI core ships the agent loop, model providers, the capabilities/hooks abstraction, and two kinds of capabilities: +## The tool -- **Capabilities that require model or framework support** -- anything backed by provider native tools (like [image generation](/docs/ai/core-concepts/capabilities#provider-adaptive-tools)), provider-specific APIs (like compaction via the OpenAI or Anthropic APIs), or deep agent graph integration. These go hand-in-hand with model class code and need to ship together. -- **Capabilities that are fundamental to the agent experience** -- things nearly every agent benefits from, like [web search](/docs/ai/core-concepts/capabilities#provider-adaptive-tools), [tool search](/docs/ai/tools-toolsets/tools-advanced#tool-search), and [thinking](/docs/ai/core-concepts/capabilities#thinking). These feel like qualities of the agent itself, not accessories. +Tool -**Pydantic AI Harness** is where everything else lives: standalone capabilities that make specific categories of agents powerful, or that are still finding their final shape. Context management, memory, guardrails, file system access, code execution, multi-agent orchestration -- these are the building blocks you pick and choose based on what your agent needs to do. +Purpose -The harness is also where new capabilities _start_. It ships as a separate package so capabilities can iterate faster without the strict backward-compatibility requirements of core. As a capability stabilizes and proves itself broadly essential, it can graduate into core -- [code mode](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/code_mode) is an early candidate. +`delegate_task(agent_name, task)` -Many capabilities benefit from a "fall up" pattern: they typically start as a local implementation that works with every model, then gain provider-native support that uses the provider's built-in API when available -- auto-switching between the two. This is how [web search](/docs/ai/core-concepts/capabilities#provider-adaptive-tools), [web fetch](/docs/ai/core-concepts/capabilities#provider-adaptive-tools), and [image generation](/docs/ai/core-concepts/capabilities#provider-adaptive-tools) already work in core, and the same approach is coming for skills, code mode, and context compaction. +Run the named sub-agent on a self-contained task and return its output. -## What's in it? +- The sub-agent runs with its own message history, so `task` must be self-contained. +- An unknown `agent_name` raises `ModelRetry`, so the model can correct itself. +- The result returned to the parent is `str(result.output)`. -See the [capability matrix](https://github.com/pydantic/pydantic-ai-harness#capability-matrix) on the harness README for the full list of what's available and what's coming, along with community alternatives. +## Deps, usage, tools, and capabilities -## Contributing capabilities +- **Deps are forwarded.** The parent run's `deps` are passed to each sub-agent, so sub-agents share the parent's `AgentDepsT` (enforced by the type signature -- every sub-agent is an `AbstractAgent[AgentDepsT, Any]`). +- **Usage is shared by default.** The parent's `usage` is passed to each sub-agent run, so token usage aggregates and a parent `usage_limits` applies across the whole agent tree. Set `forward_usage=False` to give each sub-agent run its own accounting. +- **Tools can be inherited.** With `inherit_tools=True`, the parent agent's own tools (registered directly or via `toolsets`) are added to each sub-agent run, on top of the sub-agent's own. Tools contributed by the parent's capabilities are not inherited: they are bound to capability instances registered in the parent run, and would arrive without the hooks and instructions they depend on. Use `shared_capabilities` to give sub-agents a capability. This also excludes the delegate tool itself, so a sub-agent can't recurse into further delegation. Off by default. +- **Capabilities can be shared.** `shared_capabilities` are applied to every sub-agent run -- e.g. give all sub-agents a common guardrail, memory, or planning capability without rebuilding each `Agent`. +- **Sub-agent events can be streamed.** Pass an `event_stream_handler` and it's forwarded to each sub-agent run, so the sub-agent's model-streaming and tool events surface to the caller (the handler receives the sub-agent's own `RunContext`). -Capability contributions should go to the [harness repo](https://github.com/pydantic/pydantic-ai-harness), not to pydantic-ai. The capabilities abstraction gives contributions clear boundaries, which makes them easier to review. See [Contributing](https://github.com/pydantic/pydantic-ai-harness#contributing) for details. +## Per-delegate run controls -## Build your own +Each `SubAgent` carries its own budgets, so one delegate's controls do not touch the others. A `SubAgent` with no controls set runs with the `SubAgents` defaults. + +```python +from pydantic_ai import Agent +from pydantic_ai.usage import UsageLimits +from pydantic_ai_harness.subagents import SubAgent, SubAgents + +reproducer = Agent('anthropic:claude-sonnet-4-6', instructions='Reproduce the reported bug from a minimal script.') +librarian = Agent('anthropic:claude-sonnet-4-6', instructions='Find relevant docs, issues, and prior art.') + +orchestrator = Agent( + 'anthropic:claude-opus-4-7', + capabilities=[ + SubAgents( + agents=[ + SubAgent(reproducer, usage_limits=UsageLimits(request_limit=35), timeout_seconds=600, max_calls=1), + SubAgent(librarian, usage_limits=UsageLimits(request_limit=18), timeout_seconds=300, max_calls=2), + ] + ) + ], +) +``` + +Field + +Effect + +`usage_limits` + +A request/token budget for one delegation. The child runs with its own usage accounting, so the budget counts only that child's requests and tokens (not the parent's or siblings'), even when `forward_usage=True`. The tradeoff: that child's tokens no longer aggregate into the parent's `usage`. Reaching the budget is a soft outcome (see below), not a run-stopping `UsageLimitExceeded`. + +`timeout_seconds` + +A wall-clock budget for one delegation. When the child exceeds it, its run is cancelled and the parent gets a soft steering message instead of hanging on the child. The cancelled child's `event_stream_handler` (if any) stops receiving events without a terminal event. + +`max_calls` + +The maximum number of delegations to this sub-agent per parent run. Once reached, further delegations return a soft budget-exhausted message without running the child. Counts are scoped to one `Agent.run` (a `run_id`) and cleared when it ends, so each parent run and each level of a nested tree budgets independently. + +`on_failure` + +A steering message returned to the parent for any soft degradation of this delegate, in place of the built-in default. Setting it also makes child failures soft (see below). -You can also publish capabilities as standalone packages. See [Building custom capabilities](/docs/ai/core-concepts/capabilities#building-custom-capabilities) for the API and [Publishing capability packages](/docs/ai/guides/extensibility#publishing-capability-packages) for packaging guidance. +`contain_errors` -## Links +Whether an unexpected crash in this delegate is caught and returned to the parent as a bounded `ModelRetry` instead of aborting the parent run (see below). Unset inherits the `SubAgents(contain_errors=...)` default (off). -- [GitHub](https://github.com/pydantic/pydantic-ai-harness) -- [PyPI](https://pypi.org/project/pydantic-ai-harness/) -- [Capability matrix](https://github.com/pydantic/pydantic-ai-harness#capability-matrix) -- [Version policy](https://github.com/pydantic/pydantic-ai-harness#version-policy) -- [Contributing](https://github.com/pydantic/pydantic-ai-harness#contributing) +## Failure handling +A _soft outcome_ returns a steering message to the parent as a normal tool result, so its model reads the message and decides what to do next (rather than immediately re-delegating, which a `ModelRetry` invites). A timeout, a reached `usage_limits` budget, and an exhausted `max_calls` budget are always soft. When `on_failure` is set, the message it carries replaces the built-in default for these outcomes. + +A sub-agent run that fails with a _soft model error_ (`ModelRetry`, `UnexpectedModelBehavior`, e.g. it exhausted its own retries) is, by default, converted into a `ModelRetry` for the parent -- so the parent's model sees `Sub-agent '' failed: ...` and can react by re-delegating. The delegate tool defaults to `tool_retries=2`, so the parent aborts only after that many consecutive delegate failures; the counter resets after any successful delegation. Raise `tool_retries` to tolerate a flakier sub-agent, or set `None` to inherit the parent agent's default tool retries. Set `on_failure` for a delegate to make its failures soft instead: the child error returns the `on_failure` message as a normal tool result. + +Hard errors propagate to stop the whole run. A `UsageLimitExceeded` from a child that has _no_ per-delegate `usage_limits` (so it shares the parent's accounting) means the whole tree is out of budget and propagates; a child reaching its _own_ `usage_limits` is soft, as above. + +An _unexpected crash_ -- any other exception the child raises, such as a provider `ModelAPIError`/`FallbackExceptionGroup` or a plain `ValueError` from a bad tool argument -- propagates by default and aborts the parent run. Set `contain_errors=True` (per delegate, or as the `SubAgents` default) to catch it and return it to the parent as a bounded `ModelRetry` instead, so one delegate crash cannot kill the whole run. Containment stays loud: the exception rides the retry message (`Sub-agent '' crashed: ...`), it is logged via the standard `logging` module, and `tool_retries` still bounds consecutive crashes into an abort. This is orthogonal to `on_failure` -- a contained crash always raises the loud retry, never the soft `on_failure` return, so a genuine bug is never masked as success. Cancellation, a shared `UsageLimitExceeded`, pydantic-ai control-flow signals (`CallDeferred`, `ApprovalRequired`, the `Skip*` signals), and `UserError` always propagate regardless of `contain_errors`. + +## Discovery + +The sub-agents are listed in the system prompt via `get_instructions`, using each agent's `description` (or a `SubAgent(description=...)` override). A sub-agent with no description is listed by name alone. + +## Loading sub-agents from disk + +A repo's markdown agent definitions become delegates without writing any `Agent` code. By default every `*.md` file under the conventional folders is loaded as a sub-agent, alongside the explicitly-passed `agents`. + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.subagents import SubAgents + +orchestrator = Agent( + 'anthropic:claude-opus-4-7', + capabilities=[SubAgents(inherit_tools=True)], # auto-loads ./.agents/agents/ and ~/.agents/agents/ +) +``` + +`agent_folders` controls where definitions come from. It defaults to `'agents'`, the conventional layout: + +- A folder-name `str` (the default `'agents'`): for the project root (cwd) then the home root, load from `/.agents//`, falling back to `/.claude//` when `/.agents/` is absent. +- A sequence of paths loads from exactly those folders, in order. +- `None` disables disk loading, exposing only the explicitly-passed `agents`. + +### Definition format + +A definition is a markdown file with optional frontmatter: + +```markdown +--- +name: researcher +description: Researches a topic and reports findings +tools: Read, Grep --- +You research topics. Report your findings, each with a source. +``` + +- `name` is the delegate name (how the parent refers to it and how it is listed). It falls back to the filename stem when absent. +- `description` drives the prompt listing. +- The markdown body becomes the agent's instructions. +- `tools` (or `allowed-tools`) is a comma-separated string or a YAML block list. See "Tools" below. +- `model` and `color` are ignored: the model is inherited from the parent (see below), and `color` has no pyai equivalent. + +Frontmatter is read by a small, dependency-free parser limited to those keys (`pyyaml` is not a harness dependency). Full YAML frontmatter is not supported. + +### Models and effort + +Disk agents inherit the parent run's model by default. Per agent, the caller can override the model and set a thinking/effort level via `agent_overrides`, keyed by the agent's name: + +```python +from pydantic_ai_harness.subagents import AgentOverride, SubAgents + +SubAgents( + agent_folders='agents', + agent_overrides={'researcher': AgentOverride(model='anthropic:claude-sonnet-4-6', effort='high')}, +) +``` + +Every agent the capability builds runs at a minimum thinking-effort floor. `MINIMUM_EFFORT_FLOOR` and the `clamp_effort(level, floor=...)` helper are exported so an orchestrator can apply the same floor to its own agents (that orchestrator-side application is the caller's responsibility). `clamp_effort` maps `None`/`False` to the floor, leaves `True` (provider-default effort) unchanged, and raises a concrete level below the floor up to it. Effort is applied through pyai's `ModelSettings.thinking`. + +### Tools + +A disk agent gets no tools by default (`inherit_tools` is `False`); set `inherit_tools=True` to expose the parent's tools to it through the `inherit_tools` mechanism, in which case its `tools` frontmatter is ignored. To map the frontmatter tool names to specific toolsets instead, pass a `tool_resolver`: it receives each tool name (so it can honor entries like `Bash(git:*)`) and returns the toolsets that provide it, or `None` for an unknown name, which is skipped with a warning. + +```python +from pydantic_ai_harness.subagents import SubAgents + +def resolve(tool_name: str): + return TOOLSETS.get(tool_name) # -> Sequence[AgentToolset[object]] | None + +SubAgents(agent_folders='agents', tool_resolver=resolve) +``` + +### Precedence + +When the same name appears in more than one source, the higher-precedence one wins and the others are skipped with a warning: explicitly-passed `agents` first, then the project folder, then the home folder (and, for an explicit path sequence, earlier paths before later ones). A duplicate name within the explicitly-passed `agents` list is still an error. + +## Configuration + +```python +SubAgents( + agents=(), # Sequence[SubAgent[AgentDepsT]] -- each pairs an agent with its run controls + agent_folders='agents',# folder-name str (convention) | Sequence[Path] | None (disable) + agent_overrides={}, # Mapping[str, AgentOverride] -- per-disk-agent model/effort override + tool_resolver=None, # Callable[[str], Sequence[AgentToolset[object]] | None] -- disk-agent tool mapping + forward_usage=True, # share the parent's usage with sub-agent runs + inherit_tools=False, # expose the parent's own tools to sub-agents (capability tools excluded) + shared_capabilities=(),# capabilities applied to every sub-agent run + event_stream_handler=None, # forwarded to each sub-agent run to stream its events + tool_name='delegate_task', + tool_retries=2, # extra delegate-tool attempts after a sub-agent error before aborting (None inherits the agent default) + contain_errors=False, # default for SubAgent.contain_errors: contain an unexpected crash as a bounded retry +) +``` + +```python +SubAgent( + agent, # AbstractAgent[AgentDepsT, Any] -- the child agent to run + name=None, # delegate name; defaults to the agent's own `name` + description=None, # prompt-listing description; defaults to the agent's own `description` + usage_limits=None, # per-delegation request/token budget (isolated accounting) + timeout_seconds=None, # per-delegation wall-clock budget + max_calls=None, # max delegations to this sub-agent per parent run + on_failure=None, # steering message for soft degradations of this delegate + contain_errors=None, # contain an unexpected crash as a bounded retry; None inherits the SubAgents default +) +``` + +`SubAgents` is not serializable via the [agent spec](/docs/ai/core-concepts/agent-spec/) (it holds live `Agent` instances), so `get_serialization_name()` returns `None`. + +## Notes + +- Sub-agents can themselves have `SubAgents`, forming a tree. Share `usage` (the default) and set a `usage_limits` on the top-level run to bound the whole tree. +- Delegations the model issues in parallel run as independent sub-agent runs. + +## Further reading + +- [Pydantic AI capabilities](/docs/ai/core-concepts/capabilities/) +- [Multi-agent applications](/docs/ai/guides/multi-agent-applications/) + +## API reference + +### SubAgents + +**Bases:** `AbstractCapability[AgentDepsT]` + +Let an agent delegate self-contained tasks to named sub-agents. + +Exposes a single `delegate_task(agent_name, task)` tool. Each delegation runs the chosen sub-agent in a fresh, isolated run (it never sees the parent conversation), and the available sub-agents are listed in the system prompt as a static, cache-stable instruction. + +Sub-agents are passed as a sequence of `SubAgent` entries, each pairing an agent with its per-delegate run controls (a `usage_limits` budget, a wall-clock `timeout_seconds`, a per-run `max_calls` budget, an `on_failure` steering message, and optional `name`/`description` overrides). A delegate's name is its `SubAgent.name`, or the agent's own `name` when unset; two explicitly-passed delegates resolving to the same name is an error. + +Sub-agents are also loaded from disk by default: each markdown agent definition under `./.agents/agents/` and `~/.agents/agents/` (or the `.claude/` equivalent) becomes a delegate, built with the parent's model. Disk delegates get no tools by default (`inherit_tools` is `False`); set `inherit_tools=True` to expose the parent's tools, or pass a `tool_resolver` to map their frontmatter tool names. Disk delegates coexist with explicitly-passed ones; explicitly-passed agents take precedence, then the project folder, then the home folder. A disk delegate whose name is already taken is skipped with a warning. Configure or disable this with `agent_folders`; see also `agent_overrides` and `tool_resolver`. + +The parent's `deps` are forwarded to each sub-agent (sub-agents therefore share the parent's `AgentDepsT`), and by default the parent's `usage` is shared so usage limits apply across the whole agent tree. Optionally, the parent's tools can be inherited (`inherit_tools`), extra capabilities can be applied to every sub-agent run (`shared_capabilities`), and sub-agent events can be streamed to a handler (`event_stream_handler`). + +```python +from pydantic_ai import Agent +from pydantic_ai_harness.subagents import SubAgent, SubAgents + +researcher = Agent('anthropic:claude-sonnet-4-6', name='researcher', description='Researches topics') +writer = Agent('anthropic:claude-sonnet-4-6', name='writer', description='Writes prose') + +orchestrator = Agent( + 'anthropic:claude-opus-4-7', + capabilities=[SubAgents(agents=[SubAgent(researcher), SubAgent(writer)])], +) +``` + +#### Attributes + +##### agents + +The sub-agents to expose, each a `SubAgent` pairing an agent with its per-delegate run controls. See `SubAgent`. These take precedence over any disk-loaded agents of the same name. + +**Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[`SubAgent`\[`AgentDepsT`\]\] **Default:** `()` + +##### agent\_folders + +Where to load markdown agent definitions from, in addition to `agents`. Defaults to the conventional layout, so constructing the capability auto-loads a repo's agent files with no extra configuration. + +- a folder-name `str` (the default `'agents'` is the conventional layout): for the project root (cwd) then the home root, load from `/.agents//`, falling back to `/.claude//` when `/.agents/` is absent. +- a sequence of paths: load from exactly those folders, in order. +- `None`: disable disk loading entirely (only `agents` are exposed). + +Missing folders are skipped. Within a folder every `*.md` file is a candidate. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[`Path`\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `'agents'` + +##### agent\_overrides + +Per-disk-agent overrides keyed by the agent's name. An entry can set the agent's `model` (otherwise the parent's model is inherited) and its `effort` (otherwise the minimum floor). Has no effect on explicitly-passed `agents`. + +**Type:** [`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str), `AgentOverride`\] **Default:** `field(default_factory=(dict[str, AgentOverride]))` + +##### tool\_resolver + +Optional override for how a disk agent gets its tools. When set, each tool name in a definition's `tools`/`allowed-tools` frontmatter is passed to this resolver and the returned toolsets are attached to that agent; an unknown name (resolver returns `None`) is skipped with a warning. When unset, the frontmatter tool list is ignored and disk agents inherit the parent's tools via `inherit_tools` (set `inherit_tools=True` to expose them). + +**Type:** `ToolResolver` | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### forward\_usage + +If `True`, the parent run's `usage` is shared with each sub-agent run, so token usage aggregates and usage limits apply across the whole agent tree. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `True` + +##### inherit\_tools + +If `True`, the parent agent's tools are exposed to each sub-agent run (the delegate tool itself is filtered out, so sub-agents can't recurse into further delegation). Off by default to avoid silently widening sub-agent access. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `False` + +##### shared\_capabilities + +Capabilities applied to every sub-agent run, in addition to whatever each sub-agent already has. + +**Type:** [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)\[[`AgentCapability`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.AgentCapability)\[`AgentDepsT`\]\] **Default:** `()` + +##### event\_stream\_handler + +If set, this handler is passed to each sub-agent run, so the sub-agent's model-streaming and tool events surface to the caller. The handler receives the sub-agent's own `RunContext` and event stream. + +**Type:** `EventStreamHandler`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### tool\_name + +Name of the delegate tool exposed to the model. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) **Default:** `'delegate_task'` + +##### tool\_retries + +Retries for the delegate tool -- how many extra attempts it gets after a sub-agent error before the parent run aborts. A sub-agent failure (e.g. it exhausts its own output retries) surfaces to the parent as a tool retry it can react to by re-delegating with a corrected task. The retry counter resets after any successful delegation, so this bounds consecutive failures, not total ones. Defaults to `2` (pydantic-ai's per-tool default is `1`) so a repeated flaky sub-agent does not abort the parent run on its first repeat; set `None` to inherit the parent agent's default tool retries instead. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `2` + +##### contain\_errors + +Default for `SubAgent.contain_errors`: whether an unexpected sub-agent crash is caught and returned to the parent as a bounded `ModelRetry` instead of aborting the parent run. Off by default, so a crash propagates. Any `SubAgent` can override this per delegate. See `SubAgent.contain_errors` for the containment contract and what always propagates regardless. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) **Default:** `False` + +#### Methods + +##### wrap\_run + +`@async` + +```python +def wrap_run( + ctx: RunContext[AgentDepsT], + *, + handler: WrapRunHandler, +) -> AgentRunResult[Any] +``` + +Run the parent agent, then drop this run's delegation counts so they don't accumulate. + +###### Returns + +[`AgentRunResult`](/docs/ai/api/pydantic-ai/run/#pydantic_ai.run.AgentRunResult)\[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] + +##### get\_instructions + +```python +def get_instructions() -> AgentInstructions[AgentDepsT] | None +``` + +Static, cache-stable listing of the available sub-agents. + +###### Returns + +`AgentInstructions`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### get\_toolset + +```python +def get_toolset() -> AgentToolset[AgentDepsT] | None +``` + +Toolset providing the delegate tool, or `None` when no sub-agents are configured. + +###### Returns + +`AgentToolset`\[`AgentDepsT`\] | [`None`](https://docs.python.org/3/library/constants.html#None) + +##### get\_serialization\_name + +`@classmethod` + +```python +def get_serialization_name(cls) -> str | None +``` + +Not spec-serializable -- the capability holds live `Agent` instances. + +###### Returns + +[`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) + +### SubAgent + +**Bases:** `Generic[AgentDepsT]` + +One delegate: a child agent plus its per-delegate run controls. + +Pass a sequence of these as `SubAgents(agents=[...])`. The delegate's name -- how the parent model refers to it, and how it is listed in the system prompt -- is `name` when set, otherwise the agent's own `name`. An agent with neither is rejected by `SubAgents`. + +Every control below is optional; an unset field leaves the corresponding behaviour at the `SubAgents` default. + +#### Attributes + +##### agent + +The agent that runs when this delegate is invoked. + +**Type:** `AbstractAgent`\[`AgentDepsT`, [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)\] + +##### name + +Name the parent model uses to delegate to this agent. Defaults to the agent's own `name` when unset. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### description + +Description for the system-prompt listing. Defaults to the agent's own `description` when unset; a delegate with neither is listed by name alone. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### usage\_limits + +Request/token budget for one delegation. When set, the child runs with its own usage accounting so the budget counts only the child's own requests and tokens (not the parent's or siblings'), even when `forward_usage=True`. The tradeoff: that child's tokens no longer aggregate into the parent's `usage`. Hitting this budget is a soft outcome (steering message), not a run-stopping `UsageLimitExceeded`. + +**Type:** [`UsageLimits`](/docs/ai/api/pydantic-ai/usage/#pydantic_ai.usage.UsageLimits) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` -# [](https://pydantic.dev/docs/ai/) +##### timeout\_seconds + +Wall-clock budget for one delegation. When the child exceeds it, the run is cancelled and the parent gets a soft steering message instead of hanging on the child. + +**Type:** [`float`](https://docs.python.org/3/library/functions.html#float) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### max\_calls + +Maximum number of delegations to this sub-agent per parent run. Once reached, further delegations return a soft budget-exhausted message without running the child. + +**Type:** [`int`](https://docs.python.org/3/library/functions.html#int) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### on\_failure + +Steering message returned to the parent for any soft degradation of this delegate (timeout, child failure, usage budget reached, call budget exhausted), in place of the built-in default. Setting it also makes child failures soft: a child error returns this message as a normal tool result instead of raising a parent `ModelRetry`. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### contain\_errors + +Whether an unexpected sub-agent crash is contained instead of aborting the parent run. When `True`, an exception the child raises that is not an expected soft degradation (a provider `ModelAPIError`/`FallbackExceptionGroup`, a plain `ValueError` from a bad tool argument, etc.) is caught and returned to the parent as a bounded `ModelRetry`, so one delegate crash cannot kill the whole run. It stays loud: the exception rides the retry message and is logged, and `tool_retries` still bounds consecutive crashes into an abort. Cancellation, a shared usage-limit, pydantic-ai control-flow signals, and `UserError` always propagate regardless. Unset inherits `SubAgents.contain_errors` (default off). Orthogonal to `on_failure`, which only sets the message for expected soft degradations; a contained crash always raises the loud `ModelRetry`. + +**Type:** [`bool`](https://docs.python.org/3/library/functions.html#bool) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` +##### resolved\_name +The delegate's name: `name` if set, else the agent's own `name`. + +**Type:** [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) + +### AgentOverride + +Per-agent override for a disk-loaded sub-agent, keyed by the agent's name. + +Both fields are optional. An unset `model` inherits the parent run's model; an unset `effort` runs at the capability's minimum effort floor (see `clamp_effort`). + +#### Attributes + +##### model + +Model to run this disk agent with, in place of inheriting the parent's. + +**Type:** `Model` | `KnownModelName` | [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` + +##### effort + +Thinking/reasoning level for this disk agent. Raised to at least the floor. + +**Type:** `ThinkingLevel` | [`None`](https://docs.python.org/3/library/constants.html#None) **Default:** `None` --- @@ -63185,6 +70920,7 @@ Then running `clai` will start an interactive session where you can chat with th - `/markdown`: Show the last response in markdown format - `/multiline`: Toggle multiline input mode (use Ctrl+D to submit) - `/cp`: Copy the last response to clipboard +- `/usage`: Show cumulative token usage for the session (turns, input, output, requests, tool calls); add `--json` for a single-line JSON object ### CLI Options @@ -63674,6 +71410,8 @@ When using DBOS with Pydantic AI agents, there are a few important consideration Each agent instance must have a unique `name` so DBOS can correctly resume workflows after a failure or restart. +Each [`MCPToolset`](/docs/ai/api/pydantic-ai/mcp/#pydantic_ai.mcp.MCPToolset) must have a unique [`id`](/docs/ai/api/pydantic-ai/toolsets/#pydantic_ai.toolsets.AbstractToolset.id), as DBOS derives its step names and per-run tool-defs cache key from it. This field is normally optional, but is required when using DBOS. It should not be changed once the durable agent has been deployed to production, as this would break active workflows. + Tools and event stream handlers are not automatically wrapped by DBOS. You can decide how to integrate them: - Decorate with `@DBOS.step` if the function involves non-determinism or I/O. @@ -63698,6 +71436,10 @@ When using `DBOSAgent`, tools are executed in parallel by default to minimize la If you prefer strict ordering, you can configure the agent to run tools sequentially by setting [`parallel_execution_mode='sequential'`](/docs/ai/api/pydantic-ai/durable_exec/#pydantic_ai.durable_exec.dbos.DBOSAgent) when initializing the `DBOSAgent`. +### Toolsets at Runtime + +Additional toolsets can be passed per run via [`DBOSAgent.run(toolsets=...)`](/docs/ai/api/pydantic-ai/durable_exec/#pydantic_ai.durable_exec.dbos.DBOSAgent.run). Non-executing toolsets like [`ExternalToolset`](/docs/ai/api/pydantic-ai/toolsets/#pydantic_ai.toolsets.ExternalToolset), and [`FunctionToolset`](/docs/ai/api/pydantic-ai/toolsets/#pydantic_ai.toolsets.FunctionToolset)s whose tools DBOS runs inline, are supported. [`MCPToolset`](/docs/ai/api/pydantic-ai/mcp/#pydantic_ai.mcp.MCPToolset)s and dynamic toolsets must be set when constructing the agent so their steps are registered before the workflow runs; passing them at runtime raises a `UserError`. + ## Step Configuration You can customize DBOS step behavior, such as retries, by passing [`StepConfig`](/docs/ai/api/pydantic-ai/durable_exec/#pydantic_ai.durable_exec.dbos.StepConfig) objects to the `DBOSAgent` constructor: @@ -63976,8 +71718,8 @@ The original agent, model, and MCP server can still be used as normal outside th Here is a simple but complete example of wrapping an agent for durable execution. All it requires is to install Pydantic AI with Prefect: -- [pip](#tab-panel-177) -- [uv](#tab-panel-178) +- [pip](#tab-panel-181) +- [uv](#tab-panel-182) Terminal @@ -63993,8 +71735,8 @@ uv add pydantic-ai[prefect] Or if you're using the slim package, you can install it with the `prefect` optional group: -- [pip](#tab-panel-179) -- [uv](#tab-panel-180) +- [pip](#tab-panel-183) +- [uv](#tab-panel-184) Terminal @@ -64094,6 +71836,10 @@ For real-time streaming behavior inside Prefect flows, you can set an [`event_st The event stream handler function will receive the agent [run context](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext) and an async iterable of events from the model's streaming response and the agent's execution of tools. For examples, see the [streaming docs](/docs/ai/core-concepts/agent#streaming-all-events). +### Toolsets at Runtime + +Additional toolsets can be passed per run via [`PrefectAgent.run(toolsets=...)`](/docs/ai/api/pydantic-ai/durable_exec/#pydantic_ai.durable_exec.prefect.PrefectAgent.run), but only non-executing toolsets like [`ExternalToolset`](/docs/ai/api/pydantic-ai/toolsets/#pydantic_ai.toolsets.ExternalToolset), whose tools are executed outside the agent run, are supported. Executing toolsets ([`FunctionToolset`](/docs/ai/api/pydantic-ai/toolsets/#pydantic_ai.toolsets.FunctionToolset) and [`MCPToolset`](/docs/ai/api/pydantic-ai/mcp/#pydantic_ai.mcp.MCPToolset)) and dynamic toolsets must be set when constructing the agent so their tasks are registered before the flow runs; passing them at runtime raises a `UserError`. + ## Task Configuration You can customize Prefect task behavior, such as retries and timeouts, by passing [`TaskConfig`](/docs/ai/api/pydantic-ai/durable_exec/#pydantic_ai.durable_exec.prefect.TaskConfig) objects to the `PrefectAgent` constructor: @@ -64292,8 +72038,8 @@ Any Pydantic AI agent can be made durable by wrapping it with `RestateAgent` fro Install the Restate SDK: -- [pip](#tab-panel-181) -- [uv](#tab-panel-182) +- [pip](#tab-panel-185) +- [uv](#tab-panel-186) Terminal @@ -64438,7 +72184,41 @@ Any agent can be wrapped in a [`TemporalAgent`](/docs/ai/api/pydantic-ai/durable At the time of wrapping, the agent's [model](/docs/ai/models/overview) and [toolsets](/docs/ai/tools-toolsets/toolsets) (including function tools registered on the agent and MCP servers) are frozen, activities are dynamically created for each, and the original model and toolsets are wrapped to call on the worker to execute the corresponding activities instead of directly performing the actions inside the workflow. The original agent can still be used as normal outside the Temporal workflow, but any changes to its model or toolsets after wrapping will not be reflected in the durable agent. -Here is a simple but complete example of wrapping an agent for durable execution, creating a Temporal workflow with durable execution logic, connecting to a Temporal server, and running the workflow from non-durable code. All it requires is a Temporal server to be [running locally](https://github.com/temporalio/temporal#download-and-start-temporal-server-locally): +Here is a simple but complete example of wrapping an agent for durable execution, creating a Temporal workflow with durable execution logic, connecting to a Temporal server, and running the workflow from non-durable code. All it requires is to install Pydantic AI with the `temporal` optional group: + +- [pip](#tab-panel-177) +- [uv](#tab-panel-178) + +Terminal + +```bash +pip install pydantic-ai[temporal] +``` + +Terminal + +```bash +uv add pydantic-ai[temporal] +``` + +Or if you're using the slim package, you can install it with the `temporal` optional group: + +- [pip](#tab-panel-179) +- [uv](#tab-panel-180) + +Terminal + +```bash +pip install pydantic-ai-slim[temporal] +``` + +Terminal + +```bash +uv add pydantic-ai-slim[temporal] +``` + +You'll also need a Temporal server to be [running locally](https://github.com/temporalio/temporal#download-and-start-temporal-server-locally): Terminal @@ -64466,7 +72246,7 @@ from pydantic_ai.durable_exec.temporal import ( agent = Agent( 'openai:gpt-5.2', instructions="You're an expert in geography.", - name='geography', # (10) + name='geography', # (9) ) temporal_agent = TemporalAgent(agent) # (1) @@ -64643,6 +72423,10 @@ class MultiModelWorkflow: return result.output ``` +### Toolsets at Runtime + +Additional toolsets can be passed per run via [`TemporalAgent.run(toolsets=...)`](/docs/ai/api/pydantic-ai/durable_exec/#pydantic_ai.durable_exec.temporal.TemporalAgent.run), but only non-executing toolsets like [`ExternalToolset`](/docs/ai/api/pydantic-ai/toolsets/#pydantic_ai.toolsets.ExternalToolset), whose tools are executed outside the agent run, are supported. Executing toolsets ([`FunctionToolset`](/docs/ai/api/pydantic-ai/toolsets/#pydantic_ai.toolsets.FunctionToolset) and [`MCPToolset`](/docs/ai/api/pydantic-ai/mcp/#pydantic_ai.mcp.MCPToolset)) and dynamic toolsets must be set when constructing the agent so their activities can be registered with the worker before the workflow runs; passing them at runtime raises a `UserError`. + ## Activity Configuration Temporal activity configuration, like timeouts and retry policies, can be customized by passing [`temporalio.workflow.ActivityConfig`](https://python.temporal.io/temporalio.workflow._activities.ActivityConfig.html) objects to the `TemporalAgent` constructor: @@ -64750,8 +72534,8 @@ A trace is generated for the agent run, and spans are emitted for each model req To use Logfire, you'll need a Logfire [account](https://logfire.pydantic.dev). The Logfire Python SDK is included with `pydantic-ai`: -- [pip](#tab-panel-40) -- [uv](#tab-panel-41) +- [pip](#tab-panel-36) +- [uv](#tab-panel-37) Terminal @@ -64767,8 +72551,8 @@ uv add pydantic-ai Or if you're using the slim package, you can install it with the `logfire` optional group: -- [pip](#tab-panel-42) -- [uv](#tab-panel-43) +- [pip](#tab-panel-38) +- [uv](#tab-panel-39) Terminal @@ -64784,8 +72568,8 @@ uv add "pydantic-ai-slim[logfire]" Then authenticate your local environment with Logfire: -- [pip](#tab-panel-44) -- [uv](#tab-panel-45) +- [pip](#tab-panel-40) +- [uv](#tab-panel-41) Terminal @@ -64801,8 +72585,8 @@ uv run logfire auth And configure a project to send data to: -- [pip](#tab-panel-46) -- [uv](#tab-panel-47) +- [pip](#tab-panel-42) +- [uv](#tab-panel-43) Terminal @@ -64832,7 +72616,7 @@ from pydantic_ai import Agent logfire.configure() # (1) logfire.instrument_pydantic_ai() # (2) -agent = Agent('openai:gpt-5.2', instructions='Be concise, reply with one sentence.') +agent = Agent('openai:gpt-5.2', name='hello_world_agent', instructions='Be concise, reply with one sentence.') # (4) result = agent.run_sync('Where does "hello world" come from?') # (3) print(result.output) """ @@ -64846,6 +72630,8 @@ The first known use of "hello, world" was in a 1974 textbook about the C program Since we've enabled instrumentation, a trace will be generated for each run, with spans emitted for models calls and tool function execution +Passing `name` is optional but recommended: it labels the agent's run span in Logfire. When omitted, the name is inferred from the variable the agent is assigned to and falls back to `'agent'` when it can't be (e.g. agents kept in a list or dict). This matters most when several agents run in one app and you need to tell their traces apart. + _(This example is complete, it can be run "as is")_ Which will display in Logfire thus: @@ -65017,6 +72803,40 @@ The following providers have dedicated documentation on Pydantic AI: ## Advanced usage +### Emitted metrics + +In addition to spans, the instrumentation records the following [OpenTelemetry metrics](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-metrics/), all histograms: + +Metric + +Unit + +Description + +`gen_ai.client.token.usage` + +`{token}` + +Number of tokens used per model or embedding request, split by the `gen_ai.token.type` attribute (`input` or `output`). Defined by the GenAI semantic conventions. + +`operation.cost` + +`{USD}` + +Estimated monetary cost of each model or embedding request, recorded when a price is known for the model. + +`gen_ai.client.operation.time_to_first_chunk` + +`s` + +Time from issuing a streaming request to the first chunk being surfaced to the consumer. Only recorded for streaming requests; the same value is also set as an attribute of the same name on the model request span. + +Each metric point carries the `gen_ai.provider.name` (and legacy `gen_ai.system`), `gen_ai.operation.name`, `gen_ai.request.model`, and `gen_ai.response.model` attributes, so histograms can be broken down by provider and model. + +Stability and histogram buckets + +`gen_ai.client.operation.time_to_first_chunk` is currently at **Development** stability in the [GenAI semantic conventions](https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-metrics.md#metric-gen_aiclientoperationtime_to_first_chunk), so its name or shape may change before stabilization. Both `gen_ai.client.token.usage` and `gen_ai.client.operation.time_to_first_chunk` advise the explicit bucket boundaries specified by the conventions. These are only advisories: you can override them by configuring a [View](https://opentelemetry.io/docs/specs/otel/metrics/sdk/#view) on your `MeterProvider`, and SDKs configured for exponential histogram aggregation (such as Logfire) ignore them entirely. + ### Aggregated usage attribute names By default, model request spans use the standard `gen_ai.usage.input_tokens` and `gen_ai.usage.output_tokens` attributes, while agent run spans use `gen_ai.aggregated_usage.input_tokens`, `gen_ai.aggregated_usage.output_tokens`, and `gen_ai.aggregated_usage.details.*`. @@ -65175,8 +72995,8 @@ The only dependencies are: You can install Pydantic AI with the `ag-ui` extra to ensure you have all the required AG-UI dependencies: -- [pip](#tab-panel-183) -- [uv](#tab-panel-184) +- [pip](#tab-panel-187) +- [uv](#tab-panel-188) Terminal @@ -65192,10 +73012,10 @@ uv add 'pydantic-ai-slim[ag-ui]' To run the examples you'll also need: -- [uvicorn](https://www.uvicorn.org/) or another ASGI compatible server +- [uvicorn](https://uvicorn.dev) or another ASGI compatible server -- [pip](#tab-panel-185) -- [uv](#tab-panel-186) +- [pip](#tab-panel-189) +- [uv](#tab-panel-190) Terminal @@ -65593,7 +73413,15 @@ uvicorn ag_ui_tool_events:app --host 0.0.0.0 --port 9000 ### Trust model -AG-UI's `RunAgentInput.messages` is fully client-controlled. The [`AGUIAdapter`](/docs/ai/api/ui/ag_ui/#pydantic_ai.ui.ag_ui.AGUIAdapter) applies defaults to strip untrusted parts before the agent runs -- see [Trust model for client-submitted messages](/docs/ai/integrations/ui/overview#trust-model-for-client-submitted-messages) in the UI adapter overview. +AG-UI's `RunAgentInput.messages` is fully client-controlled. The [`AGUIAdapter`](/docs/ai/api/ui/ag_ui/#pydantic_ai.ui.ag_ui.AGUIAdapter) applies defaults to strip untrusted parts before the agent runs -- see [Trust model for client-submitted messages](/docs/ai/integrations/ui/overview#trust-model-for-client-submitted-messages) in the UI adapter overview, which covers system prompts, file URL schemes, uploaded files ([`allow_uploaded_files`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allow_uploaded_files)), and unresolved tool calls. + +### Preserving files across round-trips + +AG-UI has no native representation for agent-generated files ([`FilePart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.FilePart)) or [`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile) references, so they are omitted from `dump_messages` output by default. Set [`AGUIAdapter.preserve_file_data`](/docs/ai/api/ui/ag_ui/#pydantic_ai.ui.ag_ui.AGUIAdapter.preserve_file_data) to `True` to round-trip them through reserved `pydantic_ai_*` [activity messages](https://docs.ag-ui.com/concepts/messages), which a frontend completes by echoing those activity messages back on the next request. This is a representation opt-in, not a security one: an `UploadedFile` reconstructed from a round-tripped activity message is still subject to the inbound [`allow_uploaded_files`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allow_uploaded_files) gate before it reaches the agent. + +Behavior change + +`preserve_file_data` used to gate honoring inbound client-submitted `UploadedFile` references. It is now representation-only. If your app set `AGUIAdapter(preserve_file_data=True)` to accept inbound uploaded files, you must now also set [`allow_uploaded_files`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allow_uploaded_files)`=True`, since the two concerns are now separate flags. ### System prompts and instructions @@ -65745,10 +73573,16 @@ UI adapter endpoints aren't authentication boundaries. Both the AG-UI and Vercel The adapters apply a few defaults so that the authoritative state stays on your side: - **System prompts** -- client-submitted [`SystemPromptPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.SystemPromptPart)s are stripped by default and replaced with the agent's configured prompt. Control with [`UIAdapter.manage_system_prompt`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.manage_system_prompt); see each adapter's docs for details. -- **Dangling tool calls** -- if the client-submitted history ends in a [`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse) with unresolved [`ToolCallPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ToolCallPart)s and no matching `deferred_tool_results`, the tool calls are dropped with a warning so the agent doesn't execute tool calls that the model never emitted. For human-in-the-loop resumption, pass explicit `deferred_tool_results` to the run method -- tool calls resolved by those results are kept. +- **Dangling tool calls** -- if the client-submitted history ends in a [`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse) with unresolved [`ToolCallPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ToolCallPart)s and no matching `deferred_tool_results`, the tool calls are dropped with a warning, as a best-effort default so the agent doesn't execute an unresolved tool call the model never emitted. For human-in-the-loop resumption, pass explicit `deferred_tool_results` to the run method -- tool calls resolved by those results are kept (see the warning below). - **File URL schemes** -- only `http` and `https` are accepted by default for [`FileUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.FileUrl) parts in client-submitted messages. Non-HTTP schemes like `s3://` or `gs://` are dropped, since they cause the provider to fetch the object using your server's IAM role or service account. See [`UIAdapter.allowed_file_url_schemes`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allowed_file_url_schemes). - **File URL download mode** -- [`FileUrl.force_download`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.FileUrl.force_download) values other than `False` are reset to `False` by default on client-submitted messages. This prevents clients from forcing the server to fetch a URL, or using `'allow-local'` to opt out of the SSRF private-IP block. After auditing your frontend, opt into additional values with [`UIAdapter.allowed_file_url_force_download`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allowed_file_url_force_download). -- **Uploaded files** -- client-submitted [`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile) parts are dropped by default, just like non-HTTP `FileUrl`s, since the server resolves them against the provider's file storage API using its own credentials. After auditing your frontend, keep them by setting [`UIAdapter.preserve_file_data`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.preserve_file_data) to `True`. +- **Uploaded files** -- client-submitted [`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile) parts are dropped by default, just like non-HTTP `FileUrl`s, since the server resolves them against the provider's file storage API using its own credentials. After auditing your frontend, honor them by setting [`UIAdapter.allow_uploaded_files`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allow_uploaded_files) to `True`. This is a purely inbound security setting: file content the agent produces is always serialized on the way back out to the client. + +Tool approvals and results are submitted by the client + +On the [human-in-the-loop resumption](/docs/ai/tools-toolsets/deferred-tools#human-in-the-loop-tool-approval) path, the [`DeferredToolResults`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.DeferredToolResults) passed to the run method -- approvals, denials, and externally-executed tool results -- are submitted by the client along with the history, and the adapter does not verify that an approved tool call is one the server actually issued. A client that can reach the endpoint can therefore approve a tool call of its own making, including one for an approval-gated tool. Approval guards against the _model_ acting without human sign-off; it is not an authorization boundary against the client. + +Authenticate the endpoint (as above) and enforce authorization for sensitive actions inside the tool function itself -- against the authenticated user carried in your [dependencies](/docs/ai/core-concepts/dependencies#accessing-dependencies), not the client-supplied approval -- for any call that reaches the tool function. Alternatively, persist paused runs server-side and pass your own `deferred_tool_results` rather than honoring the client's. For stricter conversation integrity (e.g. ensuring prior assistant turns and tool returns match what the server actually produced), persist the history server-side keyed by the thread/session ID and pass it to the adapter via `message_history` -- caller-supplied history is trusted as coming from server-side persistence and isn't subject to this sanitization. @@ -65884,6 +73718,14 @@ Note Protocol-control chunks such as `StartChunk`, `FinishChunk`, `StartStepChunk`, or `FinishStepChunk` are automatically filtered out -- only the four data-carrying chunk types listed above are forwarded to the stream and preserved in `dump_messages`. +### Files from client-side tools + +Vercel AI SDK [client-side tools](https://ai-sdk.dev/docs/ai-sdk-ui/chatbot-tool-usage#client-side-tools) run in the browser and submit their result back to the server, where Pydantic AI resolves them as [external tool calls](/docs/ai/tools-toolsets/deferred-tools#external-tool-execution). Such a tool can return a file by putting a shape in its output that matches one of Pydantic AI's [multimodal content types](/docs/ai/advanced-features/input#image-audio-video-document-input); Pydantic AI deserializes it into that type (via the same `ToolReturnContent` discriminator used for round-tripping) before the run continues. Use the type's snake\_case field names -- these are validated as Pydantic AI models, not Vercel-cased payloads, so `media_type` deserializes but `mediaType` stays an opaque dict. Three shapes are supported: + +- **Inline bytes** -- a [`BinaryContent`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.BinaryContent) shape, `{ kind: 'binary', media_type: 'image/png', data: }` (image media types become [`BinaryImage`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.BinaryImage)). The `data` field accepts a base64 string, or the raw byte shapes a JavaScript frontend produces when it forwards a `Uint8Array` or Node `Buffer` through `JSON.stringify` without encoding it first (`{ "0": 137, "1": 80, ... }` or `{ "type": "Buffer", "data": [137, 80, ...] }`) -- all normalized to bytes at the wire boundary, so a client-side tool can return binary data without base64-encoding it by hand. +- **A file URL** -- a [`FileUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.FileUrl) shape such as `{ kind: 'image-url', url: 'https://...' }` or `{ kind: 'document-url', url: 'https://...' }`. This is often more efficient than inlining the bytes, since only the reference crosses the wire and the provider fetches the file directly -- a good fit when the file already lives at a URL the frontend trusts. The URL is honored only if its scheme passes the adapter's [`allowed_file_url_schemes`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allowed_file_url_schemes) allowlist (`http`/`https` by default); see the [trust model](#trust-model). +- **A provider-hosted file** -- an [`UploadedFile`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile) shape, `{ kind: 'uploaded-file', file_id: 'file-123', provider_name: 'openai' }`, referencing a file already uploaded to the provider's storage. Honored only when [`allow_uploaded_files`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allow_uploaded_files) is `True`, since the server resolves it against the provider's file API using its own credentials. + ## Message metadata [`VercelAIAdapter.dump_messages`](/docs/ai/api/ui/vercel_ai/#pydantic_ai.ui.vercel_ai.VercelAIAdapter.dump_messages) writes [`ModelRequest.metadata`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelRequest.metadata) and [`ModelResponse.metadata`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse.metadata) into Vercel AI [`UIMessage.metadata`](https://ai-sdk.dev/docs/ai-sdk-ui/message-metadata), and stores the message `timestamp` under a reserved `pydantic_ai` key so it survives the round-trip. [`VercelAIAdapter.load_messages`](/docs/ai/api/ui/vercel_ai/#pydantic_ai.ui.vercel_ai.VercelAIAdapter.load_messages) restores it on the way back. @@ -65894,7 +73736,7 @@ When streaming, the timestamp is also emitted as a Vercel AI `message-metadata` ## Trust model -Vercel AI's request `messages` array is fully client-controlled, and the protocol round-trips approval responses and tool results through the message history. The [`VercelAIAdapter`](/docs/ai/api/ui/vercel_ai/#pydantic_ai.ui.vercel_ai.VercelAIAdapter) applies defaults to strip untrusted parts before the agent runs -- see [Trust model for client-submitted messages](/docs/ai/integrations/ui/overview#trust-model-for-client-submitted-messages) in the UI adapter overview. +Vercel AI's request `messages` array is fully client-controlled, and the protocol round-trips approval responses and tool results through the message history. The [`VercelAIAdapter`](/docs/ai/api/ui/vercel_ai/#pydantic_ai.ui.vercel_ai.VercelAIAdapter) applies defaults to strip untrusted parts before the agent runs -- see [Trust model for client-submitted messages](/docs/ai/integrations/ui/overview#trust-model-for-client-submitted-messages) in the UI adapter overview, which covers system prompts, file URL schemes, uploaded files ([`allow_uploaded_files`](/docs/ai/api/ui/base/#pydantic_ai.ui.UIAdapter.allow_uploaded_files)), and unresolved tool calls. ## Tool Approval @@ -65977,8 +73819,8 @@ For most use cases, use the [`MCP` capability](/docs/ai/core-concepts/capabiliti You need to either install [`pydantic-ai`](/docs/ai/overview/install), or [`pydantic-ai-slim`](/docs/ai/overview/install#slim-install) with the `mcp` optional group: -- [pip](#tab-panel-48) -- [uv](#tab-panel-49) +- [pip](#tab-panel-34) +- [uv](#tab-panel-35) Terminal @@ -66006,6 +73848,8 @@ Each `MCPToolset` instance is a [toolset](/docs/ai/tools-toolsets/toolsets) and You can use [`async with agent`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.__aenter__) to open and close connections to all registered MCP toolsets (and in the case of stdio servers, start and stop the subprocesses) around the context where they'll be used in agent runs. You can also use `async with toolset` to manage the lifecycle of a specific toolset directly, for example if you'd like to share it across multiple agents. If you don't explicitly enter one of these context managers, the toolset will be opened and closed automatically as needed. +Note that a shared `MCPToolset` instance connects to the server as a single identity; if your users have their own credentials for the MCP server, see [per-user authentication](#per-user-authentication). + ### Streamable HTTP The [Streamable HTTP](https://modelcontextprotocol.io/introduction#streamable-http) transport is the recommended way to connect to a remote MCP server. @@ -66206,6 +74050,10 @@ Caution If a referenced environment variable using `${VAR}` syntax is not defined, a `ValueError` will be raised. Use the `${VAR:-default}` syntax to provide a fallback value. +Treat configuration files as trusted input + +A configuration file specifies executables and arguments to spawn as subprocesses, so anyone who can write it can run arbitrary commands. `${VAR}` references are expanded against the full process environment without an allowlist, so a config file can also read any environment variable. Only load configuration files you control; never load them from untrusted sources. + ### Usage mcp\_config\_loader.py @@ -66458,6 +74306,56 @@ if __name__ == '__main__': _(This example is complete, it can be run "as is")_ +## HTTP authentication + +For HTTP transports, `MCPToolset` accepts an `auth` argument: a bearer token string, any [`httpx.Auth`](https://www.python-httpx.org/advanced/authentication/), or the literal string `'oauth'` to enable [FastMCP's OAuth flow](https://gofastmcp.com/clients/auth/oauth). Static headers like API keys can be passed via the `headers` argument instead. + +### Per-user authentication + +In a multi-user or multi-tenant application, each user typically has their own credentials for the MCP server, such as a tenant-scoped bearer token. + +A shared `MCPToolset` instance is a single identity + +An `MCPToolset` instance maintains one MCP session that's shared by all concurrent agent runs using it: the connection is established (and authentication resolved) by whichever run needs it first, and only torn down once the last one finishes. Deriving credentials per-request from task-local state like a [`ContextVar`](https://docs.python.org/3/library/contextvars.html#contextvars.ContextVar) inside an `httpx.Auth` does not work on a shared instance: overlapping runs will silently send their requests with the credentials of whichever run opened the session. + +To make requests with the credentials of the user in question, each concurrent run needs its own `MCPToolset` instance so that it establishes its own authenticated session. The recommended way to do this is to build the toolset [dynamically](/docs/ai/tools-toolsets/toolsets#dynamically-building-a-toolset) using the [`@agent.toolset`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.toolset) decorator: the decorated function is passed the [run context](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext) and can read the user's credentials from the run's [dependencies](/docs/ai/core-concepts/dependencies): + +mcp\_per\_user\_auth.py + +```python +from dataclasses import dataclass + +from pydantic_ai import Agent, RunContext +from pydantic_ai.mcp import MCPToolset + + +@dataclass +class UserDeps: + mcp_token: str + + +agent = Agent('openai:gpt-5.2', deps_type=UserDeps) + + +@agent.toolset(per_run_step=False) # (1) +def user_mcp_server(ctx: RunContext[UserDeps]) -> MCPToolset: + return MCPToolset('http://localhost:8000/mcp', auth=ctx.deps.mcp_token) + + +async def main(): + result = await agent.run('What is 7 plus 5?', deps=UserDeps(mcp_token='')) + print(result.output) + #> The answer is 12. +``` + +`per_run_step=False` builds the toolset once per run instead of ahead of each run step, so the whole run shares a single MCP session. + +_(This example is complete, it can be run "as is" -- you'll need to add `asyncio.run(main())` to run `main`)_ + +Because the per-run toolset's session is established inside the run itself, credentials held in a `ContextVar` also resolve correctly with this pattern -- but passing them through deps is more explicit and doesn't depend on task-local state. + +As an alternative to a dynamic toolset, you can construct a new `MCPToolset` yourself for each request and pass it to the [`toolsets` argument](/docs/ai/tools-toolsets/toolsets) of the agent run methods. + ## Custom TLS / SSL configuration In some environments you need to tweak how HTTPS connections are established -- for example to trust an internal Certificate Authority, present a client certificate for **mTLS**, or (during local development only!) disable certificate verification altogether. `MCPToolset` exposes an `http_client` parameter so you can pass your own pre-configured [`httpx.AsyncClient`](https://www.python-httpx.org/async/): @@ -66988,8 +74886,8 @@ _(This example is complete, it can be run "as is")_ To use `AnthropicModel` models, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `anthropic` optional group: -- [pip](#tab-panel-56) -- [uv](#tab-panel-57) +- [pip](#tab-panel-50) +- [uv](#tab-panel-51) Terminal @@ -67092,6 +74990,7 @@ from pydantic_ai.models.anthropic import AnthropicModel, AnthropicModelSettings model = AnthropicModel('claude-sonnet-4-5') settings = AnthropicModelSettings( temperature=0.2, + top_k=40, service_tier='auto', ) agent = Agent(model, model_settings=settings) @@ -67100,7 +74999,7 @@ agent = Agent(model, model_settings=settings) ### Service tier -Anthropic supports controlling the [service tier](https://docs.anthropic.com/en/docs/build-with-claude/latency-and-throughput) to manage latency and throughput. You can use the unified [`service_tier`](/docs/ai/api/pydantic-ai/settings/#pydantic_ai.settings.ModelSettings.service_tier) field or the provider-specific [`anthropic_service_tier`](/docs/ai/api/models/anthropic/#pydantic_ai.models.anthropic.AnthropicModelSettings.anthropic_service_tier) field. `anthropic_service_tier` takes precedence over the unified field when both are set, and accepts Anthropic's native values (`'auto'` or `'standard_only'`). +Anthropic supports controlling the [service tier](https://platform.claude.com/docs/en/api/service-tiers) to manage latency and throughput. You can use the unified [`service_tier`](/docs/ai/api/pydantic-ai/settings/#pydantic_ai.settings.ModelSettings.service_tier) field or the provider-specific [`anthropic_service_tier`](/docs/ai/api/models/anthropic/#pydantic_ai.models.anthropic.AnthropicModelSettings.anthropic_service_tier) field. `anthropic_service_tier` takes precedence over the unified field when both are set, and accepts Anthropic's native values (`'auto'` or `'standard_only'`). The unified field maps as follows for Anthropic: @@ -67138,6 +75037,10 @@ Tool search on the legacy `AsyncAnthropicBedrock` client The legacy `InvokeModel` API doesn't support the `bm25` [tool search](/docs/ai/tools-toolsets/tools-advanced#tool-search) variant, so [`ToolSearch`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.ToolSearch) defaults to `'regex'` on the `AsyncAnthropicBedrock` client (instead of `'bm25'`), and passing `ToolSearch(strategy='bm25')` raises a `UserError`. +Token counting on the legacy `AsyncAnthropicBedrock` client + +The Anthropic SDK blocks its high-level token-counting method on Bedrock, so `count_tokens()` (and `count_tokens_before_request`) instead call Bedrock's own `/model/{model}/count-tokens` endpoint. That endpoint only accepts **base** foundation-model IDs (e.g. `anthropic.claude-sonnet-4-20250514-v1:0`); cross-region inference profile IDs (`us.`/`eu.`/`global.` prefixes) and end-of-life model versions are rejected by Bedrock. + ### Google Cloud To use Claude models via [Google Cloud Vertex AI](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude), follow the [Anthropic documentation](https://docs.anthropic.com/en/api/claude-on-vertex-ai) on how to set up an `AsyncAnthropicVertex` client and then pass it to `AnthropicProvider`: @@ -67183,7 +75086,7 @@ See [Anthropic's Microsoft Foundry documentation](https://platform.claude.com/do Anthropic's [task budgets](https://platform.claude.com/docs/en/build-with-claude/task-budgets) let you give Claude an advisory token budget for a full agentic loop -- including thinking, tool calls, tool results, and output -- so the model can pace itself and finish gracefully as the budget is consumed. Configure them with [`AnthropicModelSettings.anthropic_task_budget`](/docs/ai/api/models/anthropic/#pydantic_ai.models.anthropic.AnthropicModelSettings.anthropic_task_budget), which takes an [`AnthropicTaskBudget`](/docs/ai/api/models/anthropic/#pydantic_ai.models.anthropic.AnthropicTaskBudget) payload and maps to `output_config.task_budget`. -Pydantic AI automatically enables Anthropic's required `task-budgets-2026-03-13` beta when this setting is present. Support is currently limited to native Anthropic `claude-opus-4-7` and `claude-opus-4-8` requests, not Bedrock, Vertex, or Microsoft Foundry Anthropic model IDs. +Pydantic AI automatically enables Anthropic's required `task-budgets-2026-03-13` beta when this setting is present. Support is currently limited to native Anthropic `claude-opus-4-7`, `claude-opus-4-8`, and `claude-sonnet-5` requests, not Bedrock, Vertex, or Microsoft Foundry Anthropic model IDs. anthropic\_task\_budget.py @@ -67605,8 +75508,8 @@ Pydantic AI raises a [`UserError`](/docs/ai/api/pydantic-ai/exceptions/#pydantic To use `BedrockConverseModel`, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `bedrock` optional group: -- [pip](#tab-panel-62) -- [uv](#tab-panel-63) +- [pip](#tab-panel-58) +- [uv](#tab-panel-59) Terminal @@ -67986,8 +75889,8 @@ Unlike other providers that use httpx for HTTP requests, Bedrock uses boto3's na To use `CerebrasModel`, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `cerebras` optional group: -- [pip](#tab-panel-64) -- [uv](#tab-panel-65) +- [pip](#tab-panel-56) +- [uv](#tab-panel-57) Terminal @@ -68081,8 +75984,8 @@ agent = Agent(model) To use `CohereModel`, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `cohere` optional group: -- [pip](#tab-panel-66) -- [uv](#tab-panel-67) +- [pip](#tab-panel-60) +- [uv](#tab-panel-61) Terminal @@ -68164,6 +76067,23 @@ agent = Agent(model) ... ``` +## Model settings + +You can customize model behavior using [`CohereModelSettings`](/docs/ai/api/models/cohere/#pydantic_ai.models.cohere.CohereModelSettings): + +```python +from pydantic_ai import Agent +from pydantic_ai.models.cohere import CohereModel, CohereModelSettings + +model = CohereModel('command-r7b-12-2024') +settings = CohereModelSettings( + temperature=0.2, + top_k=40, +) +agent = Agent(model, model_settings=settings) +... +``` + --- # [Google](https://pydantic.dev/docs/ai/models/google/) @@ -68181,8 +76101,8 @@ Two providers wrap those endpoints: To use `GoogleModel`, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `google` optional group: -- [pip](#tab-panel-58) -- [uv](#tab-panel-59) +- [pip](#tab-panel-52) +- [uv](#tab-panel-53) Terminal @@ -68474,6 +76394,37 @@ agent = Agent(model) ... ``` +## HTTP Retries + +Note + +For most use cases, the model-agnostic [HTTP request retries](/docs/ai/advanced-features/retries) approach is preferable, as it works the same way across all providers. The `retry_options` argument below is a Google-specific alternative that delegates retrying to the `google-genai` SDK's own HTTP layer. + +By default, the `google-genai` SDK does not retry requests that fail with a transient HTTP error. You can enable retries by passing a [`HttpRetryOptions`](https://googleapis.github.io/python-genai/genai.html#genai.types.HttpRetryOptions) instance to the `retry_options` argument of `GoogleProvider` or `GoogleCloudProvider`: + +```python +from google.genai.types import HttpRetryOptions + +from pydantic_ai import Agent +from pydantic_ai.models.google import GoogleModel +from pydantic_ai.providers.google import GoogleProvider + +retry_options = HttpRetryOptions( + attempts=4, + initial_delay=1.0, + max_delay=60.0, + http_status_codes=[408, 429, 500, 502, 503, 504], +) +model = GoogleModel( + 'gemini-3-pro-preview', + provider=GoogleProvider(api_key='your-api-key', retry_options=retry_options), +) +agent = Agent(model) +... +``` + +This passes the options through to the SDK's [`HttpOptions.retry_options`](https://googleapis.github.io/python-genai/genai.html#genai.types.HttpOptions.retry_options). See the [Vertex AI retry strategy documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/retry-strategy) for guidance on choosing values. + ## Document, Image, Audio, and Video Input `GoogleModel` supports multi-modal input, including documents, images, audio, and video. @@ -68534,6 +76485,7 @@ from pydantic_ai.models.google import GoogleModel, GoogleModelSettings settings = GoogleModelSettings( temperature=0.2, max_tokens=1024, + top_k=40, google_safety_settings=[ { 'category': HarmCategory.HARM_CATEGORY_HATE_SPEECH, @@ -68736,8 +76688,8 @@ Calling `cancel()` from a different task while iteration is in progress is not c To use `GroqModel`, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `groq` optional group: -- [pip](#tab-panel-68) -- [uv](#tab-panel-69) +- [pip](#tab-panel-64) +- [uv](#tab-panel-65) Terminal @@ -68837,8 +76789,8 @@ This page covers chat completions via Hugging Face Inference Providers. To run H To use `HuggingFaceModel`, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `huggingface` optional group: -- [pip](#tab-panel-70) -- [uv](#tab-panel-71) +- [pip](#tab-panel-62) +- [uv](#tab-panel-63) Terminal @@ -68996,8 +76948,8 @@ Calling `cancel()` from a different task while iteration is in progress is not c To use `MistralModel`, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `mistral` optional group: -- [pip](#tab-panel-84) -- [uv](#tab-panel-85) +- [pip](#tab-panel-66) +- [uv](#tab-panel-67) Terminal @@ -69091,8 +77043,8 @@ agent = Agent(model) To use [`OllamaModel`](/docs/ai/api/models/ollama/#pydantic_ai.models.ollama.OllamaModel), you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `openai` optional group: -- [pip](#tab-panel-72) -- [uv](#tab-panel-73) +- [pip](#tab-panel-68) +- [uv](#tab-panel-69) Terminal @@ -69207,8 +77159,8 @@ If you use [`NativeOutput`](/docs/ai/api/pydantic-ai/output/#pydantic_ai.output. To use OpenAI models or OpenAI-compatible APIs, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `openai` optional group: -- [pip](#tab-panel-54) -- [uv](#tab-panel-55) +- [pip](#tab-panel-48) +- [uv](#tab-panel-49) Terminal @@ -69241,7 +77193,7 @@ The bare `'openai:'` prefix resolves to [`OpenAIResponsesModel`](/docs/ai/api/mo ```python from pydantic_ai import Agent -agent = Agent('openai:gpt-5.2') +agent = Agent('openai:gpt-5.6-sol') ... ``` @@ -69253,7 +77205,7 @@ Or initialise the model directly with just the model name: from pydantic_ai import Agent from pydantic_ai.models.openai import OpenAIResponsesModel -model = OpenAIResponsesModel('gpt-5.2') +model = OpenAIResponsesModel('gpt-5.6-sol') agent = Agent(model) ... ``` @@ -69341,6 +77293,24 @@ OpenAI supports controlling the [service tier](https://platform.openai.com/docs/ The features below are specific to the Responses API and only available on [`OpenAIResponsesModel`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIResponsesModel) (the default). For background on how the Responses API differs from Chat Completions, see the [OpenAI API docs](https://platform.openai.com/docs/guides/migrate-to-responses). +### Reasoning mode + +Models that support it (currently the GPT-5.6 family) can use OpenAI's [`standard` and `pro` reasoning modes](https://developers.openai.com/api/docs/guides/reasoning#reasoning-mode). `standard` is the default; `pro` performs more model work to improve reliability on difficult tasks, at the cost of higher latency and token usage. The mode is independent of the reasoning effort: any combination of mode and effort is valid, and the unified [`thinking`](/docs/ai/advanced-features/thinking) setting only ever influences the effort, so `pro` is used only when you set it explicitly. + +Configure the mode with [`openai_reasoning_mode`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIResponsesModelSettings.openai_reasoning_mode); there is no separate `pro` model to select: + +```python +from pydantic_ai import Agent +from pydantic_ai.models.openai import OpenAIResponsesModel, OpenAIResponsesModelSettings + +model = OpenAIResponsesModel('gpt-5.6-sol') +settings = OpenAIResponsesModelSettings(openai_reasoning_mode='pro') +agent = Agent(model, model_settings=settings) +... +``` + +The setting is ignored on models that don't support reasoning mode, per [`OpenAIModelProfile.openai_responses_supports_reasoning_mode`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.openai.OpenAIModelProfile.openai_responses_supports_reasoning_mode). + ### Native tools The Responses API has native tools that you can use instead of building your own: @@ -69523,6 +77493,30 @@ Stateful compaction pairs especially well with [`openai_previous_response_id='au For lower-level use cases, you can call [`compact_messages`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIResponsesModel.compact_messages) directly on the model. +### Background mode + +For long-running requests, such as large reasoning or tool-heavy jobs that may exceed the practical duration of a synchronous request, OpenAI's Responses API offers a [background mode](https://platform.openai.com/docs/guides/background) that runs the request server-side and lets you retrieve the result once it's ready. Enable it with [`openai_background`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIResponsesModelSettings.openai_background): + +openai\_background.py + +```python +from pydantic_ai import Agent +from pydantic_ai.models.openai import OpenAIResponsesModel, OpenAIResponsesModelSettings + +model = OpenAIResponsesModel('gpt-5.2') +settings = OpenAIResponsesModelSettings(openai_background=True) +agent = Agent(model, model_settings=settings) +... +``` + +When the response comes back still pending (`'queued'` or `'in_progress'`), Pydantic AI continues it to completion transparently, so you don't need to do anything. This works for both [`agent.run`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run) and [`agent.run_stream`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run_stream), and the result is stitched into a single [`ModelResponse`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse) -- when streaming, live token activity is surfaced as it's generated and arrives as one continuous stream. + +Because the request is queued server-side, the time to the first token is higher than for a synchronous request. While a background response is still pending, Pydantic AI polls for completion at a fixed interval. + +Note + +If a run is suspended mid-request (its final [`ModelResponse.state`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ModelResponse.state) is `'suspended'`) and persisted in message history, passing that history back resumes the same background response rather than starting a new one. Resuming after the provider's retention window raises [`SuspendedResponseExpired`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.SuspendedResponseExpired). Abandoning or cancelling the run cancels the server-side background job. + ## Chat Completions API If you need the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat) instead of the default [Responses API](https://platform.openai.com/docs/api-reference/responses), pin to it with the `'openai-chat:'` prefix or [`OpenAIChatModel`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIChatModel): @@ -69595,6 +77589,12 @@ model = OpenAIChatModel( agent = Agent(model) ``` +#### Models that accept only one leading system message + +Some models are served with a chat template (applied server-side, for example by [vLLM](https://docs.vllm.ai/), [LiteLLM](#litellm), or TGI) that accepts only a single system message at the start of the conversation and rejects additional ones. Sending more than one fails with a `400` error such as `System message must be at the beginning.` or `Conversation roles must alternate ...`, seen with some newer Qwen, Mistral, Gemma, and Command-R models. It's easy to hit without intending to, since more than one leading system message can be produced in several ways. + +Set `openai_chat_supports_multiple_system_messages=False` on the model's [`OpenAIModelProfile`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.openai.OpenAIModelProfile) (as shown above) to merge the leading run of system messages into one, joined with two newlines, before the request is sent. The merge is lossless, so it's safe to enable whenever a backend rejects multiple system messages. + ### DeepSeek To use the [DeepSeek](https://deepseek.com) provider, first create an API key by following the [Quick Start guide](https://api-docs.deepseek.com/). @@ -69687,6 +77687,10 @@ agent = Agent(model) ... ``` +Document input is not supported + +The DashScope compatible-mode Chat Completions API does not accept document content parts, so passing a [`DocumentUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.DocumentUrl) or document [`BinaryContent`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.BinaryContent) to an [`OpenAIChatModel`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIChatModel) backed by [`AlibabaProvider`](/docs/ai/api/pydantic-ai/providers/#pydantic_ai.providers.alibaba.AlibabaProvider) raises a `UserError`. + ### Ollama See [Ollama](/docs/ai/models/ollama) for dedicated Ollama documentation, including structured output and Ollama Cloud limitations. @@ -69749,6 +77753,21 @@ agent = Agent(model) Azure AI Foundry also supports the OpenAI Responses API through [`OpenAIResponsesModel`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIResponsesModel). This is particularly recommended when working with document inputs ([`DocumentUrl`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.DocumentUrl) and [`BinaryContent`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.BinaryContent)), as Azure's Chat Completions API does not support these input types. +Use the `azure-responses:` prefix to select the Responses API by name (the `azure:` prefix uses the Chat Completions API): + +```python +from pydantic_ai import Agent + +agent = Agent('azure-responses:gpt-5.2') +... +``` + +Note + +Azure's Responses API doesn't yet support every feature of OpenAI's Responses API -- for example, the web search built-in tool is unavailable, and there are limits around image editing and file uploads. See [Microsoft's Responses API docs](https://learn.microsoft.com/en-us/azure/foundry/openai/how-to/responses) for the current list. This applies whether you use the `azure-responses:` shorthand or construct `OpenAIResponsesModel` with `AzureProvider` directly. + +Or initialise the model and provider directly: + Document processing with Azure using Responses API ```python @@ -69990,6 +78009,10 @@ print(result.output) ... ``` +Note + +If your model rejects requests with more than one leading system message (for example, you see `System message must be at the beginning.`), set `openai_chat_supports_multiple_system_messages=False` on its profile. See [Models that accept only one leading system message](#models-that-accept-only-one-leading-system-message) for details. + ### Nebius AI Studio Go to [Nebius AI Studio](https://studio.nebius.com/) and create an API key. @@ -70110,6 +78133,56 @@ agent = Agent(model) ... ``` +### Atlas Cloud + +[Atlas Cloud](https://www.atlascloud.ai/) is an OpenAI-compatible API gateway that provides access to 300+ models from a single endpoint, including DeepSeek, Qwen, Claude, GPT, and Gemini. + +Atlas Cloud doesn't have a dedicated provider class, so you can use it with [`OpenAIProvider`](/docs/ai/api/pydantic-ai/providers/#pydantic_ai.providers.openai.OpenAIProvider) by setting the `base_url` and `api_key`: + +```python +from pydantic_ai import Agent +from pydantic_ai.models.openai import OpenAIChatModel +from pydantic_ai.providers.openai import OpenAIProvider + +model = OpenAIChatModel( + 'deepseek-ai/deepseek-v4-pro', + provider=OpenAIProvider( + base_url='https://api.atlascloud.ai/v1', + api_key='your-atlas-cloud-api-key', + ), +) +agent = Agent(model) +... +``` + +### Rapid-MLX (Apple Silicon) + +[Rapid-MLX](https://github.com/raullenchai/Rapid-MLX) is an OpenAI-compatible inference server for Apple Silicon, built on Apple's MLX framework. + +Terminal + +```bash +pip install rapid-mlx +rapid-mlx serve mlx-community/Qwen3.5-4B-MLX-4bit +``` + +The server listens on `http://localhost:8000/v1` and implements the OpenAI chat completions API, so you can point [`OpenAIProvider`](/docs/ai/api/pydantic-ai/providers/#pydantic_ai.providers.openai.OpenAIProvider) at it: + +```python +from pydantic_ai import Agent +from pydantic_ai.models.openai import OpenAIChatModel +from pydantic_ai.providers.openai import OpenAIProvider + +rapid_mlx_model = OpenAIChatModel( + model_name='default', + provider=OpenAIProvider( + base_url='http://localhost:8000/v1', + api_key='not-needed', + ), +) +agent = Agent(rapid_mlx_model) +``` + --- # [OpenRouter](https://pydantic.dev/docs/ai/models/openrouter/) @@ -70120,8 +78193,8 @@ agent = Agent(model) To use `OpenRouterModel`, you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `openrouter` optional group: -- [pip](#tab-panel-74) -- [uv](#tab-panel-75) +- [pip](#tab-panel-70) +- [uv](#tab-panel-71) Terminal @@ -70329,6 +78402,7 @@ Pydantic AI is model-agnostic and has built-in support for multiple model provid - [Hugging Face](/docs/ai/models/huggingface) - [Mistral](/docs/ai/models/mistral) - [OpenRouter](/docs/ai/models/openrouter) +- [Z.AI](/docs/ai/models/zai) ## OpenAI-compatible Providers @@ -70363,6 +78437,27 @@ Pydantic AI uses a few key terms to describe how it interacts with different LLM When you instantiate an [`Agent`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent) with just a name formatted as `:`, e.g. `openai:gpt-5.2` or `openrouter:google/gemini-3-pro-preview`, Pydantic AI will automatically select the appropriate model class, provider, and profile. If you want to use a different provider or profile, you can instantiate a model class directly and pass in `provider` and/or `profile` arguments. +### Inspecting a model's profile + +A model's [`ModelProfile`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile) also describes what the model can do. It is a `TypedDict`, so you read capability flags with normal dictionary access via `model.profile` -- for example [`supports_tools`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile.supports_tools), [`supports_json_schema_output`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile.supports_json_schema_output), and [`supported_native_tools`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile.supported_native_tools). This is useful when you want to branch on a capability rather than discover a limitation at request time -- for example checking whether a model supports tool calling, native JSON-schema output, or a specific native tool before relying on it: + +```python +from pydantic_ai.models.test import TestModel +from pydantic_ai.native_tools import WebSearchTool + +model = TestModel() +profile = model.profile + +print(profile['supports_tools']) +#> True +print(profile['supports_json_schema_output']) +#> False +print(WebSearchTool in profile['supported_native_tools']) +#> True +``` + +`model.profile` is usually the fully _resolved_ profile: keys from [`DEFAULT_PROFILE`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.DEFAULT_PROFILE) are merged with the provider's defaults, so direct key access like `profile['supports_tools']` works. If you supply `profile=` as a callable (or otherwise have a partial profile dict), use `profile.get('supports_tools', DEFAULT_PROFILE['supports_tools'])` (after importing `DEFAULT_PROFILE`) to tolerate missing keys. Any [`Model`](/docs/ai/api/models/base/#pydantic_ai.models.Model) instance exposes its resolved profile the same way, so the same check works whether the model was selected automatically from a `:` name or instantiated directly. Don't confuse this with [Capabilities](/docs/ai/core-concepts/capabilities), which are reusable bundles of tools, hooks, and settings you add to an agent -- the profile describes what the underlying model itself supports. + ## HTTP Client Lifecycle When a [`Provider`](/docs/ai/api/pydantic-ai/providers/#pydantic_ai.providers.Provider) creates its own HTTP client (i.e. you don't pass a custom `http_client`), it owns that client's lifecycle. Using the [`Agent`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent) as an async context manager ensures the HTTP client is closed cleanly on exit: @@ -70565,8 +78660,8 @@ In this example, if the OpenAI model fails, the agent will automatically fall ba The next example demonstrates the exception-handling capabilities of `FallbackModel`. If all models fail, a [`FallbackExceptionGroup`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.FallbackExceptionGroup) is raised, which contains all the exceptions encountered during the `run` execution. -- [Python >=3.11](#tab-panel-50) -- [Python <3.11](#tab-panel-51) +- [Python >=3.11](#tab-panel-44) +- [Python <3.11](#tab-panel-45) fallback\_model\_failure.py @@ -70788,8 +78883,8 @@ This decorator will miss `ModelAPIError` exceptions when using `FallbackModel`, To handle both cases, you can use Python 3.11+ `except*` syntax, which catches matching exceptions from exception groups as well as bare exceptions. Note that `except*` always delivers the caught exceptions as an `ExceptionGroup` (even if the original was a bare exception), so re-raising will propagate an `ExceptionGroup` rather than the original exception type: -- [Python >=3.11](#tab-panel-52) -- [Python <3.11](#tab-panel-53) +- [Python >=3.11](#tab-panel-46) +- [Python <3.11](#tab-panel-47) middleware\_with\_fallback.py @@ -70873,8 +78968,8 @@ except FallbackExceptionGroup as exc_group: To use [`XaiModel`](/docs/ai/api/models/xai/#pydantic_ai.models.xai.XaiModel), you need to either install `pydantic-ai`, or install `pydantic-ai-slim` with the `xai` optional group: -- [pip](#tab-panel-60) -- [uv](#tab-panel-61) +- [pip](#tab-panel-54) +- [uv](#tab-panel-55) Terminal @@ -71011,7 +79106,7 @@ _(This example is complete, it can be run "as is")_ The `XSearch` capability accepts: -- **`allowed_x_handles`** / **`excluded_x_handles`**: filter results to (or away from) up to 10 X handles. These are mutually exclusive. +- **`allowed_x_handles`** / **`excluded_x_handles`**: filter results to (or away from) up to 20 X handles. These are mutually exclusive. - **`from_date`** / **`to_date`**: restrict results to posts created within the given datetime range (naive datetimes are interpreted as UTC). - **`enable_image_understanding`** (default: `False`): analyze images attached to posts. - **`enable_video_understanding`** (default: `False`): analyze video content attached to posts. @@ -71037,6 +79132,8 @@ agent = Agent( Set `xai_reasoning_effort='none'` or `thinking=False` to disable reasoning on Grok 4.3. xAI redirects several retired text model slugs to `grok-4.3`; choose `grok-4.3` and an explicit reasoning effort when you need predictable behavior and cost. See the [xAI May 15 retirement guide](https://docs.x.ai/developers/migration/may-15-retirement) for details. +Grok 4.5 supports `'low'`, `'medium'`, and `'high'` but not `'none'`, so it always reasons: `thinking=False` is silently ignored and `thinking=True` maps to `'medium'`. + ## Agentic turns When a request uses xAI's server-side [native tools](/docs/ai/overview/native-tools) (e.g. web search, code execution, X search), xAI runs its own loop -- calling those tools and processing their results -- before returning a final response. You can cap how many turns that server-side loop may take with [`XaiModelSettings.xai_max_turns`](/docs/ai/api/models/xai/#pydantic_ai.models.xai.XaiModelSettings.xai_max_turns): @@ -71185,11 +79282,7 @@ Add `--claude` to also install into `.claude/skills/` alongside the default `.ag # Pydantic AI Gateway -**[Pydantic AI Gateway](https://logfire.pydantic.dev/)** is a unified interface for accessing multiple AI providers with a single key, managed through [Pydantic Logfire](https://logfire.pydantic.dev/). Features include built-in OpenTelemetry observability, real-time cost monitoring, failover management, and native integration with the other tools in the [Pydantic stack](https://pydantic.dev/). - -Migrated to Pydantic Logfire - -The AI Gateway has moved from `gateway.pydantic.dev` to [Pydantic Logfire](https://logfire.pydantic.dev/). If you were using the standalone gateway, see [Pydantic AI Gateway is Moving to Pydantic Logfire](https://logfire.pydantic.dev/docs/gateway-migration/). +**[Pydantic AI Gateway](https://pydantic.dev/ai-gateway)** is a unified interface for accessing multiple AI providers with a single key, managed through [Pydantic Logfire](https://pydantic.dev/logfire). Features include built-in OpenTelemetry observability, real-time cost monitoring, failover management, and native integration with the other tools in the [Pydantic stack](https://pydantic.dev/). Sign up at [logfire.pydantic.dev](https://logfire.pydantic.dev/). @@ -71288,8 +79381,8 @@ AWS Bedrock Before you start, make sure you are on version 1.16 or later of `pydantic-ai`. To update to the latest version run: -- [uv](#tab-panel-86) -- [pip](#tab-panel-87) +- [uv](#tab-panel-80) +- [pip](#tab-panel-81) Terminal @@ -71381,8 +79474,8 @@ Before you start, log out of Claude Code using `/logout`. Set your gateway credentials as environment variables, using the base URL that matches your Logfire region: -- [US](#tab-panel-88) -- [EU](#tab-panel-89) +- [US](#tab-panel-82) +- [EU](#tab-panel-83) Terminal @@ -71416,8 +79509,8 @@ export PYDANTIC_AI_GATEWAY_API_KEY="YOUR_GATEWAY_API_KEY" Then add the following configuration to `~/.codex/config.toml`, using the base URL that matches your Logfire region: -- [US](#tab-panel-90) -- [EU](#tab-panel-91) +- [US](#tab-panel-84) +- [EU](#tab-panel-85) ```toml model = "gpt-5.4" @@ -71455,8 +79548,8 @@ Launch Codex by typing `codex`. All requests will now route through the Pydantic Use the base URL that matches your Logfire region (`gateway-us` or `gateway-eu`). -- [US](#tab-panel-92) -- [EU](#tab-panel-93) +- [US](#tab-panel-86) +- [EU](#tab-panel-87) openai\_sdk.py @@ -71498,8 +79591,8 @@ print(response.choices[0].message.content) Use the base URL that matches your Logfire region (`gateway-us` or `gateway-eu`). -- [US](#tab-panel-94) -- [EU](#tab-panel-95) +- [US](#tab-panel-88) +- [EU](#tab-panel-89) anthropic\_sdk.py @@ -71543,8 +79636,8 @@ print(response.content[0].text) The [Vercel AI SDK](https://ai-sdk.dev/) can route through the Gateway by pointing each provider's `baseURL` at the matching proxy path (e.g. `/proxy/openai` or `/proxy/anthropic`). Use the base URL that matches your Logfire region (`gateway-us` or `gateway-eu`). -- [US](#tab-panel-96) -- [EU](#tab-panel-97) +- [US](#tab-panel-90) +- [EU](#tab-panel-91) ```typescript import { createOpenAI } from "@ai-sdk/openai"; @@ -71701,11 +79794,13 @@ Yet despite virtually every Python agent framework and LLM library using Pydanti We built Pydantic AI with one simple aim: to bring that FastAPI feeling to GenAI app and agent development. +Pydantic AI ships the agent loop and a composable [capabilities](/docs/ai/core-concepts/capabilities) system; [Pydantic AI Harness](https://pydantic.dev/docs/ai/harness/) is our official library of ready-made capabilities -- code execution, file access, guardrails, sub-agent orchestration, and more -- that you pick and choose to build coding agents, research assistants, and anything in between. + ## Why use Pydantic AI 1. **Built by the Pydantic Team**: [Pydantic Validation](https://docs.pydantic.dev/latest/) is the validation layer of the OpenAI SDK, the Google ADK, the Anthropic SDK, LangChain, LlamaIndex, AutoGPT, Transformers, CrewAI, Instructor and many more. _Why use the derivative when you can go straight to the source?_ 😃 -2. **Model-agnostic**: Supports virtually every [model](/docs/ai/models/overview) and provider: OpenAI, Anthropic, Gemini, DeepSeek, Grok, Cohere, Mistral, and Perplexity; Azure AI Foundry, Amazon Bedrock, Google Cloud, Ollama, LiteLLM, Groq, OpenRouter, Together AI, Fireworks AI, Cerebras, Hugging Face, GitHub, Heroku, Vercel, Nebius, OVHcloud, Alibaba Cloud, and SambaNova. If your favorite model or provider is not listed, you can easily implement a [custom model](/docs/ai/models/overview#custom-models). +2. **Model-agnostic**: Supports virtually every [model](/docs/ai/models/overview) and provider: OpenAI, Anthropic, Gemini, DeepSeek, Grok, Cohere, Mistral, and Perplexity; Azure AI Foundry, Amazon Bedrock, Google Cloud, Ollama, LiteLLM, Groq, OpenRouter, Together AI, Fireworks AI, Cerebras, Hugging Face, GitHub, Heroku, Vercel, Nebius, OVHcloud, Alibaba Cloud, SambaNova, and Z.AI. If your favorite model or provider is not listed, you can easily implement a [custom model](/docs/ai/models/overview#custom-models). 3. **Seamless Observability**: Tightly [integrates](/docs/ai/integrations/logfire) with [Pydantic Logfire](https://pydantic.dev/logfire), our general-purpose OpenTelemetry observability platform, for real-time debugging, evals-based performance monitoring, and behavior, tracing, and cost tracking. If you already have an observability platform that supports OTel, you can [use that too](/docs/ai/integrations/logfire#alternative-observability-backends). @@ -71713,7 +79808,7 @@ We built Pydantic AI with one simple aim: to bring that FastAPI feeling to GenAI 5. **Powerful Evals**: Enables you to systematically test and [evaluate](/docs/ai/evals/evals) the performance and accuracy of the agentic systems you build, and monitor the performance over time in Pydantic Logfire. -6. **Extensible by Design**: Build agents from composable [capabilities](/docs/ai/core-concepts/capabilities) that bundle tools, hooks, instructions, and model settings into reusable units. Use built-in capabilities for [web search](/docs/ai/core-concepts/capabilities#provider-adaptive-tools), [thinking](/docs/ai/core-concepts/capabilities#thinking), and [MCP](/docs/ai/core-concepts/capabilities#provider-adaptive-tools), pick from the [Pydantic AI Harness](/docs/ai/harness/overview) capability library, build your own, or install [third-party capability packages](/docs/ai/guides/extensibility). Define agents entirely in [YAML/JSON](/docs/ai/core-concepts/agent-spec) -- no code required. +6. **Extensible by Design**: Build agents from composable [capabilities](/docs/ai/core-concepts/capabilities) that bundle tools, hooks, instructions, and model settings into reusable units. Use built-in capabilities for [web search](/docs/ai/core-concepts/capabilities#provider-adaptive-tools), [thinking](/docs/ai/core-concepts/capabilities#thinking), and [MCP](/docs/ai/core-concepts/capabilities#provider-adaptive-tools), pick from the [Pydantic AI Harness](https://pydantic.dev/docs/ai/harness/) capability library, build your own, or install [third-party capability packages](/docs/ai/guides/extensibility). Define agents entirely in [YAML/JSON](/docs/ai/core-concepts/agent-spec) -- no code required. 7. **MCP and UI**: Integrates the [Model Context Protocol](/docs/ai/mcp/overview) and various [UI event stream](/docs/ai/integrations/ui/overview) standards to give your agent access to external tools and data and build interactive applications with streaming event-based communication. @@ -71761,6 +79856,25 @@ Register static [instructions](/docs/ai/core-concepts/agent#instructions) using _(This example is complete, it can be run "as is", assuming you've [installed the `pydantic_ai` package](/docs/ai/overview/install))_ +No API key yet? + +You don't need a provider API key to try Pydantic AI. Pass the built-in [`'test'` model](/docs/ai/guides/testing#unit-testing-with-testmodel), which runs entirely offline without calling an LLM: + +hello\_world\_test.py + +```python +from pydantic_ai import Agent + +agent = Agent('test') # (1) +result = agent.run_sync('Where does "hello world" come from?') +print(result.output) +#> success (no tool calls) +``` + +[`TestModel`](/docs/ai/api/models/test/#pydantic_ai.models.test.TestModel) returns canned responses so you can exercise your agent, tools, and outputs without a key. + +When you're ready to use a real model, see [Models and Providers](/docs/ai/models/overview) to pick a provider and set its API key. + The exchange will be very short: Pydantic AI will send the instructions and the user prompt to the LLM, and the model will return a text response. Not very interesting yet, but we can easily add [tools](/docs/ai/tools-toolsets/tools), [dynamic instructions](/docs/ai/core-concepts/agent#instructions), [structured outputs](/docs/ai/core-concepts/output), or composable [capabilities](/docs/ai/core-concepts/capabilities) to build more powerful agents. @@ -71963,8 +80077,8 @@ Join [Slack](https://logfire.pydantic.dev/docs/join-slack/) or file an issue on Pydantic AI is available on PyPI as [`pydantic-ai`](https://pypi.org/project/pydantic-ai/) so installation is as simple as: -- [pip](#tab-panel-76) -- [uv](#tab-panel-77) +- [pip](#tab-panel-72) +- [uv](#tab-panel-73) Terminal @@ -71994,8 +80108,8 @@ We distribute the [`pydantic_ai_examples`](https://github.com/pydantic/pydantic- To install examples, use the `examples` optional group: -- [pip](#tab-panel-78) -- [uv](#tab-panel-79) +- [pip](#tab-panel-74) +- [uv](#tab-panel-75) Terminal @@ -72015,8 +80129,8 @@ To run the examples, follow instructions in the [examples docs](/docs/ai/example If you know which model you're going to use and want to avoid installing superfluous packages, you can use the [`pydantic-ai-slim`](https://pypi.org/project/pydantic-ai-slim/) package. For example, if you're using just [`OpenAIChatModel`](/docs/ai/api/models/openai/#pydantic_ai.models.openai.OpenAIChatModel), you would run: -- [pip](#tab-panel-80) -- [uv](#tab-panel-81) +- [pip](#tab-panel-76) +- [uv](#tab-panel-77) Terminal @@ -72063,8 +80177,8 @@ uv add "pydantic-ai-slim[openai]" You can also install dependencies for multiple models and use cases, for example: -- [pip](#tab-panel-82) -- [uv](#tab-panel-83) +- [pip](#tab-panel-78) +- [uv](#tab-panel-79) Terminal @@ -72116,10 +80230,12 @@ result = agent.run_sync('Who let the dogs out?') ## API Key Configuration -### `UserError: API key must be provided or set in the [MODEL]_API_KEY environment variable` +### [`UserError`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.UserError): Set the `[PROVIDER]_API_KEY` environment variable or pass it via the provider's `api_key=...` argument If you're running into issues with setting the API key for your model, visit the [Models](/docs/ai/models/overview) page to learn more about how to set an environment variable and/or pass in an `api_key` argument. +To try Pydantic AI without an API key, use the built-in [`'test'` model](/docs/ai/guides/testing#unit-testing-with-testmodel): [`Agent('test')`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent). + ## Monitoring HTTPX Requests You can use custom `httpx` clients in your models in order to access specific requests, responses, and headers at runtime. @@ -72154,8 +80270,8 @@ The seventh V2 beta, forked from **v1.107.0**. There are no new V2 breaking or b Install it the same way, pinning the exact pre-release version: -- [pip](#tab-panel-98) -- [uv](#tab-panel-99) +- [pip](#tab-panel-92) +- [uv](#tab-panel-93) Terminal @@ -72177,8 +80293,8 @@ The sixth V2 beta, forked from **v1.106.0**. There are no new V2 breaking or beh Install it the same way, pinning the exact pre-release version: -- [pip](#tab-panel-100) -- [uv](#tab-panel-101) +- [pip](#tab-panel-94) +- [uv](#tab-panel-95) Terminal @@ -72200,8 +80316,8 @@ The fifth V2 beta, forked from **v1.105.0**. There are no new V2 breaking or beh Install it the same way, pinning the exact pre-release version: -- [pip](#tab-panel-102) -- [uv](#tab-panel-103) +- [pip](#tab-panel-96) +- [uv](#tab-panel-97) Terminal @@ -72227,8 +80343,8 @@ This beta also picks up two V1 releases on top -- [v1.103.0](https://github.com/ Install it the same way, pinning the exact pre-release version: -- [pip](#tab-panel-104) -- [uv](#tab-panel-105) +- [pip](#tab-panel-98) +- [uv](#tab-panel-99) Terminal @@ -72250,8 +80366,8 @@ The third V2 beta, forked from **v1.102.0**. There are no new V2 breaking change Install it the same way, pinning the exact pre-release version: -- [pip](#tab-panel-106) -- [uv](#tab-panel-107) +- [pip](#tab-panel-100) +- [uv](#tab-panel-101) Terminal @@ -72273,8 +80389,8 @@ The second V2 beta, forked from **v1.101.0**. There are no new V2 breaking chang Install it the same way, pinning the exact pre-release version: -- [pip](#tab-panel-108) -- [uv](#tab-panel-109) +- [pip](#tab-panel-102) +- [uv](#tab-panel-103) Terminal @@ -72292,7 +80408,7 @@ For the full breaking-change list and the recommended upgrade path, see the [v2. ### v2.0.0b1 (2026-05-20) -The first V2 beta, forked from **v1.100.0**, which deprecates most of what V2 removes. V2 leans into a harness-first design with [capabilities](/docs/ai/core-concepts/capabilities) as a core primitive: a single, composable unit that bundles an agent's tools, [hooks](/docs/ai/core-concepts/hooks), instructions, and model settings, reaching every layer of the agent through one concept. Many of V2's changes move configuration that used to be spread across `Agent` arguments onto that primitive, alongside the behavior changes that V1's stability guarantee didn't allow. Pydantic AI stays a small core: some capabilities ship with it, more come from the first-party [Pydantic AI Harness](/docs/ai/harness/overview), and others are third-party or your own. +The first V2 beta, forked from **v1.100.0**, which deprecates most of what V2 removes. V2 leans into a harness-first design with [capabilities](/docs/ai/core-concepts/capabilities) as a core primitive: a single, composable unit that bundles an agent's tools, [hooks](/docs/ai/core-concepts/hooks), instructions, and model settings, reaching every layer of the agent through one concept. Many of V2's changes move configuration that used to be spread across `Agent` arguments onto that primitive, alongside the behavior changes that V1's stability guarantee didn't allow. Pydantic AI stays a small core: some capabilities ship with it, more come from the first-party [Pydantic AI Harness](https://pydantic.dev/docs/ai/harness/), and others are third-party or your own. The breaking changes below are split into two groups: @@ -72575,7 +80691,7 @@ If the fix could reasonably go more than one way, or you're unsure it's actually ### Features, integrations, or API changes -Before writing code, ask whether the change needs to live in core at all. Most new agent behaviors belong in [**Pydantic AI Harness**](https://github.com/pydantic/pydantic-ai-harness), the official capability library -- not in this repo. Pydantic AI core is for the agent loop, model providers, and capabilities that require model-specific support or are fundamental to the agent experience. Standalone capabilities -- guardrails, memory, context management, file system access, etc. -- belong in the harness, where they can iterate faster. See [What goes where?](/docs/ai/harness/overview#what-goes-where) for the full distinction. +Before writing code, ask whether the change needs to live in core at all. Most new agent behaviors belong in [**Pydantic AI Harness**](https://github.com/pydantic/pydantic-ai-harness), the official capability library -- not in this repo. Pydantic AI core is for the agent loop, model providers, and capabilities that require model-specific support or are fundamental to the agent experience. Standalone capabilities -- guardrails, memory, context management, file system access, etc. -- belong in the harness, where they can iterate faster. See [What goes where?](https://pydantic.dev/docs/ai/harness/#what-goes-where) for the full distinction. **If your idea is a capability**, open an issue on [pydantic-ai-harness](https://github.com/pydantic/pydantic-ai-harness/issues) instead. You can also publish capabilities as your own package using the `pydantic-ai-` convention -- see [Publishing capability packages](/docs/ai/guides/extensibility#publishing-capability-packages). Once a capability has real users and a stable shape, we can talk about upstreaming to harness or core. @@ -72766,8 +80882,8 @@ The DuckDuckGo search tool allows you to search the web for information. It is b To use [`duckduckgo_search_tool`](/docs/ai/api/pydantic-ai/common_tools/#pydantic_ai.common_tools.duckduckgo.duckduckgo_search_tool), you need to install [`pydantic-ai-slim`](/docs/ai/overview/install#slim-install) with the `duckduckgo` optional group: -- [pip](#tab-panel-110) -- [uv](#tab-panel-111) +- [pip](#tab-panel-104) +- [uv](#tab-panel-105) Terminal @@ -72856,8 +80972,8 @@ The web fetch tool allows your agent to fetch the content of web pages and conve To use `web_fetch_tool`, you need to install [`pydantic-ai-slim`](/docs/ai/overview/install#slim-install) with the `web-fetch` optional group: -- [pip](#tab-panel-112) -- [uv](#tab-panel-113) +- [pip](#tab-panel-106) +- [uv](#tab-panel-107) Terminal @@ -72909,8 +81025,8 @@ The Tavily search tool allows you to search the web for information. It is built To use [`tavily_search_tool`](/docs/ai/api/pydantic-ai/common_tools/#pydantic_ai.common_tools.tavily.tavily_search_tool), you need to install [`pydantic-ai-slim`](/docs/ai/overview/install#slim-install) with the `tavily` optional group: -- [pip](#tab-panel-114) -- [uv](#tab-panel-115) +- [pip](#tab-panel-108) +- [uv](#tab-panel-109) Terminal @@ -73015,8 +81131,8 @@ Exa is a neural search engine that finds high-quality, relevant results across b To use Exa tools, you need to install [`pydantic-ai-slim`](/docs/ai/overview/install#slim-install) with the `exa` optional group: -- [pip](#tab-panel-116) -- [uv](#tab-panel-117) +- [pip](#tab-panel-110) +- [uv](#tab-panel-111) Terminal @@ -73202,6 +81318,10 @@ If whether a tool function requires approval depends on the tool call arguments To require approval for calls to tools provided by a [toolset](/docs/ai/tools-toolsets/toolsets) (like an [MCP server](/docs/ai/mcp/client)), see the [`ApprovalRequiredToolset` documentation](/docs/ai/tools-toolsets/toolsets#requiring-tool-approval). +Approval is not an authorization boundary against an untrusted client + +When you serve an agent over a [UI adapter](/docs/ai/integrations/ui/overview#trust-model-for-client-submitted-messages), the approval decision is submitted by the client along with the rest of the request, and the adapter has no server-side record of which tool calls it issued. A client that can reach the endpoint can approve a tool call of its own making. Human-in-the-loop approval protects against the _model_ acting without human sign-off; it does not replace authenticating the adapter endpoint and enforcing authorization for sensitive actions inside the tool function itself, which runs regardless of how the call entered the history. See [Trust model for client-submitted messages](/docs/ai/integrations/ui/overview#trust-model-for-client-submitted-messages). + When the model calls a tool that requires approval, the agent run will end with a [`DeferredToolRequests`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.DeferredToolRequests) output object with an `approvals` list holding [`ToolCallPart`s](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.ToolCallPart) containing the tool name, validated arguments, and a unique tool call ID. Once you've gathered the user's approvals or denials, you can build a [`DeferredToolResults`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.DeferredToolResults) object with an `approvals` dictionary that maps each tool call ID to a boolean, a [`ToolApproved`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.ToolApproved) object (with optional `override_args`), or a [`ToolDenied`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.ToolDenied) object (with an optional custom `message` to provide to the model). You can also provide a `metadata` dictionary on `DeferredToolResults` that maps each tool call ID to a dictionary of metadata that will be available in the tool's [`RunContext.tool_call_metadata`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.tool_call_metadata) attribute. This `DeferredToolResults` object can then be provided to one of the agent run methods as `deferred_tool_results`, alongside the original run's [message history](/docs/ai/core-concepts/message-history). @@ -73624,6 +81744,10 @@ Provider-adaptive capabilities For a higher-level, model-agnostic approach, consider the [provider-adaptive tool capabilities](/docs/ai/core-concepts/capabilities#provider-adaptive-tools): [`WebSearch`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.WebSearch), [`WebFetch`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.WebFetch), [`ImageGeneration`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.ImageGeneration), and [`MCP`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.MCP). These automatically use the model's native tool when supported and fall back to a local implementation, so your agent works across providers without code changes. +### Google tool combinations + +[Gemini 3 models](https://ai.google.dev/gemini-api/docs/structured-output#structured_outputs_with_tools) support combining native tools with function tools, including [output tools](/docs/ai/core-concepts/output#tool-output), and [`NativeOutput`](/docs/ai/api/pydantic-ai/output/#pydantic_ai.output.NativeOutput). Earlier Gemini models cannot use these combinations; use [`PromptedOutput`](/docs/ai/api/pydantic-ai/output/#pydantic_ai.output.PromptedOutput) for structured output alongside native tools. + ## Dynamic Configuration Sometimes you need to configure a native tool dynamically based on the [run context](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext) (e.g., user dependencies), or conditionally omit it. You can achieve this by wrapping a function with [`NativeTool`](/docs/ai/api/pydantic-ai/capabilities/#pydantic_ai.capabilities.NativeTool) in `capabilities`. The function takes [`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext) as an argument and returns an [`AbstractNativeTool`](/docs/ai/api/pydantic-ai/native_tools/#pydantic_ai.native_tools.AbstractNativeTool) or `None`. @@ -73700,13 +81824,13 @@ Google ✅ -No parameter support. No [`NativeToolCallPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.NativeToolCallPart) or [`NativeToolReturnPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.NativeToolReturnPart) is generated when streaming. Using native tools and function tools (including [output tools](/docs/ai/core-concepts/output#tool-output)) at the same time is not supported; to use structured output, use [`PromptedOutput`](/docs/ai/core-concepts/output#prompted-output) instead. +No parameter support. No [`NativeToolCallPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.NativeToolCallPart) or [`NativeToolReturnPart`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.NativeToolReturnPart) is generated when streaming. See [Google tool combinations](#google-tool-combinations). xAI ✅ -Supports `blocked_domains` and `allowed_domains` parameters. +Supports `blocked_domains`, `allowed_domains`, and `user_location` parameters. Groq @@ -73853,7 +81977,7 @@ OpenRouter ✅ -❌ +✅ ❌ @@ -73899,6 +82023,12 @@ Anthropic Domain Filtering With Anthropic, you can only use either `blocked_domains` or `allowed_domains`, not both. +Anthropic Web Search Tool Versions + +Pydantic AI does not expose a `dynamic_filtering` option. For Anthropic, Pydantic AI selects the web search tool version from the model profile and Anthropic client: `web_search_20260209` for models and platforms that support Anthropic's dynamic-filtering web tools, and `web_search_20250305` otherwise. The legacy Amazon Bedrock client does not support Anthropic web search, so Pydantic AI raises a `UserError` if you use `WebSearchTool` with `AsyncAnthropicBedrock`. On Vertex AI, `WebSearchTool` always uses `web_search_20250305`, as Anthropic does not offer the dynamic-filtering version there, so dynamic filtering is unavailable even on otherwise-supported models. See the [Anthropic web search docs](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool) and [tool reference](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference) for current model support and platform availability. + +Add [`CodeExecutionTool`](/docs/ai/api/pydantic-ai/native_tools/#pydantic_ai.native_tools.CodeExecutionTool) only when you want Anthropic's standalone code execution tool; it is not needed to use `web_search_20260209`. For Zero Data Retention behavior with `_20260209` web tools, see Anthropic's [server tools docs](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools#zdr-and-allowed-callers). + ## X Search Tool Tip @@ -73962,7 +82092,7 @@ _(This example is complete, it can be run "as is")_ Handle Filtering -You can only use one of `allowed_x_handles` or `excluded_x_handles`, not both. Each list is limited to 10 handles maximum. +You can only use one of `allowed_x_handles` or `excluded_x_handles`, not both. Each list is limited to 20 handles maximum. Including raw search results @@ -73990,7 +82120,7 @@ Google ✅ -Using native tools and function tools (including [output tools](/docs/ai/core-concepts/output#tool-output)) at the same time is not supported; to use structured output, use [`PromptedOutput`](/docs/ai/core-concepts/output#prompted-output) instead. +See [Google tool combinations](#google-tool-combinations). Anthropic @@ -74093,6 +82223,102 @@ assert isinstance(result.output, BinaryImage) _(This example is complete, it can be run "as is")_ +### File Uploads + +You can upload files via the provider's Files API and make them available to the code execution container. This allows the agent to process data files, analyze CSVs, work with images, and more. Files whose [`UploadedFile.provider_name`](/docs/ai/api/pydantic-ai/messages/#pydantic_ai.messages.UploadedFile.provider_name) does not match the model provider are ignored. + +#### Anthropic + +code\_execution\_with\_files\_anthropic.py + +```python +import asyncio + +import anthropic + +from pydantic_ai import Agent, CodeExecutionTool, UploadedFile +from pydantic_ai.capabilities import NativeTool + + +async def main(): + # Upload a file via the Anthropic Files API + client = anthropic.AsyncAnthropic() + with open('data.csv', 'rb') as f: + file = await client.beta.files.upload(file=('data.csv', f.read(), 'text/csv'), betas=['files-api-2025-04-14']) + + # Create an agent with CodeExecutionTool that has access to the uploaded file + agent = Agent( + 'anthropic:claude-sonnet-4-6', + capabilities=[NativeTool(CodeExecutionTool(files=[UploadedFile(file_id=file.id, provider_name='anthropic')]))], + ) + + result = await agent.run('Analyze the data.csv file and summarize the key statistics.') + print(result.output) + #> The CSV file contains 1000 rows with columns: name, age, salary... + + +asyncio.run(main()) +``` + +For details on file management, persistence, and container behavior, see the [Anthropic Files API documentation](https://platform.claude.com/docs/en/build-with-claude/files). + +#### OpenAI + +code\_execution\_with\_files\_openai.py + +```python +import asyncio + +from openai import AsyncOpenAI + +from pydantic_ai import Agent, CodeExecutionTool, UploadedFile +from pydantic_ai.capabilities import NativeTool + + +async def main(): + # Upload a file via the OpenAI Files API + client = AsyncOpenAI() + with open('data.csv', 'rb') as f: + file = await client.files.create(file=f, purpose='assistants') + + # Create an agent with CodeExecutionTool that has access to the uploaded file + agent = Agent( + 'openai-responses:gpt-5.2', + capabilities=[NativeTool(CodeExecutionTool(files=[UploadedFile(file_id=file.id, provider_name='openai')]))], + ) + + result = await agent.run('Analyze the data.csv file and summarize the key statistics.') + print(result.output) + #> The CSV file contains 1000 rows with columns: name, age, salary... + + +asyncio.run(main()) +``` + +For details on file management, container lifecycle, and persistence behavior, see the [OpenAI Responses API documentation](https://platform.openai.com/docs/api-reference/responses). + +#### Provider Support + +Parameter + +Anthropic + +OpenAI + +Google + +xAI + +`files` + +✅ + +✅ + +❌ + +❌ + ## Image Generation Tool Tip @@ -74395,7 +82621,7 @@ Google ✅ -No parameter support. The limits are fixed at 20 URLs per request with a maximum of 34MB per URL. Using native tools and function tools (including [output tools](/docs/ai/core-concepts/output#tool-output)) at the same time is not supported; to use structured output, use [`PromptedOutput`](/docs/ai/core-concepts/output#prompted-output) instead. +No parameter support. The limits are fixed at 20 URLs per request with a maximum of 34MB per URL. See [Google tool combinations](#google-tool-combinations). xAI @@ -74521,6 +82747,12 @@ Anthropic Domain Filtering With Anthropic, you can only use either `blocked_domains` or `allowed_domains`, not both. +Anthropic Web Fetch Tool Versions + +Pydantic AI does not expose a `dynamic_filtering` option. For Anthropic, Pydantic AI selects the web fetch tool version from the model profile and Anthropic client: `web_fetch_20260209` for models and platforms that support Anthropic's dynamic-filtering web tools, and `web_fetch_20250910` otherwise. `WebFetchTool` is unavailable on the legacy Amazon Bedrock and Vertex AI Anthropic clients, so Pydantic AI raises a `UserError` if you use it with `AsyncAnthropicBedrock` or `AsyncAnthropicVertex`. See the [Anthropic web fetch docs](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool) and [tool reference](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference) for current model support and platform availability. + +Add [`CodeExecutionTool`](/docs/ai/api/pydantic-ai/native_tools/#pydantic_ai.native_tools.CodeExecutionTool) only when you want Anthropic's standalone code execution tool; it is not needed to use `web_fetch_20260209`. For Zero Data Retention behavior with `_20260209` web tools, see Anthropic's [server tools docs](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools#zdr-and-allowed-callers). + ## Memory Tool The [`MemoryTool`](/docs/ai/api/pydantic-ai/native_tools/#pydantic_ai.native_tools.MemoryTool) enables your agent to use memory. @@ -74912,7 +83144,7 @@ Google (Gemini) ✅ -Requires files to be uploaded via the [Gemini Files API](https://ai.google.dev/gemini-api/docs/files). Files are automatically deleted after 48 hours. Supports up to 2 GB per file and 20 GB per project. Using native tools and function tools (including [output tools](/docs/ai/core-concepts/output#tool-output)) at the same time is not supported; to use structured output, use [`PromptedOutput`](/docs/ai/core-concepts/output#prompted-output) instead. +Requires files to be uploaded via the [Gemini Files API](https://ai.google.dev/gemini-api/docs/files). Files are automatically deleted after 48 hours. Supports up to 2 GB per file and 20 GB per project. See [Google tool combinations](#google-tool-combinations). xAI @@ -76151,7 +84383,7 @@ def my_flaky_tool(query: str) -> str: Raising `ModelRetry` also generates a `RetryPromptPart` containing the exception message, which is sent back to the LLM to guide its next attempt. Both `ValidationError` and `ModelRetry` respect the configured retry limit -- set per-tool via [`Tool(max_retries=N)`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.Tool) (or `@agent.tool(retries=N)`), per-toolset via [`FunctionToolset(max_retries=N)`](/docs/ai/api/pydantic-ai/toolsets/#pydantic_ai.toolsets.FunctionToolset), or agent-wide via [`Agent(retries={'tools': N})`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.__init__), applied in that order of precedence. -Tool retries are tracked **per tool**: every function tool has its own counter, with no global 'tool call' budget shared across the run. When a tool raises `ModelRetry` or its arguments fail validation, only that tool's counter advances. Inside a tool function, [`ctx.max_retries`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.max_retries) reflects that tool's enforcement limit and [`ctx.retry`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.retry) is that tool's own counter. When a tool exhausts its counter, the run raises [`UnexpectedModelBehavior`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.UnexpectedModelBehavior) with message `'Tool {name!r} exceeded max retries count of {N}'`. User-provided toolsets inherit `Agent(retries={'tools': ...})` as their default when no per-toolset value is set. +Tool retries are tracked **per tool**: every function tool has its own counter, with no global 'tool call' budget shared across the run. When a tool raises `ModelRetry` or its arguments fail validation, only that tool's counter advances. Inside a tool function, [`ctx.max_retries`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.max_retries) reflects that tool's enforcement limit and [`ctx.retry`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext.retry) is that tool's own counter. When a tool exhausts its counter, the run raises [`UnexpectedModelBehavior`](/docs/ai/api/pydantic-ai/exceptions/#pydantic_ai.exceptions.UnexpectedModelBehavior) with message `'Tool {name!r} exceeded max retries count of {N}. Consider raising the retry limit, or see the docs on tool retries: https://ai.pydantic.dev/tools-advanced/#tool-retries'`. User-provided toolsets inherit `Agent(retries={'tools': ...})` as their default when no per-toolset value is set. ### Tool Timeout @@ -76312,9 +84544,9 @@ You can cap tool executions within a run using [`UsageLimits(tool_calls_limit=.. #### Output Tool Calls -When a model calls an [output tool](/docs/ai/core-concepts/output#tool-output) in parallel with other tools, the agent's [`end_strategy`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.end_strategy) parameter controls how these tool calls are executed. The default `'graceful'` strategy ensures all function tools are executed even after a final result is found, while skipping remaining output tools. The `'exhaustive'` strategy goes further and also executes all output tools. Both are useful when tools have side effects (like logging, sending notifications, or updating metrics) that should always execute. +When a model produces a final result -- an [output tool](/docs/ai/core-concepts/output#tool-output) call, or structured [native](/docs/ai/core-concepts/output#native-output)/[prompted](/docs/ai/core-concepts/output#prompted-output) or [image](/docs/ai/core-concepts/output#image-output) output -- in parallel with other tools, the agent's [`end_strategy`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.end_strategy) parameter controls how these tool calls are executed. The default `'graceful'` strategy ensures all function tools are executed even after a final result is found, while skipping remaining output tools. The `'exhaustive'` strategy goes further and also executes all output tools. Both are useful when tools have side effects (like logging, sending notifications, or updating metrics) that should always execute. -For more information on how `end_strategy` works with both function tools and output tools, see [Parallel Output Tool Calls](/docs/ai/core-concepts/output#parallel-output-tool-calls). +For more information on how `end_strategy` works with function tools, output tools, and non-tool output, see [Tool calls alongside a final result](/docs/ai/core-concepts/output#parallel-output-tool-calls). ## Tool Search @@ -76445,7 +84677,7 @@ Same execution-mode handling as `'keywords'`: client-executed native on supporti The execution mode (server-executed, client-executed-native, or local fallback) is auto-derived from the chosen algorithm and the current provider -- users don't pick it directly. Native execution is preferred whenever available because it keeps the model-facing tool list stable across discovery rounds, which preserves Anthropic and OpenAI prompt caching. -To force the local `keywords` algorithm on a provider that natively supports tool search, override `ModelProfile.supported_builtin_tools` to exclude `ToolSearchTool` -- the capability then falls through to the local `search_tools` function tool. +To force the local `keywords` algorithm on a provider that natively supports tool search, override [`ModelProfile.supported_native_tools`](/docs/ai/api/pydantic-ai/profiles/#pydantic_ai.profiles.ModelProfile.supported_native_tools) to exclude `ToolSearchTool` -- the capability then falls through to the local `search_tools` function tool. Cross-provider history replay @@ -76482,7 +84714,7 @@ The toolsets that will be available during an agent run can be specified in four - at agent construction time, via the [`toolsets`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.__init__) keyword argument to `Agent`, which takes toolset instances as well as functions that generate toolsets [dynamically](#dynamically-building-a-toolset) based on the agent [run context](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext) - at agent run time, via the `toolsets` keyword argument to [`agent.run()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run), [`agent.run_sync()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run_sync), [`agent.run_stream()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.AbstractAgent.run_stream), or [`agent.iter()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.iter). These toolsets will be additional to those registered on the `Agent` - [dynamically](#dynamically-building-a-toolset), via the [`@agent.toolset`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.toolset) decorator which lets you build a toolset based on the agent [run context](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext) -- as a contextual override, via the `toolsets` keyword argument to the [`agent.override()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.iter) context manager. These toolsets will replace those provided at agent construction or run time during the life of the context manager +- as a contextual override, via the `toolsets` keyword argument to the [`agent.override()`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.override) context manager. These toolsets will replace those provided at agent construction or run time during the life of the context manager toolsets.py @@ -77145,7 +85377,7 @@ print(LOG) """ ``` -All docs examples are tested in CI and their their output is verified, so we need `LOG` to always have the same order whenever this code is run. Since the tools could finish in any order, we sleep an increasing amount of time based on which number tool call we are to ensure that they finish (and log) in the same order they were called in. +All docs examples are tested in CI and their output is verified, so we need `LOG` to always have the same order whenever this code is run. Since the tools could finish in any order, we sleep an increasing amount of time based on which number tool call we are to ensure that they finish (and log) in the same order they were called in. We use [`TestModel`](/docs/ai/api/models/test/#pydantic_ai.models.test.TestModel) here as it will automatically call each tool. @@ -77311,7 +85543,7 @@ _(This example is complete, it can be run "as is")_ ## Dynamically Building a Toolset -Toolsets can be built dynamically ahead of each agent run or run step using a function that takes the agent [run context](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext) and returns a toolset or `None`. This is useful when a toolset (like an MCP server) depends on information specific to an agent run, like its [dependencies](/docs/ai/core-concepts/dependencies). +Toolsets can be built dynamically ahead of each agent run or run step using a function that takes the agent [run context](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext) and returns a toolset or `None`. This is useful when a toolset (like an MCP server) depends on information specific to an agent run, like its [dependencies](/docs/ai/core-concepts/dependencies) -- for example to [connect to an MCP server with per-user credentials](/docs/ai/mcp/client#per-user-authentication). To register a dynamic toolset, you can pass a function that takes [`RunContext`](/docs/ai/api/pydantic-ai/tools/#pydantic_ai.tools.RunContext) to the `toolsets` argument of the `Agent` constructor, or you can wrap a compliant function in the [`@agent.toolset`](/docs/ai/api/pydantic-ai/agent/#pydantic_ai.agent.Agent.toolset) decorator. diff --git a/docs/vercel-ai-sdk-ui.txt b/docs/vercel-ai-sdk-ui.txt index 0f87c30..e20c5e8 100644 --- a/docs/vercel-ai-sdk-ui.txt +++ b/docs/vercel-ai-sdk-ui.txt @@ -176,6 +176,7 @@ - [Multistep Interfaces](/docs/advanced/multistep-interfaces) - [Sequential Generations](/docs/advanced/sequential-generations) - [Vercel Deployment Guide](/docs/advanced/vercel-deployment-guide) + - [Secure URL Fetching](/docs/advanced/secure-url-fetching) - [Reference](/docs/reference) - [AI SDK Core](/docs/reference/ai-sdk-core) - [generateText](/docs/reference/ai-sdk-core/generate-text) @@ -184,6 +185,7 @@ - [embedMany](/docs/reference/ai-sdk-core/embed-many) - [rerank](/docs/reference/ai-sdk-core/rerank) - [generateImage](/docs/reference/ai-sdk-core/generate-image) + - [experimental_streamTranscribe](/docs/reference/ai-sdk-core/stream-transcribe) - [transcribe](/docs/reference/ai-sdk-core/transcribe) - [generateSpeech](/docs/reference/ai-sdk-core/generate-speech) - [experimental_generateVideo](/docs/reference/ai-sdk-core/generate-video) @@ -671,12 +673,12 @@ When the user clicks the "Regenerate" button, the AI provider will regenerate th This feature is currently only available for React. By default, the `useChat` hook will trigger a render every time a new chunk is received. -You can throttle the UI updates with the `experimental_throttle` option. +You can throttle the UI updates with the `throttle` option. ```tsx filename="page.tsx" highlight="2-3" const { messages, ... } = useChat({ // Throttle the messages and data updates to 50ms: - experimental_throttle: 50 + throttle: 50 }) ``` @@ -1437,8 +1439,8 @@ By using `FileList`, you can send multiple files as attachments along with a mes Currently, only `image/*` and `text/*` content types get automatically converted into [multi-modal content - parts](/docs/foundations/prompts#multi-modal-messages). You will need to - handle other content types manually. + parts](/docs/foundations/prompts#file-parts). You will need to handle other + content types manually. ```tsx filename="app/page.tsx" @@ -1828,7 +1830,7 @@ When processing messages on the server that contain tool calls, custom metadata, When your messages include tool calls, validate them against your tool definitions: -```tsx filename="app/api/chat/route.ts" highlight="9-27,34-39" +```tsx filename="app/api/chat/route.ts" highlight="7-8,14-27,35-45" import { convertToModelMessages, createUIMessageStreamResponse, @@ -2018,7 +2020,7 @@ export default function Chat({ When loading messages from storage that contain tools, metadata, or custom data parts, validate them using `validateUIMessages` before processing (see the -[validation section](#validating-messages-from-database) above). +[validation section](#validating-messages-on-the-server) above). @@ -2091,11 +2093,20 @@ By default, message IDs are generated client-side: - AI response message IDs are generated by `streamText` on the server For applications without persistence, client-side ID generation works perfectly. -However, **for persistence, you need server-side generated IDs** to ensure consistency across sessions and prevent ID conflicts when messages are stored and retrieved. +However, **for persistence, you should use IDs that are stable before messages +are stored** to ensure consistency across sessions and prevent ID conflicts when +messages are restored. + +The server-side options below control IDs for generated assistant response +messages. User message IDs are created by `useChat` before the request is sent +to your API route, so keep those client-generated IDs when saving incoming +request messages, or generate and persist your own user message IDs before +sending/storing them. ### Setting Up Server-side ID Generation -When implementing persistence, you have two options for generating server-side IDs: +When implementing persistence, you have two options for generating server-side +IDs for assistant response messages: 1. **Using `generateMessageId` in `toUIMessageStream`** 2. **Setting IDs in your start message part with `createUIMessageStream`** @@ -2139,7 +2150,7 @@ export async function POST(req: Request) { Alternatively, you can use `createUIMessageStream` to control the message ID by writing a start message part: -```tsx filename="app/api/chat/route.ts" highlight="9-19" +```tsx filename="app/api/chat/route.ts" highlight="14-18,25-27" import { generateId, streamText, @@ -3705,12 +3716,12 @@ When the user clicks the "Stop" button, the fetch request will be aborted. This This feature is currently only available for React. By default, the `useCompletion` hook will trigger a render every time a new chunk is received. -You can throttle the UI updates with the `experimental_throttle` option. +You can throttle the UI updates with the `throttle` option. ```tsx filename="page.tsx" highlight="2-3" const { completion, ... } = useCompletion({ // Throttle the completion and data updates to 50ms: - experimental_throttle: 50 + throttle: 50 }) ``` @@ -4591,10 +4602,7 @@ Message metadata is ideal for: # Object Generation - - `useObject` is an experimental feature and only available in React, Svelte, - and Vue. - +`useObject` is only available in React, Svelte, and Vue. The [`useObject`](/docs/reference/ai-sdk-ui/use-object) hook allows you to create interfaces that represent a structured JSON object that is being streamed. @@ -4632,7 +4640,7 @@ Please note the code for handling `undefined` values in the JSX. ```tsx filename='app/page.tsx' 'use client'; -import { experimental_useObject as useObject } from '@ai-sdk/react'; +import { useObject } from '@ai-sdk/react'; import { notificationSchema } from './api/notifications/schema'; export default function Page() { @@ -4701,7 +4709,7 @@ When using `useObject` with enum output mode, your schema must be an object with ```tsx filename='app/classify/page.tsx' 'use client'; -import { experimental_useObject as useObject } from '@ai-sdk/react'; +import { useObject } from '@ai-sdk/react'; import { z } from 'zod'; export default function ClassifyPage() { @@ -4760,7 +4768,7 @@ purposes: ```tsx filename='app/page.tsx' highlight="6,13-20,24" 'use client'; -import { experimental_useObject as useObject } from '@ai-sdk/react'; +import { useObject } from '@ai-sdk/react'; export default function Page() { const { isLoading, object, submit } = useObject({ @@ -4797,7 +4805,7 @@ The `stop` function can be used to stop the object generation process. This can ```tsx filename='app/page.tsx' highlight="6,14-16" 'use client'; -import { experimental_useObject as useObject } from '@ai-sdk/react'; +import { useObject } from '@ai-sdk/react'; export default function Page() { const { isLoading, stop, object, submit } = useObject({ @@ -4842,7 +4850,7 @@ It can be used to display an error message, or to disable the submit button: ```tsx file="app/page.tsx" highlight="6,13" 'use client'; -import { experimental_useObject as useObject } from '@ai-sdk/react'; +import { useObject } from '@ai-sdk/react'; export default function Page() { const { error, object, submit } = useObject({ @@ -4881,7 +4889,7 @@ These callbacks can be used to trigger additional actions, such as logging, anal ```tsx filename='app/page.tsx' highlight="10-20" 'use client'; -import { experimental_useObject as useObject } from '@ai-sdk/react'; +import { useObject } from '@ai-sdk/react'; import { notificationSchema } from './api/notifications/schema'; export default function Page() { @@ -4978,11 +4986,11 @@ Here is a comparison of the supported functions across these frameworks: | | [useChat](/docs/reference/ai-sdk-ui/use-chat) | [useCompletion](/docs/reference/ai-sdk-ui/use-completion) | [useObject](/docs/reference/ai-sdk-ui/use-object) | [MCP Apps](/docs/ai-sdk-core/mcp-apps) | | --------------------------------------------------------------- | --------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------- | -------------------------------------- | -| React `@ai-sdk/react` | | | | | -| Vue.js `@ai-sdk/vue` | | | | | -| Svelte `@ai-sdk/svelte` | Chat | Completion | StructuredObject | | -| Angular `@ai-sdk/angular` | Chat | Completion | StructuredObject | | -| [SolidJS](https://github.com/kodehort/ai-sdk-solid) (community) | | | | | +| React `@ai-sdk/react` | | | | | +| Vue.js `@ai-sdk/vue` | | | | | +| Svelte `@ai-sdk/svelte` | Chat | Completion | StructuredObject | | +| Angular `@ai-sdk/angular` | Chat | Completion | StructuredObject | | +| [SolidJS](https://github.com/kodehort/ai-sdk-solid) (community) | | | | | ## Framework Examples @@ -5816,7 +5824,7 @@ export async function POST(req: Request) { const result = streamText({ model: __MODEL__, messages: await convertToModelMessages(messages), - onFinish() { + onEnd() { // 4. Update the same data part (reconciliation) writer.write({ type: 'data-weather', @@ -5848,7 +5856,7 @@ export async function POST(req: Request) { You can also send stream data from custom backends, e.g. Python / FastAPI, using the [UI Message Stream - Protocol](/docs/ai-sdk-ui/stream-protocol#ui-message-stream-protocol). + Protocol](/docs/ai-sdk-ui/stream-protocol#data-stream-protocol). ## Types of Streamable Data @@ -6463,11 +6471,11 @@ Here is a comparison of the supported functions across these frameworks: | | [useChat](/docs/reference/ai-sdk-ui/use-chat) | [useCompletion](/docs/reference/ai-sdk-ui/use-completion) | [useObject](/docs/reference/ai-sdk-ui/use-object) | [MCP Apps](/docs/ai-sdk-core/mcp-apps) | | --------------------------------------------------------------- | --------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------- | -------------------------------------- | -| React `@ai-sdk/react` | | | | | -| Vue.js `@ai-sdk/vue` | Chat | | | | -| Svelte `@ai-sdk/svelte` | Chat | Completion | StructuredObject | | -| Angular `@ai-sdk/angular` | Chat | Completion | StructuredObject | | -| [SolidJS](https://github.com/kodehort/ai-sdk-solid) (community) | | | | | +| React `@ai-sdk/react` | | | | | +| Vue.js `@ai-sdk/vue` | Chat | | | | +| Svelte `@ai-sdk/svelte` | Chat | Completion | StructuredObject | | +| Angular `@ai-sdk/angular` | Chat | Completion | StructuredObject | | +| [SolidJS](https://github.com/kodehort/ai-sdk-solid) (community) | | | | | [Contributions](https://github.com/vercel/ai/blob/main/CONTRIBUTING.md) are @@ -6484,6 +6492,7 @@ Here is a comparison of the supported functions across these frameworks: - [embedMany](/docs/reference/ai-sdk-core/embed-many) - [rerank](/docs/reference/ai-sdk-core/rerank) - [generateImage](/docs/reference/ai-sdk-core/generate-image) + - [experimental_streamTranscribe](/docs/reference/ai-sdk-core/stream-transcribe) - [transcribe](/docs/reference/ai-sdk-core/transcribe) - [generateSpeech](/docs/reference/ai-sdk-core/generate-speech) - [experimental_generateVideo](/docs/reference/ai-sdk-core/generate-video) @@ -8733,7 +8742,7 @@ Allows you to easily create a conversational user interface for your chatbot app 'Optional callback function that is called when a data part is received.', }, { - name: 'experimental_throttle', + name: 'throttle', type: 'number', isOptional: true, description: @@ -9025,7 +9034,7 @@ Allows you to create text completion based capabilities for your application. It 'Optional. A custom fetch function to be used for the API call. Defaults to the global fetch function.', }, { - name: 'experimental_throttle', + name: 'throttle', type: 'number', isOptional: true, description: @@ -9120,12 +9129,9 @@ Allows you to create text completion based capabilities for your application. It -# `experimental_useObject()` +# `useObject()` - - `useObject` is an experimental feature and only available in React, Svelte, - and Vue. - +`useObject` is only available in React, Svelte, and Vue. Allows you to consume text streams that represent a JSON object and parse them into a complete object based on a schema. You can use it together with [`streamText`](/docs/reference/ai-sdk-core/stream-text) and [`Output.object()`](/docs/reference/ai-sdk-core/output#output-object) in the backend. @@ -9133,7 +9139,7 @@ You can use it together with [`streamText`](/docs/reference/ai-sdk-core/stream-t ```tsx 'use client'; -import { experimental_useObject as useObject } from '@ai-sdk/react'; +import { useObject } from '@ai-sdk/react'; export default function Page() { const { object, submit } = useObject({ @@ -9155,21 +9161,21 @@ export default function Page() { diff --git a/frontend/package.json b/frontend/package.json index 61fb1f4..fcf29e1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,52 +11,52 @@ "generate-client": "node api/scripts/modifyOpenApiFile.mjs && openapi-ts && pnpm run format" }, "dependencies": { - "@ai-sdk/react": "^4.0.3", - "@radix-ui/react-alert-dialog": "^1.1.17", - "@radix-ui/react-collapsible": "^1.1.14", - "@radix-ui/react-dialog": "^1.1.17", - "@radix-ui/react-dropdown-menu": "^2.1.18", + "@ai-sdk/react": "^4.0.32", + "@radix-ui/react-alert-dialog": "^1.1.19", + "@radix-ui/react-collapsible": "^1.1.16", + "@radix-ui/react-dialog": "^1.1.19", + "@radix-ui/react-dropdown-menu": "^2.1.20", "@radix-ui/react-icons": "^1.3.2", - "@radix-ui/react-separator": "^1.1.10", + "@radix-ui/react-separator": "^1.1.11", "@radix-ui/react-slot": "^1.3.0", - "@radix-ui/react-tabs": "^1.1.15", - "@radix-ui/react-tooltip": "^1.2.10", - "@tanstack/react-query": "^5.101.1", + "@radix-ui/react-tabs": "^1.1.17", + "@radix-ui/react-tooltip": "^1.2.12", + "@tanstack/react-query": "^5.101.2", "@wavesurfer/react": "^1.0.12", - "ai": "^7.0.2", + "ai": "^7.0.29", "axios": "^1.18.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.4.0", "fast-deep-equal": "^3.1.3", "geist": "^1.7.2", - "lucide-react": "^1.21.0", - "motion": "^12.42.0", - "next": "16.2.9", + "lucide-react": "^1.24.0", + "motion": "^12.42.2", + "next": "16.2.10", "next-themes": "^0.4.6", - "radix-ui": "^1.6.0", + "radix-ui": "^1.6.2", "react": "^19.2.7", "react-dom": "^19.2.7", - "recharts": "^3.9.0", + "recharts": "^3.9.2", "sonner": "^2.0.7", "streamdown": "^2.5.0", "tailwind-merge": "^3.6.0", "tailwindcss-animate": "^1.0.7", "use-stick-to-bottom": "^1.1.6", "usehooks-ts": "^3.1.1", - "wavesurfer.js": "^7.12.8" + "wavesurfer.js": "^7.12.10" }, "devDependencies": { - "@biomejs/biome": "2.5.1", + "@biomejs/biome": "2.5.4", "@hey-api/openapi-ts": "^0.99.0", - "@tailwindcss/postcss": "^4.3.1", - "@types/node": "^26.0.1", + "@tailwindcss/postcss": "^4.3.3", + "@types/node": "^26.1.1", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", - "postcss": "^8.5.15", - "tailwindcss": "^4.3.1", - "typescript": "^6.0.3", - "ultracite": "7.8.3" + "postcss": "^8.5.19", + "tailwindcss": "^4.3.3", + "typescript": "^7.0.2", + "ultracite": "7.9.4" }, "packageManager": "pnpm@9.12.3" } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 1b73ae6..327760c 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -9,44 +9,44 @@ importers: .: dependencies: '@ai-sdk/react': - specifier: ^4.0.3 - version: 4.0.3(react@19.2.7)(zod@4.4.3) + specifier: ^4.0.32 + version: 4.0.32(react@19.2.7)(zod@4.4.3) '@radix-ui/react-alert-dialog': - specifier: ^1.1.17 - version: 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^1.1.19 + version: 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-collapsible': - specifier: ^1.1.14 - version: 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^1.1.16 + version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-dialog': - specifier: ^1.1.17 - version: 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^1.1.19 + version: 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-dropdown-menu': - specifier: ^2.1.18 - version: 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^2.1.20 + version: 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-icons': specifier: ^1.3.2 version: 1.3.2(react@19.2.7) '@radix-ui/react-separator': - specifier: ^1.1.10 - version: 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^1.1.11 + version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': specifier: ^1.3.0 version: 1.3.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-tabs': - specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^1.1.17 + version: 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-tooltip': - specifier: ^1.2.10 - version: 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^1.2.12 + version: 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tanstack/react-query': - specifier: ^5.101.1 - version: 5.101.1(react@19.2.7) + specifier: ^5.101.2 + version: 5.101.2(react@19.2.7) '@wavesurfer/react': specifier: ^1.0.12 - version: 1.0.12(react@19.2.7)(wavesurfer.js@7.12.8) + version: 1.0.12(react@19.2.7)(wavesurfer.js@7.12.10) ai: - specifier: ^7.0.2 - version: 7.0.2(zod@4.4.3) + specifier: ^7.0.29 + version: 7.0.29(zod@4.4.3) axios: specifier: ^1.18.1 version: 1.18.1 @@ -64,22 +64,22 @@ importers: version: 3.1.3 geist: specifier: ^1.7.2 - version: 1.7.2(next@16.2.9(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)) + version: 1.7.2(next@16.2.10(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)) lucide-react: - specifier: ^1.21.0 - version: 1.21.0(react@19.2.7) + specifier: ^1.24.0 + version: 1.24.0(react@19.2.7) motion: - specifier: ^12.42.0 - version: 12.42.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^12.42.2 + version: 12.42.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7) next: - specifier: 16.2.9 - version: 16.2.9(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: 16.2.10 + version: 16.2.10(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) radix-ui: - specifier: ^1.6.0 - version: 1.6.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^1.6.2 + version: 1.6.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: specifier: ^19.2.7 version: 19.2.7 @@ -87,8 +87,8 @@ importers: specifier: ^19.2.7 version: 19.2.7(react@19.2.7) recharts: - specifier: ^3.9.0 - version: 3.9.0(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react-is@19.2.5)(react@19.2.7)(redux@5.0.1) + specifier: ^3.9.2 + version: 3.9.2(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react-is@19.2.5)(react@19.2.7)(redux@5.0.1) sonner: specifier: ^2.0.7 version: 2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -100,7 +100,7 @@ importers: version: 3.6.0 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@4.3.1) + version: 1.0.7(tailwindcss@4.3.3) use-stick-to-bottom: specifier: ^1.1.6 version: 1.1.6(react@19.2.7) @@ -108,21 +108,21 @@ importers: specifier: ^3.1.1 version: 3.1.1(react@19.2.7) wavesurfer.js: - specifier: ^7.12.8 - version: 7.12.8 + specifier: ^7.12.10 + version: 7.12.10 devDependencies: '@biomejs/biome': - specifier: 2.5.1 - version: 2.5.1 + specifier: 2.5.4 + version: 2.5.4 '@hey-api/openapi-ts': specifier: ^0.99.0 - version: 0.99.0(typescript@6.0.3) + version: 0.99.0(typescript@7.0.2) '@tailwindcss/postcss': - specifier: ^4.3.1 - version: 4.3.1 + specifier: ^4.3.3 + version: 4.3.3 '@types/node': - specifier: ^26.0.1 - version: 26.0.1 + specifier: ^26.1.1 + version: 26.1.1 '@types/react': specifier: ^19.2.17 version: 19.2.17 @@ -130,44 +130,44 @@ importers: specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.17) postcss: - specifier: ^8.5.15 - version: 8.5.15 + specifier: ^8.5.19 + version: 8.5.19 tailwindcss: - specifier: ^4.3.1 - version: 4.3.1 + specifier: ^4.3.3 + version: 4.3.3 typescript: - specifier: ^6.0.3 - version: 6.0.3 + specifier: ^7.0.2 + version: 7.0.2 ultracite: - specifier: 7.8.3 - version: 7.8.3 + specifier: 7.9.4 + version: 7.9.4(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) packages: - '@ai-sdk/gateway@4.0.2': - resolution: {integrity: sha512-Jz1BiiTSvhDsCBJrkFRSqLHDRMVjFtYk9GdbSi3UOqY+/epza+oIESMDzfN4m+YHT/1IYmNEmxaMfjXOvxKDjQ==} + '@ai-sdk/gateway@4.0.21': + resolution: {integrity: sha512-GafyeyHFDKJjdtbyhCQ0aC2FORdyE56IxK45EZ1JLO1iVdg8XqEU3bwnRzI0+5S1XHV7W2qQsxkZnGYPbsFiXA==} engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/mcp@2.0.1': - resolution: {integrity: sha512-tsk+FAX7TN8BsA3eqwR7jf33PiYaGh+w/rHofk7hj13mFvicAhrdITgSOvncULUReUZcDoS0axHEXv1y1/yR8Q==} + '@ai-sdk/mcp@2.0.14': + resolution: {integrity: sha512-Vf2THTWylnOiPUZgPEzTcbin+6pIBKMdJnDNAMHD0u+upNoO5747HMMFUJJnNNjk8eroAPomaVmF0w8gadA4WA==} engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@5.0.0': - resolution: {integrity: sha512-zj66M02jc6ASYwIgWZowsooDUwaVngeNZQ3H10GwcPMZ+KR6gHMhcUuKl6tkai+JPXTKDyHY1pnszuxRtw2D4A==} + '@ai-sdk/provider-utils@5.0.10': + resolution: {integrity: sha512-uPyec0+85dwxZYXtb8qe8gCjhjDfxP4LCDo/uRQS/iG+FIgYbHPRhr/ys281udG90bTaE18+5cxWraYaf8oHCw==} engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider@4.0.0': - resolution: {integrity: sha512-fr9Gs89prDWiuox/T+kCA+i2cJkHpxU5S+tr4megjTzRC27ZsvFhwjU/+XrqqMbvBUlfmXxTOYWy8ng45dsjIg==} + '@ai-sdk/provider@4.0.3': + resolution: {integrity: sha512-e0CpNWJUY7OxAFAnCZkw+ri9QOHWwTs1tXP42782KFGCU07qt8NiXCrCVowyCB5dP2r5/Uls+g2oPd8kOJn9dw==} engines: {node: '>=22'} - '@ai-sdk/react@4.0.3': - resolution: {integrity: sha512-t06aGX/d87FJFmidEmxqkGiKXS1LSTDot4GWPK6TrIPTA1rDnwD6buCnhdE7ybr6ehUfmMjhY2O4I8P0MKqeZg==} + '@ai-sdk/react@4.0.32': + resolution: {integrity: sha512-H1tu1C5XoY1fh14Q4ABS1ljPx+H/y20MqJrUhATW5jjdAqOyyAy0rFl2qj5BC9uhFMtM+NkxEUOEPB5qeQLDQQ==} engines: {node: '>=22'} peerDependencies: react: ^18 || ~19.0.1 || ~19.1.2 || ^19.2.1 @@ -179,55 +179,55 @@ packages: '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@biomejs/biome@2.5.1': - resolution: {integrity: sha512-IXWLCxKmae+rI7LOHS1B3EbVisQ6GRAWbhN9msa6KjNCyFWrvKZWR4oUdinaNssrV852OrSHuSPa95h1GPJc7Q==} + '@biomejs/biome@2.5.4': + resolution: {integrity: sha512-xy5FNE5kQJKyK5MR1gJy6ztXYx4WBAbYGlK04lMEgmyPRWKybY9NFwiG9yo0XdzOU8Xvhj41u034J1ywfoWfMw==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.5.1': - resolution: {integrity: sha512-npqDzvqv7vFaWRiNN1Te71siRgPaqS9MpqgYCdP/CrUbkJ7ApezaeaKjueKHRN/JH/6lRjJQAHi8acQDCAz22w==} + '@biomejs/cli-darwin-arm64@2.5.4': + resolution: {integrity: sha512-4o3NFRobXHynkgcFVrlZsoDAFtF2ldlEGN8sORSws5ZQqyY4PXnPUIylu4ksfyHuwkfvDREuWh3JK+niRwGq3w==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.5.1': - resolution: {integrity: sha512-RgwTqPAM8g2tn1j+b5oRjF/DbSBX8a4gwojtuG9XuhfK7GgomvZ9+T+tqjXiVbjLEeGJOoL6VEk8mvRTVeSybw==} + '@biomejs/cli-darwin-x64@2.5.4': + resolution: {integrity: sha512-D32P5HkU2Y6PySuC/WsVDTOgsDwVFmujzhhhOQjajtATpVWFDXuVd3oRbsWNSEA+aaFzyzZm22szsyydBYlSyQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.5.1': - resolution: {integrity: sha512-WMcvMLgByyTqVxGlq918NBBYliq9FRR9GAQVETHb+VjGVqXCZFfHlZHC1FX4ibuYY/Hg6TJE3rHU0xVrdJXNRw==} + '@biomejs/cli-linux-arm64-musl@2.5.4': + resolution: {integrity: sha512-Rpm5/AT1m+DlJmUoYvS4/vXc+0tXJPJ2NQz25TGPyHVF5JrWy75PE0GH6kVxsKtQDuCH4OgzquZq0R4kj/wCVg==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-arm64@2.5.1': - resolution: {integrity: sha512-yhV35CzZh38VyMvTEXi3JTjxZBs++oCKK9KG8vB6VI5+uvQvZNR3BFWEKKzuOmx9DJJj7sQpZ4LQJcmbGTs3+Q==} + '@biomejs/cli-linux-arm64@2.5.4': + resolution: {integrity: sha512-pSEfW7B8kTsXUjUxC1xVVK+y85Ht3C5XxZ9gclmC7/3Ku9Vqz8jmI7k0p/BNIjQ6t4sFERI2sFeH73ybiZl6YQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-x64-musl@2.5.1': - resolution: {integrity: sha512-ANTowtlLmPYm5yeMckWY8Xzb9Ix+JJP3tgHR/n6xRj1VWyIzzWtfRfih9hv9VmClwadpBvZduISZIbBsIlYG3A==} + '@biomejs/cli-linux-x64-musl@2.5.4': + resolution: {integrity: sha512-aby/PohmmgbShcHqFsZVzG8H6D98+P+A6xRWRrQcLW1pCjabcov5UUlke4UqNQBYTkDQav+jB4zyyDDeKB2GaA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-linux-x64@2.5.1': - resolution: {integrity: sha512-J/7uHSX7NfoYDI7HijAkd8lnQIOrRb2W7j3X+tw4R+N5ExvXGsyXFiGdQcfcxfOmNQmZVSQOCDk757fwpzqQcg==} + '@biomejs/cli-linux-x64@2.5.4': + resolution: {integrity: sha512-FNxojWJkL7EajAuzBgoLe0T2G0y112M4lBrDIFl/DomFTx8yqenYOIdsRLNXvOvBBofE8hJi85LjzLmBDpY7/Q==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-win32-arm64@2.5.1': - resolution: {integrity: sha512-zgXnKNgWPC4iPF7Y1lR3STUeCUuZRpD6IiOrC7TZTlh0Lx6FiVUT05myuMQHQ9D+1cc7uyMldi4forE6lp0ivQ==} + '@biomejs/cli-win32-arm64@2.5.4': + resolution: {integrity: sha512-emoXexPZIPAZkz2RKmA95WJUqK3I5MJNYtwEbL5ESciRzhmFMMyekDhNG8hpeOaK+ZGRDxAU4wvGuA5IHQ0h0w==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.5.1': - resolution: {integrity: sha512-6uxpR9hvaglANkZemeSiN/FhYgkGasrEGn267eXIWvjrjJ2LhDlk251IhjVJq6MXzkV2/bcXwLwSroLyPtqRZg==} + '@biomejs/cli-win32-x64@2.5.4': + resolution: {integrity: sha512-U1jaluLw1qQc2Tx7/CeSoL9N5XcqIH+GWjpUAy1ouB5nVjSCMNO+NNHdY3RAs8zxNurLWAdj6pehQdCA2zyU+Q==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] @@ -238,31 +238,61 @@ packages: '@chevrotain/types@11.1.2': resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==} - '@clack/core@1.4.2': - resolution: {integrity: sha512-0Ty/1Gfm+Kb07sXcuESjyKfwEhSy4Ns1AgeEisHb/bDY5fWme0tTeTkU14T1Gmcs17YIjB/teiDe4uaCghbYqQ==} + '@clack/core@1.4.3': + resolution: {integrity: sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==} engines: {node: '>= 20.12.0'} - '@clack/prompts@1.6.0': - resolution: {integrity: sha512-EYlRokl8szrP9Z25qT5aepMdBjzBvHF9ZEhzIiUBc9guz/T31EqRgvD0QSgZcpE93xiwrr+OkB4nz0BZyF6fSA==} + '@clack/prompts@1.7.0': + resolution: {integrity: sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==} engines: {node: '>= 20.12.0'} - '@emnapi/runtime@1.11.1': - resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + '@emnapi/runtime@1.11.2': + resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.23.5': + resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@floating-ui/core@1.7.5': - resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + '@eslint/config-helpers@0.6.0': + resolution: {integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@floating-ui/dom@1.7.6': - resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@floating-ui/react-dom@2.1.8': - resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} + '@eslint/object-schema@3.0.5': + resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/plugin-kit@0.7.2': + resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/react-dom@2.1.9': + resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' - '@floating-ui/utils@0.2.11': - resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} '@hey-api/codegen-core@0.9.1': resolution: {integrity: sha512-s97jL1dgTMuiMHv2BZ1X4Tgd99Mf9GOvGdNqNcGwIMmnR+PgYNoraj4Zvp134MKsNCap/m7k0r0vKKnl56pj4w==} @@ -289,11 +319,31 @@ packages: '@hey-api/types@0.1.4': resolution: {integrity: sha512-thWfawrDIP7wSI9ioT13I5soaaqB5vAPIiZmgD8PbeEVKNrkonc0N/Sjj97ezl7oQgusZmaNphGdMKipPO6IBg==} + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - '@iconify/utils@3.1.3': - resolution: {integrity: sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw==} + '@iconify/utils@3.1.4': + resolution: {integrity: sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==} '@img/colour@1.1.0': resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} @@ -458,53 +508,53 @@ packages: '@mermaid-js/parser@1.2.0': resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==} - '@next/env@16.2.9': - resolution: {integrity: sha512-ki5VxxXfzD/9TDe13wyeTKIjQTAwBVpnr8KhRDUr8ltMUq1/NBpWNT5tiPoxiGl+PHM4X2ahSOiPk6iAimIzPg==} + '@next/env@16.2.10': + resolution: {integrity: sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==} - '@next/swc-darwin-arm64@16.2.9': - resolution: {integrity: sha512-HkfxNYUCmcct0Xsqib5KxqMSHV4AHJq857BNRchyBDs4YS19aHzVfn1kDuBYKqLLQBjXgnkIsjV2Kd4d2wzYhw==} + '@next/swc-darwin-arm64@16.2.10': + resolution: {integrity: sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.2.9': - resolution: {integrity: sha512-7IAtK4MeybpqRV9GRABWEhJ62mOS+rzWOzOTFie4cSEtm12xsoOMJRcECoZx3FHPzFAqN/IJtHqWAFOLfl152w==} + '@next/swc-darwin-x64@16.2.10': + resolution: {integrity: sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.2.9': - resolution: {integrity: sha512-hBD75iWpUtkL9SmQmcRhmLomn9jgkPzCEkbOcLgHymPEKzv+6ONy13RRiIEz/iEObjkS2Jlb5gYS2XGoS3X4rw==} + '@next/swc-linux-arm64-gnu@16.2.10': + resolution: {integrity: sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@16.2.9': - resolution: {integrity: sha512-qZTI3pf9SGc/obr8NkQAekBxmp1QK+kVm+VAf3BALLfFAj+1kUhkTxmrWpVos9R/UYIA8AWX2p6cGI5WdwzVUA==} + '@next/swc-linux-arm64-musl@16.2.10': + resolution: {integrity: sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@16.2.9': - resolution: {integrity: sha512-xm0HfRNX+UkH4R3c18ynswjj5o5uEj/7iI9p9omdtTSIsRCzQqkGMA+10nzJ4EHnYC3as65IMhbbl5fWRUWHYg==} + '@next/swc-linux-x64-gnu@16.2.10': + resolution: {integrity: sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@16.2.9': - resolution: {integrity: sha512-QumimHkGEG6vM3PfEDWKyKen03NcqLOkeKB1EfcPe7VxzmEiCa4jNnMyBn/US5zcd/VE1CI+O8Ovb3lfjVHfGw==} + '@next/swc-linux-x64-musl@16.2.10': + resolution: {integrity: sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@16.2.9': - resolution: {integrity: sha512-hzQpKZvw8rAwI6A2uQh6SacCSvNAXaIkPNsWwzqqfRiIMiXMfH936skDhz1OO6KpvdKkJrgHHtqQOq5PIXOvdQ==} + '@next/swc-win32-arm64-msvc@16.2.10': + resolution: {integrity: sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.2.9': - resolution: {integrity: sha512-qr2VL3Ce5QrwgO2yh1ujSBawrimjVKX8FGF/cOynmdYKJY0BdHpGVNIRK1tqONB10Vkm25Ub1BD2bkjWs4+96w==} + '@next/swc-win32-x64-msvc@16.2.10': + resolution: {integrity: sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -516,11 +566,11 @@ packages: '@radix-ui/number@1.1.2': resolution: {integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==} - '@radix-ui/primitive@1.1.4': - resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==} + '@radix-ui/primitive@1.1.5': + resolution: {integrity: sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg==} - '@radix-ui/react-accessible-icon@1.1.10': - resolution: {integrity: sha512-TraSwZUqTcVbiDV2/RXzAXC7aeVVXchq0daPFZE7zAxYFaMzjOUggLOfQH9KFLgRizuwVKZO/crveV1eeO3/ZQ==} + '@radix-ui/react-accessible-icon@1.1.11': + resolution: {integrity: sha512-HQDOFTKwSnmUij6l54wYJJtxTAnxI71+YJLOrjm2ladFB8HAV5Jt7hwaZPhWTGBkYoW4+ZAOfNZrLDh/qvxSYA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -532,8 +582,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-accordion@1.2.14': - resolution: {integrity: sha512-iE8YB9nmTBH8zd73ofBISZ8JCzgMoMkATJr7qDwa6u5F1+7mTM81V6fa71jgZ65rpjVpecDf1vSnwIFP9Ly1zw==} + '@radix-ui/react-accordion@1.2.16': + resolution: {integrity: sha512-BpZJNmetujnGgUI6OX0jEhEmlA46WPqgub8Rv09Kyquwd0cc1ndMKpiPYCjmBU6KSSRPAMtgLpEoZSG/tdNIWQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -545,8 +595,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-alert-dialog@1.1.17': - resolution: {integrity: sha512-563ygGeyWPrxyVCNp7OV4rE2aIXhFPknpFyo4wbDlcyMMPZ6ySh+zC5WTvY0ZFLgPTg/QB6tA8PyDQyJ2b4cPg==} + '@radix-ui/react-alert-dialog@1.1.19': + resolution: {integrity: sha512-FA7n1f6D/DwGE0+AWxiY5LacNbbExQuEgMubeG06idEaH+mSLuf9dp/qBNqOnvbTQ+4gZ2ue1RATF1Ub91Mg5g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -558,8 +608,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-arrow@1.1.10': - resolution: {integrity: sha512-j2VTDz1vgCsmuG0k5lBfOcM8n5JPFqZBcMryasFjHYMhwxYL5SRUV5lMSUpRdNtw3D/Sv8pzJtrlAgkssYSsQQ==} + '@radix-ui/react-arrow@1.1.11': + resolution: {integrity: sha512-Kdil9BB1rIFC/khmf4hC35bn8701AJcizTU7G7cUbEbk5XqqbjDuHW60uUfKqO5WojjZcbAW51Q7P0hRmMLw8A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -571,8 +621,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-aspect-ratio@1.1.10': - resolution: {integrity: sha512-kbI7NrqhDeuytYrq7JjAsoXczvL8wgj2tc1MyaYWm+50bMKHCHQtVWCryslx4cCpmCTTkBcwQckE4CmmGV2haQ==} + '@radix-ui/react-aspect-ratio@1.1.11': + resolution: {integrity: sha512-IUAhIVpBUvP5NNICjlaB1OFmtRLGqQqTF3ZOSGPoq3XeLXRFtHiWTRxSVEULgOd9GQR2c7tsYqDnhUennapZnw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -584,8 +634,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-avatar@1.2.0': - resolution: {integrity: sha512-am/CwltXtmtdtP+5FbYblYDnMa/zuKcMJP1i3/SJMDXXfj2mG+BTqLH2wucqeyyiQMursUtg/5cK+Nh2pCaSOA==} + '@radix-ui/react-avatar@1.2.2': + resolution: {integrity: sha512-sST0qh8GzOB7besQ3tMLWLyngnRuSk0gc/Hm+667KYKQFCt6Y6ZXv25WlqM7dIDK54ULCh5+CHmk4LIolzfz+A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -597,8 +647,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-checkbox@1.3.5': - resolution: {integrity: sha512-pREzrmNnVwGvYaBoM64huTRK7B3lrTRuwj8A9nwhPiEtMb+yudiWh6zWAqEtP0Dzd5+iBa1Ki7V1pCxV8ExMdA==} + '@radix-ui/react-checkbox@1.3.7': + resolution: {integrity: sha512-JroKHfQBfh+fDuzpPsBC+pESkhuq8ql4hljTguz8MWnS35cISr3d/Jhl9kYrB44FlDtxCArYdDvTx+BSsJ64rQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -610,8 +660,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collapsible@1.1.14': - resolution: {integrity: sha512-9bT+FvifX1FK2Mj6UEsTdyu0cN3JaA3KdfhaBao+ONrYFy/pyOy3TU1TNw7iOk1o+0hOEq67RojlUUmoFGwxyA==} + '@radix-ui/react-collapsible@1.1.16': + resolution: {integrity: sha512-opfXRe6nnzyGmCDPx+l1Aqo/RbqWtQal2FnsBqF9hhePp6j0LsRoBaRxcMOlTv+uYTJVtWYZKg9t9wTe+BA/ZA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -623,8 +673,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.10': - resolution: {integrity: sha512-IVVz4EvBcKjrzKgof714qDnz/SzQAkLA2Emh5edlHbgcE6fNd3Un6CJLlaYcnm8N4JmAtzQgse4dOKxcD2yc9g==} + '@radix-ui/react-collection@1.1.12': + resolution: {integrity: sha512-nb67INpE0IahJKN7EYPp9m9YGwYeKlnzxT3MwXVkgCskaSJia97kG4T0ywpjNUSSnoJk/uvk12V8vbrEHEj+/Q==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -645,8 +695,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-context-menu@2.3.1': - resolution: {integrity: sha512-XbrxS68W5dyiE4fAb96yvJwSVU5x66B20A99sD5Mk3xSWK/LqeOnx6TZnim1KieMjXS/CTFq8reOAjWxas2G8Q==} + '@radix-ui/react-context-menu@2.3.3': + resolution: {integrity: sha512-PS+gKE0z2prJ74Y0sM+brAGK4mYOHIR7TlcV5EJgUQ6E0xMvyswkK2X4yRqyganrzsRL+WCSKAPu0NQITICRWg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -658,8 +708,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-context@1.1.4': - resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==} + '@radix-ui/react-context@1.2.0': + resolution: {integrity: sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -667,8 +717,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-dialog@1.1.17': - resolution: {integrity: sha512-TDTYmpdq8dI2+Xgvgj9AJ8Ghqq+Eph/TRVEdaFQPDItIY+6QSkU7MJMeevw1568Yw/2Ijz8BTphPSP2XejKphw==} + '@radix-ui/react-dialog@1.1.19': + resolution: {integrity: sha512-+HhbN2+YtkRgVirjZ2afMeutQRuGOrdkWR5+EFC58SJojGmtyNQwYzgi6tHBpOxvFHefMtPeHdgtjz0BOGxFQg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -689,8 +739,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-dismissable-layer@1.1.13': - resolution: {integrity: sha512-2v+zNAWWe0ySxgC0D0yeXMPQ23xZVgXZTerTz+JKlmdRj6gfTqmCcR29jb6d290DezXPGgruHWDX/vYUebtErg==} + '@radix-ui/react-dismissable-layer@1.1.15': + resolution: {integrity: sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -702,8 +752,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.18': - resolution: {integrity: sha512-PZGV82gFk0WltDRI//SsG28ZIjlo9ANTmoNYg0jLNzXXiDsAy5PkOOYQaVD1pPxY6t7gxffb1QMD6qaUvsBZdw==} + '@radix-ui/react-dropdown-menu@2.1.20': + resolution: {integrity: sha512-slfm+rRaZRuQBvHq60lXvSVUPhid0IPtjSZzIuUlWZMUs01iYZNlGS3mJgRD3ChLQVBAYlKiL/tFyWGX+dz8Xw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -724,8 +774,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-focus-scope@1.1.10': - resolution: {integrity: sha512-Fas/lXQqhVvqwAb64s5RFeHiHYElZ6SUQbZaNd6EkfhP/Al7wTIQ9WIR4QVX475tlu5yFCEdDcJH6/UwsZjMWw==} + '@radix-ui/react-focus-scope@1.1.12': + resolution: {integrity: sha512-jjk/lqTeNL0azUx5ZYzVrl4NgaDIrdzTNE4mABV9yBFI7FQqN7pIgzV1bTleUezP2QiTGA1BFTqY8MegDgWX9A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -737,8 +787,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-form@0.1.10': - resolution: {integrity: sha512-1NfuvctVtX4sU3Mmq/IdrR8UunxiCMiVg3A5UENKhFzxUBeOyaQQ+lmaQaV7Tc8cqvBKsJL3/KGBsixK0D8WFg==} + '@radix-ui/react-form@0.1.12': + resolution: {integrity: sha512-JTX94E4LDL91rzLg7X0mHPdxr0A8JEdVwZEmeOwZJSMDHCGW5DFtSlTSJozUyUs807IQmnvbfzKZFVCK5DmkqQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -750,8 +800,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-hover-card@1.1.17': - resolution: {integrity: sha512-GjZQIEANVkuuWeztlKz6QEHe31ZX2iDfHzcTMCQVZXC0JyQrgfKWSC+LOOEw6aVV64zyjzobIzSA4AU4eKWrHA==} + '@radix-ui/react-hover-card@1.1.19': + resolution: {integrity: sha512-2KTgMLQtKvicznQgbindEI2RZ3QbDIwU5gabjUPwFJsormjGDz+rUvO4NANmYwzEEpTcTONUt33vBHIfTIVSfw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -777,8 +827,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-label@2.1.10': - resolution: {integrity: sha512-ib0zvq2ZsAqKm5tRnqGJn3vOxSgIts5ToxsXT0q1S/GfLD1Zj7UOEnkw8u2w6sRmn47djpQWuSU1DCL1R29/yw==} + '@radix-ui/react-label@2.1.11': + resolution: {integrity: sha512-3PKvDDxOn62k0oV1n4QtNtD2vpu+zYjXR7ojLBPaO6SPvhy53yg0vAmgNeBQeJW5rV3dffoRG+HYfLBZuzw0CQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -790,8 +840,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menu@2.1.18': - resolution: {integrity: sha512-lj8Rxjtn6zJq1oSbE/uDtAwCbB9BnxgHD+8MwJMuTh6u1dPamYhW9iuELr/Z8d0D/UysFblYYHeBPwi7T4k0YQ==} + '@radix-ui/react-menu@2.1.20': + resolution: {integrity: sha512-VsUrXxFe9d2ScbZF0fR/oPR1+qjyeLs5p0jzG8h90puMoA9bq4SirYlXbE+USRg9Q2qTeJSFNqjw2nts8jJe4w==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -803,8 +853,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menubar@1.1.18': - resolution: {integrity: sha512-hX7EGx/oFq6DPY27GQuP/2wP48GHf5LG6r06VgNJlG+znmDS8OfopZcRcGly3L4lsB9FqpmLx6JQSE9P3BUpyw==} + '@radix-ui/react-menubar@1.1.20': + resolution: {integrity: sha512-gzFZvybgmwYsFBWDqanycIoEYnhyk8MMnuLamdFVHUZYGp4COM+sqXiwbnn0VMWqGLeeU7GV7jm+dXRa+Wufag==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -816,8 +866,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-navigation-menu@1.2.16': - resolution: {integrity: sha512-nJ0SkrSQgudyYhMiYeHA1ayLVuduEJCFLan1RZZN7c9kqzzCFLaU9kuy81uNtqzweM9YaQPgWzxi9MwQ9jZ04g==} + '@radix-ui/react-navigation-menu@1.2.18': + resolution: {integrity: sha512-K9HiuxZ6xCwSaHcIuUpxyhy4w5gpwzWjh9dHTSbMN3Ix4qAyVObS9RlU3zMycb0PO3v9Tpk0BXMwWvXOUbVXew==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -829,8 +879,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-one-time-password-field@0.1.10': - resolution: {integrity: sha512-GHkcJ+WVj91At+OvUVTD4R3W0/wxw9t/sG5xFUBYXaCbtWiooZX5Md376QjJqgH4VsVyXrbVNHO2O4NYcmjfVg==} + '@radix-ui/react-one-time-password-field@0.1.12': + resolution: {integrity: sha512-nQLu5OAcORDQp1EHAv6k3mJGV1hjMTw2NTGVAsGE1g/mWeNqAd1R5jyaAs3U+A8ZD/W8XNPY2yKT0ZdQnqo3NA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -842,8 +892,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-password-toggle-field@0.1.5': - resolution: {integrity: sha512-fVuA82u0b/fClpbEJv8yp1nU9eSvoSEOERsU/hhf3FXGPIvkmE7oEaHEu8poowoXO39/Va7zq2E0TUcYr1dBRg==} + '@radix-ui/react-password-toggle-field@0.1.7': + resolution: {integrity: sha512-gB1Mr8vzdv1XzDjrtJTXmL0JORRs1B4g7ngUs0F+H2VvMOwXTZMTmLCl0wZZ3m7ylX8TssI7NCvgiSHmLuTm/A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -855,8 +905,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popover@1.1.17': - resolution: {integrity: sha512-/YSAOdJ7YJvdn7bn5sdSx2egW+SKY+u7O5RyAVs94Ymrg2fg5QTSFPMRkzvhGyFuE4/qsmPBdrwYoZMZh/4f+g==} + '@radix-ui/react-popover@1.1.19': + resolution: {integrity: sha512-jkrTdQVxnIB8fpn0NyyxW9CTB5aCXZZelVz5z+Xmii6g5WxMqS3fInNslZ63puP39+Puu4jYohUK31y3dT87gQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -868,8 +918,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popper@1.3.1': - resolution: {integrity: sha512-bhnq/0DEPTi2lsOD3J5rTL65qUKHbKbhqHsmN9TMiclSXpipi651ooUKPPp6G5lF/WiHBdn1s0Wuqsn+myVAvw==} + '@radix-ui/react-popper@1.3.3': + resolution: {integrity: sha512-mS7dGpyjv6b+gsDjLF7e0ia1W4Im1B1hSCy2yuXlHuvnZxHKagfDaobt/KAKt27EpZMit2pss8eJBVyVjEWM+g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -881,8 +931,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-portal@1.1.12': - resolution: {integrity: sha512-m309havGzsjLHHaIX50G5PlvRs3xkgPCsGk/5PTvYm8D5q33yG0J7w/712PTOhid7NTaFETtnSXjngHQavvhVw==} + '@radix-ui/react-portal@1.1.13': + resolution: {integrity: sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -894,8 +944,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.6': - resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==} + '@radix-ui/react-presence@1.1.7': + resolution: {integrity: sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -907,8 +957,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.1.6': - resolution: {integrity: sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g==} + '@radix-ui/react-primitive@2.1.7': + resolution: {integrity: sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -920,8 +970,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-progress@1.1.10': - resolution: {integrity: sha512-JYzEg60lk79PwKM27WZyKd7PW8O4OM5jOaFfRPfOyeXmMw7tLJh5kSj+CEjVTehszuwml/AdCzPGMXBTGf4BBw==} + '@radix-ui/react-progress@1.1.12': + resolution: {integrity: sha512-ZPHyI0JyzoH/rP0tq2uRaIZTj/4s8+kAbqPz+e2N8+ejHvwPJ889dHhqn+vh7PNvNeq+boAoH9yzqeoShzwF2w==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -933,8 +983,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-radio-group@1.4.1': - resolution: {integrity: sha512-/SSxZdKEo2Eo29FFRKd06EfFDYp8HryKg0WYg7QLXaydPzl52YfSvCH2a3QDBRdtcuwACroJT8UVjQVgOJ7P9A==} + '@radix-ui/react-radio-group@1.4.3': + resolution: {integrity: sha512-WwZFjWV4s3aC1QtR3k04R+oANHtX2q6fgKlc7MCEiDNlnTxCZ3H8k3mHtEgVlOejystwk1WQgarQhNOQZ2bK1g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -946,8 +996,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.1.13': - resolution: {integrity: sha512-9gkwneI0guf8JDmrFxPjJF6Ozzgioyw+/lonYNCwefS9ZHA05er0BVHiXr+LbWGHxUfczvMY6G1oiZZi1VzjRw==} + '@radix-ui/react-roving-focus@1.1.15': + resolution: {integrity: sha512-40svmmugfM3mUN7VUDGVE1tQGOhyi8enlGD0CNJEcMM36C1f71PKM21DFgNHUfem0XnA+d8H8oN3Z9ZpJjSslg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -959,8 +1009,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-scroll-area@1.2.12': - resolution: {integrity: sha512-xuafVzQiTCLsyEjakowTdG3OgTXsmO7IdCiO77otIa+z44xoLNs9Do5eg7POFumIOCjtG6djfm6RKUKpUa/csA==} + '@radix-ui/react-scroll-area@1.2.14': + resolution: {integrity: sha512-bBODCWZK7JTbQLHs0uIP4f73wIWatakK4OS33UzkR1x897wu0PuO658a3f+6P2GEGyDzGYMuHRatMVoAk9WZTw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -972,8 +1022,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-select@2.3.1': - resolution: {integrity: sha512-w6eDvY78LE9ZUiNnXCA1QVK8RYN7k9galFv09kjVydJqBAgHd7Y9A6h0UJ/6DCZNGZMZrB2ohcSW1Bo9d8+wWA==} + '@radix-ui/react-select@2.3.3': + resolution: {integrity: sha512-L5RQTXz6Anxsf9CCv+pTgiAsUpyVj7rJxsGtmhFaEOJ++cVfXucv4qWfsIO0AIB4NAhi3yovWGVMKKS1Xf1Wrg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -985,8 +1035,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-separator@1.1.10': - resolution: {integrity: sha512-Y6K6jLQCVfCnTL2MEtGxDLffkhNfEfHsEg3Wa8JU+IWdn3EWbLXd3OuOfQRN7p/W/cUce1WyTk3QeuAoDBzN9g==} + '@radix-ui/react-separator@1.1.11': + resolution: {integrity: sha512-jRhe86+8PF7VZ1u14eOWVOuh2BuAhALg/FT1VcMC4OHedMTRUazDnDlKTt+yxo5cRNKHMfmvZ4sSQtWDeMV4CQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -998,8 +1048,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slider@1.4.1': - resolution: {integrity: sha512-r91WSpQucNGFKAIxT8FT0H0zyjd5tJlqObLp7LOMV4z49KoDCwjy01w3vDOU4e1wxhF9IgjYco7SB6byOW7Buw==} + '@radix-ui/react-slider@1.4.3': + resolution: {integrity: sha512-CWVVj+XaTom0SKCqw1EUgb0NuiLwS+N3OFG73mVEezKEjgNIvZiu0EevMelSSU+CbX3owbqJweG2gPU31WGC5A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1020,8 +1070,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-switch@1.3.1': - resolution: {integrity: sha512-55bQtCnOB0BohomSHi6qvQXpJEEqUGDm6hRrM0Bph5OXwhSegqkd8IqgBAQkM1IlgUlWZIxpxRcpOEfRIgimyw==} + '@radix-ui/react-switch@1.3.3': + resolution: {integrity: sha512-1+mlB4/lxJfk5tgJ4g+R5mUCbRpPE1T9+UsEyeLYbGgMtwiMgmuTnfKz4Mw1nHALHjuwyxw4MLd4cSHn6pNSlQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1033,8 +1083,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tabs@1.1.15': - resolution: {integrity: sha512-kxc9gI6/HfcU4nfMMVS3AmQK414kbU1IE6UCJmMmxjhO3cRPXOyYnmvyKD+ODt7q56nRq9l7Wovi6uaGwKgMlg==} + '@radix-ui/react-tabs@1.1.17': + resolution: {integrity: sha512-nRyXnrAVCwjeXcHbvEbLS6ndbTeKHG1RqCP4A8Gw5L4cemDzPXdD8rAmr6wet0v57R69wGvuIIsFjHSVkZiMzQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1046,8 +1096,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toast@1.2.17': - resolution: {integrity: sha512-uL4kyyWy000pPL43fGGCV5qT6ZchCWEQZOSlkYiPwPt8Hy1iW38RjeptIvz1/SZesrW6Vn58Ct3sV7tfEfiAbw==} + '@radix-ui/react-toast@1.2.19': + resolution: {integrity: sha512-SxfVZfVOibWKWdkf0Xx1awW2d09fQu4V4PXDY1j5hi4MVf7MWdJZqTBJMa1KWtOr1S6GGtCk02nniZ0Iia+dHw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1059,8 +1109,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toggle-group@1.1.13': - resolution: {integrity: sha512-Xb9PLtlvU66F36LiKba6dFswu6V2mDkgidO4fNSbQHQwmZ9ObxMIO17MN/LJ4aWJecVuSVLAHPZjyeMzJrgeiA==} + '@radix-ui/react-toggle-group@1.1.15': + resolution: {integrity: sha512-gIC5Q+Xljg7lmUdzSuDoy0t97yZn1sZl00Ra37ZvKrYdWnQLU6sWLd09yG8cIB9jUAlQfHgJ2ACAG00MFwsqSQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1072,8 +1122,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toggle@1.1.12': - resolution: {integrity: sha512-AsAVsYNZIlRBsci7BhE+QyQeKd1h6TffJYt+lF0QQkd5OpQ3klfIByPsCb4G0h/Fq6PJwh1FYNluzBFYzhk4+w==} + '@radix-ui/react-toggle@1.1.14': + resolution: {integrity: sha512-QI/hB65XKWACA66P64A+aHxtLUgHJeJLkaQa+awUNXT6T3swndtY5DojeHA+vldrTspMTtFBd7HfZ9QGbM1Qrw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1085,8 +1135,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toolbar@1.1.13': - resolution: {integrity: sha512-Za1l4f6fzTkGgz/iynAMN8iaqiKff2wm2/QwiLmHPtDQreWEBrvSimgQFIekxMUdRPhILM7xdIXxuS/o/DGZag==} + '@radix-ui/react-toolbar@1.1.15': + resolution: {integrity: sha512-t/iEuVjUnXXtrsGK40AA43uIx37sn3AqZ7oAVnPICK6lFJP6dzMzWR3U9b6eCfFjb6wtSEqkJ9Rn9xDjiOx20g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1098,8 +1148,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tooltip@1.2.10': - resolution: {integrity: sha512-NlNe8D0dWEpVfXFli90IO6X07Josx/b1iu98tDnx9Xv0HT4wLIL+m2VOheMHhK7qbp2HoTBqALEFzGyZs/levw==} + '@radix-ui/react-tooltip@1.2.12': + resolution: {integrity: sha512-U3HoftgWnmla78vzQbLvKKb7bUYJxoiiqYFzp1wu/TBMyDqMZSuCl3aRICsD6EfVEwcJD2mumGDGUXLFVqQHKA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1138,8 +1188,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-escape-keydown@1.1.2': - resolution: {integrity: sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==} + '@radix-ui/react-use-escape-keydown@1.1.3': + resolution: {integrity: sha512-3wEkMiPHXha/2VadZ68rYBcmYnPINVGl4Y3gtcM7fKRjANk0OscK+cdqBgUWdozb7YJxsh0vefM7vgAMHXOjqg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -1192,8 +1242,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-visually-hidden@1.2.6': - resolution: {integrity: sha512-jCE0WljWifTI4niIMCll06kGpsJTAPiZVU9H4WR1N6qW7At9ystHbN7dDB+we2xH535roFHj7qKS+RGj0FMDWQ==} + '@radix-ui/react-visually-hidden@1.2.7': + resolution: {integrity: sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1228,65 +1278,65 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@tailwindcss/node@4.3.1': - resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==} + '@tailwindcss/node@4.3.3': + resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} - '@tailwindcss/oxide-android-arm64@4.3.1': - resolution: {integrity: sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==} + '@tailwindcss/oxide-android-arm64@4.3.3': + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} engines: {node: '>= 20'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.3.1': - resolution: {integrity: sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==} + '@tailwindcss/oxide-darwin-arm64@4.3.3': + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.3.1': - resolution: {integrity: sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==} + '@tailwindcss/oxide-darwin-x64@4.3.3': + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.3.1': - resolution: {integrity: sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==} + '@tailwindcss/oxide-freebsd-x64@4.3.3': + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': - resolution: {integrity: sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} engines: {node: '>= 20'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': - resolution: {integrity: sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.3.1': - resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==} + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.3.1': - resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==} + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} engines: {node: '>= 20'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.3.1': - resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==} + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} engines: {node: '>= 20'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.3.1': - resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==} + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -1297,30 +1347,30 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': - resolution: {integrity: sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==} + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.3.1': - resolution: {integrity: sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==} + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} engines: {node: '>= 20'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.3.1': - resolution: {integrity: sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==} + '@tailwindcss/oxide@4.3.3': + resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==} engines: {node: '>= 20'} - '@tailwindcss/postcss@4.3.1': - resolution: {integrity: sha512-dNJuNbdEJT/SWRuXTYP1WSamelsz3ztkUsdtWQPjrexysrTpaEPM40P/71knXiXLYEojqPOEGitVLLpPMS5T6A==} + '@tailwindcss/postcss@4.3.3': + resolution: {integrity: sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==} - '@tanstack/query-core@5.101.1': - resolution: {integrity: sha512-Y6Y92dkXtNqx67m2pMSxUsA3zOCwv862JexZRP8/EPwvKXMPu9m8rv43spiXWzOUIggQ3SQApttALStzhA8B4g==} + '@tanstack/query-core@5.101.2': + resolution: {integrity: sha512-hH5MLoJhF7KaIGd7q3xTXGXvslI+GYlM1Z/35aSHHWaCJWB7XvTSHYuV3eM7tw+aE0mT/xMro4M4Q9rCGHT0lw==} - '@tanstack/react-query@5.101.1': - resolution: {integrity: sha512-ZnONUuQKJe1bJMStXUL1s5uKN9FcfC28j5cK+iDZcdSHtUv1wtin1cGc/Oewhf2Oc4eKY7lggtpvT/AbMmhHew==} + '@tanstack/react-query@5.101.2': + resolution: {integrity: sha512-seDkr6kzGzX1okaaTtZPtgA688CDPlXUz1C6xSg0ESqn04Vuc8tlrYms1s3de+znBqhPVxFRfpAfUf+6XvfPWg==} peerDependencies: react: ^18 || ^19 @@ -1384,8 +1434,8 @@ packages: '@types/d3-quadtree@3.0.6': resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} - '@types/d3-random@3.0.3': - resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} + '@types/d3-random@3.0.4': + resolution: {integrity: sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==} '@types/d3-scale-chromatic@3.1.0': resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} @@ -1420,6 +1470,9 @@ packages: '@types/debug@4.1.13': resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -1429,8 +1482,8 @@ packages: '@types/geojson@7946.0.16': resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} - '@types/hast@3.0.4': - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1441,8 +1494,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@26.0.1': - resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==} + '@types/node@26.1.1': + resolution: {integrity: sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==} '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} @@ -1464,8 +1517,165 @@ packages: '@types/use-sync-external-store@0.0.6': resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} - '@ungap/structured-clone@1.3.2': - resolution: {integrity: sha512-5jsZFwgR5rTdKwidH9Qmat75RKwqfpKlWWB1frDkljN127mwqBu8K0PYo7/hFpF03IEJpfVPpCQDY/eDx3iHvA==} + '@typescript-eslint/project-service@8.64.0': + resolution: {integrity: sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.64.0': + resolution: {integrity: sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.64.0': + resolution: {integrity: sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.64.0': + resolution: {integrity: sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.64.0': + resolution: {integrity: sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.64.0': + resolution: {integrity: sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.64.0': + resolution: {integrity: sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] + + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] + + '@typescript/typescript-darwin-x64@7.0.2': + resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] + + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] + + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [freebsd] + + '@typescript/typescript-linux-arm64@7.0.2': + resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] + + '@typescript/typescript-linux-arm@7.0.2': + resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] + + '@typescript/typescript-linux-loong64@7.0.2': + resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} + engines: {node: '>=16.20.0'} + cpu: [loong64] + os: [linux] + + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] + os: [linux] + + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [linux] + + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} + engines: {node: '>=16.20.0'} + cpu: [riscv64] + os: [linux] + + '@typescript/typescript-linux-s390x@7.0.2': + resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} + engines: {node: '>=16.20.0'} + cpu: [s390x] + os: [linux] + + '@typescript/typescript-linux-x64@7.0.2': + resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [netbsd] + + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [netbsd] + + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [openbsd] + + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [openbsd] + + '@typescript/typescript-sunos-x64@7.0.2': + resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [sunos] + + '@typescript/typescript-win32-arm64@7.0.2': + resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] + + '@typescript/typescript-win32-x64@7.0.2': + resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] + + '@ungap/structured-clone@1.3.3': + resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} '@upsetjs/venn.js@2.0.0': resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} @@ -1483,16 +1693,29 @@ packages: '@workflow/serde@4.1.0': resolution: {integrity: sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ==} + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - ai@7.0.2: - resolution: {integrity: sha512-VMU08jHIDJnnKDrbC9AFa5ZsPpOTfAPRLvTRHtJk4FGAoeldmJROMxvZ2ak5lCjEJ2GP2OLPQbMRyEK8w0+S4A==} + ai@7.0.29: + resolution: {integrity: sha512-q+A+skhl6SyjWliU6W7zNYgDUrEk5SbNrCc5vt4S9n6+n6e7jSorDmu51hlhjDOsS7VwzWGCgbWFvvT3iomtvg==} engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -1517,13 +1740,13 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - baseline-browser-mapping@2.10.40: - resolution: {integrity: sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==} + baseline-browser-mapping@2.10.43: + resolution: {integrity: sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==} engines: {node: '>=6.0.0'} hasBin: true - brace-expansion@5.0.6: - resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + brace-expansion@5.0.7: + resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} engines: {node: 18 || 20 || >=22} bundle-name@4.1.0: @@ -1542,8 +1765,8 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - caniuse-lite@1.0.30001799: - resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} + caniuse-lite@1.0.30001806: + resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -1793,6 +2016,9 @@ packages: decode-named-character-reference@1.3.0: resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -1836,8 +2062,8 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - dompurify@3.4.11: - resolution: {integrity: sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==} + dompurify@3.4.12: + resolution: {integrity: sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==} dotenv@17.4.2: resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} @@ -1847,8 +2073,8 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - enhanced-resolve@5.21.6: - resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} + enhanced-resolve@5.24.2: + resolution: {integrity: sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==} engines: {node: '>=10.13.0'} entities@6.0.1: @@ -1874,13 +2100,59 @@ packages: es-toolkit@1.49.0: resolution: {integrity: sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==} + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@10.7.0: + resolution: {integrity: sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + estree-util-is-identifier-name@3.0.0: resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} @@ -1897,6 +2169,12 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-string-truncated-width@3.0.3: resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} @@ -1906,6 +2184,30 @@ packages: fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + follow-redirects@1.16.0: resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} engines: {node: '>=4.0'} @@ -1919,8 +2221,8 @@ packages: resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} engines: {node: '>= 6'} - framer-motion@12.42.0: - resolution: {integrity: sha512-wp7EJnfWaaEScVygKv3e20udoRz+LbtxScsuTkakAxfXmt+ReC6WyPW2nINRAGvd+hG9odwcjBLyOTPjH5pBRA==} + framer-motion@12.42.2: + resolution: {integrity: sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -1960,6 +2262,10 @@ packages: resolution: {integrity: sha512-gzi2D96p+AMfDcmJHGDj3KJ9NRiwvlFAU5yfa3ROwWZmFUjX4P43x3BcyRaOMMLto1vUo7C+86+MFhYTl6Ryiw==} hasBin: true + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + glob@13.0.6: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} @@ -2024,15 +2330,20 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - immer@10.2.0: - resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} - immer@11.1.8: - resolution: {integrity: sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA==} + immer@11.1.11: + resolution: {integrity: sha512-qzXuyXAkPySAGYkfsAwodDPWT8Zm7/Uo5BNt4BjhMhG5WlWyZZ4wQqnWwdS8kjlQ1Cwu6gjw3A6+0gTQwlyYtw==} import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -2057,6 +2368,14 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} hasBin: true + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} @@ -2088,9 +2407,18 @@ packages: resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} hasBin: true + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} @@ -2098,6 +2426,9 @@ packages: resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} hasBin: true + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + khroma@2.1.0: resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} @@ -2107,6 +2438,10 @@ packages: layout-base@2.0.1: resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} @@ -2177,6 +2512,10 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + lodash-es@4.18.1: resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} @@ -2186,12 +2525,12 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - lru-cache@11.5.1: - resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} - lucide-react@1.21.0: - resolution: {integrity: sha512-reEZMXq8Qdd5jg5XYkQ5TR1fB/GiQ7ih4vcrthYDtgjSDwh0i6/YLiGjsWsIwgN49gpAnd4J2elSNzncMEEUUQ==} + lucide-react@1.24.0: + resolution: {integrity: sha512-YT6mBD8lGKkg4nM39enlm94/sfJIiW0YKUT60fBy4YK8tai31ylg1VhGNWxkpSKHo9UagfnZqwIff3HTDQwXeA==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -2363,14 +2702,14 @@ packages: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} - motion-dom@12.42.0: - resolution: {integrity: sha512-M63h4n8R+quJdNhBwuLlgxM+OLYa9+I/T2pzDRboB9fLXRdbou+Gw7Zury+SkpaCyACP1JHSjHgZ1EgTkBr30w==} + motion-dom@12.42.2: + resolution: {integrity: sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA==} motion-utils@12.39.0: resolution: {integrity: sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==} - motion@12.42.0: - resolution: {integrity: sha512-Qhwvu9sVl5/URSq5CNzwMCpSKK8Uhnrwb6VO977kZyj/wOCS7mWebJUnBoHx5cZU1Zv8a9BD5CSICWKAlrLJgA==} + motion@12.42.2: + resolution: {integrity: sha512-Atvv11yUKIid41cVrRBDVX5m8tF8kNpExRSlbpt6APClhDjtwQssgFHhQzejxw7/7YYbjHSPKBVbHo05BuJT5Q==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -2386,19 +2725,22 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - nanoid@3.3.15: - resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + next-themes@0.4.6: resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} peerDependencies: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - next@16.2.9: - resolution: {integrity: sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww==} + next@16.2.10: + resolution: {integrity: sha512-2som5AVXb3kE6Yjine3/mNbBayYF58eguBWIVVUdr1y/L426xyVEgYxgBG+1QC34P2x5E+tcDup6XkuOAX3dCA==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -2418,8 +2760,8 @@ packages: sass: optional: true - nypm@0.6.7: - resolution: {integrity: sha512-s3ds97SD5pd1dULE+tHUk1DrV0cSHOnsfpcdGATJ8JpBo21DoKqN9exTH4/2nhPQNOLomBdTFMicN94S4DrZrQ==} + nypm@0.6.8: + resolution: {integrity: sha512-Q9K4Diu6l5u6xJQogeFSs/zKtyMSgFKFtRQV+tHP4kL7KPm2grpBU0dFIwFaXwNxN0MtfKWc43VpCugAa+LPsw==} engines: {node: '>=18'} hasBin: true @@ -2430,8 +2772,20 @@ packages: resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} engines: {node: '>=20'} - package-manager-detector@1.6.0: - resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + package-manager-detector@1.7.0: + resolution: {integrity: sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ==} parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} @@ -2442,6 +2796,10 @@ packages: path-data-parser@0.1.0: resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -2459,6 +2817,10 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + pkce-challenge@5.0.1: resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} @@ -2476,14 +2838,18 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.15: - resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + postcss@8.5.19: + resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==} engines: {node: ^10 || ^12 || >=14} powershell-utils@0.1.0: resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} engines: {node: '>=20'} + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + property-information@7.2.0: resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} @@ -2491,8 +2857,12 @@ packages: resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} engines: {node: '>=10'} - radix-ui@1.6.0: - resolution: {integrity: sha512-EUEC70O03EgxWMP5aoqfBZ6iLC5bczFagGy7zhSYRt8o5DP7IWNiP3ywetse3L9b8843ExB0OGWZvgbYVJuNeg==} + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + radix-ui@1.6.2: + resolution: {integrity: sha512-OwYUjzMwiInCUxgAWpPsavXC3Kh4iyi/49uU1/qZTG3RQDlvegyk1GOMiGvSkjua1RDb3JD3fo3eroL9FV4GQw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2565,8 +2935,8 @@ packages: resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} engines: {node: '>= 20.19.0'} - recharts@3.9.0: - resolution: {integrity: sha512-dCEcE9y20c8H2tkVeByrAXhhnBJk6/QLbxKmn+dJUptOfc5NMjwRh1jo0vZPRLD+5dMrHrP+hPEsfbGBMfnf5Q==} + recharts@3.9.2: + resolution: {integrity: sha512-G4fy+Pk46RaXgwWMh+Nzhyo/lbFAVqXo9gtetlyehe6Ehge9CsgDuOTwQDD+i1+llaLktNBiNq4bhnGlDRXFtw==} engines: {node: '>=18'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -2712,8 +3082,8 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders' - tailwindcss@4.3.1: - resolution: {integrity: sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==} + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} tapable@2.3.3: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} @@ -2730,12 +3100,22 @@ packages: resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} engines: {node: '>=18'} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + ts-dedent@2.3.0: resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==} engines: {node: '>=6.10'} @@ -2743,13 +3123,17 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - typescript@6.0.3: - resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} - engines: {node: '>=14.17'} + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} hasBin: true - ultracite@7.8.3: - resolution: {integrity: sha512-Fsj9aYJfh57uDB6DrdKVafKhF0QNekwqTrZKoTMYePjZp1npyFCnfZVj2SnPZdOcxAGHJfmLYUIDq0YPh8acVA==} + ultracite@7.9.4: + resolution: {integrity: sha512-efAEbQJoii3wH0aZ+jwbcGXQYBwC1E6TvC4//EIV3ei84RDGHDo9fSB6h/TBTNk/+/psQVGm56trVvA+2VwRbg==} hasBin: true peerDependencies: oxfmt: '>=0.1.0' @@ -2781,6 +3165,9 @@ packages: unist-util-visit@5.1.0: resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-callback-ref@1.3.3: resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} engines: {node: '>=10'} @@ -2833,8 +3220,8 @@ packages: victory-vendor@37.3.6: resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} - wavesurfer.js@7.12.8: - resolution: {integrity: sha512-G3nxzcC4X+ZWrLtcIV17kCWHVq3ysJCS4dS0YkGKILrQ2esAb8cScw965zKNKYxUvpiZsPK93KLWgWTYdIBQiw==} + wavesurfer.js@7.12.10: + resolution: {integrity: sha512-M3AC5biFmvfy7Oxe8FN6I88H0z1c8Vuz81N6Oq2CY4kOumNDb6U1/0qg4pomjP619vJqdWADlgUcpOKvfGifng==} web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} @@ -2844,6 +3231,10 @@ packages: engines: {node: '>= 8'} hasBin: true + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + wsl-utils@0.3.1: resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} engines: {node: '>=20'} @@ -2853,6 +3244,10 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -2861,38 +3256,38 @@ packages: snapshots: - '@ai-sdk/gateway@4.0.2(zod@4.4.3)': + '@ai-sdk/gateway@4.0.21(zod@4.4.3)': dependencies: - '@ai-sdk/provider': 4.0.0 - '@ai-sdk/provider-utils': 5.0.0(zod@4.4.3) + '@ai-sdk/provider': 4.0.3 + '@ai-sdk/provider-utils': 5.0.10(zod@4.4.3) '@vercel/oidc': 3.2.0 zod: 4.4.3 - '@ai-sdk/mcp@2.0.1(zod@4.4.3)': + '@ai-sdk/mcp@2.0.14(zod@4.4.3)': dependencies: - '@ai-sdk/provider': 4.0.0 - '@ai-sdk/provider-utils': 5.0.0(zod@4.4.3) + '@ai-sdk/provider': 4.0.3 + '@ai-sdk/provider-utils': 5.0.10(zod@4.4.3) pkce-challenge: 5.0.1 zod: 4.4.3 - '@ai-sdk/provider-utils@5.0.0(zod@4.4.3)': + '@ai-sdk/provider-utils@5.0.10(zod@4.4.3)': dependencies: - '@ai-sdk/provider': 4.0.0 + '@ai-sdk/provider': 4.0.3 '@standard-schema/spec': 1.1.0 '@workflow/serde': 4.1.0 eventsource-parser: 3.1.0 zod: 4.4.3 - '@ai-sdk/provider@4.0.0': + '@ai-sdk/provider@4.0.3': dependencies: json-schema: 0.4.0 - '@ai-sdk/react@4.0.3(react@19.2.7)(zod@4.4.3)': + '@ai-sdk/react@4.0.32(react@19.2.7)(zod@4.4.3)': dependencies: - '@ai-sdk/mcp': 2.0.1(zod@4.4.3) - '@ai-sdk/provider': 4.0.0 - '@ai-sdk/provider-utils': 5.0.0(zod@4.4.3) - ai: 7.0.2(zod@4.4.3) + '@ai-sdk/mcp': 2.0.14(zod@4.4.3) + '@ai-sdk/provider': 4.0.3 + '@ai-sdk/provider-utils': 5.0.10(zod@4.4.3) + ai: 7.0.29(zod@4.4.3) react: 19.2.7 swr: 2.4.2(react@19.2.7) throttleit: 2.1.0 @@ -2903,81 +3298,111 @@ snapshots: '@antfu/install-pkg@1.1.0': dependencies: - package-manager-detector: 1.6.0 + package-manager-detector: 1.7.0 tinyexec: 1.2.4 - '@biomejs/biome@2.5.1': + '@biomejs/biome@2.5.4': optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.5.1 - '@biomejs/cli-darwin-x64': 2.5.1 - '@biomejs/cli-linux-arm64': 2.5.1 - '@biomejs/cli-linux-arm64-musl': 2.5.1 - '@biomejs/cli-linux-x64': 2.5.1 - '@biomejs/cli-linux-x64-musl': 2.5.1 - '@biomejs/cli-win32-arm64': 2.5.1 - '@biomejs/cli-win32-x64': 2.5.1 - - '@biomejs/cli-darwin-arm64@2.5.1': + '@biomejs/cli-darwin-arm64': 2.5.4 + '@biomejs/cli-darwin-x64': 2.5.4 + '@biomejs/cli-linux-arm64': 2.5.4 + '@biomejs/cli-linux-arm64-musl': 2.5.4 + '@biomejs/cli-linux-x64': 2.5.4 + '@biomejs/cli-linux-x64-musl': 2.5.4 + '@biomejs/cli-win32-arm64': 2.5.4 + '@biomejs/cli-win32-x64': 2.5.4 + + '@biomejs/cli-darwin-arm64@2.5.4': optional: true - '@biomejs/cli-darwin-x64@2.5.1': + '@biomejs/cli-darwin-x64@2.5.4': optional: true - '@biomejs/cli-linux-arm64-musl@2.5.1': + '@biomejs/cli-linux-arm64-musl@2.5.4': optional: true - '@biomejs/cli-linux-arm64@2.5.1': + '@biomejs/cli-linux-arm64@2.5.4': optional: true - '@biomejs/cli-linux-x64-musl@2.5.1': + '@biomejs/cli-linux-x64-musl@2.5.4': optional: true - '@biomejs/cli-linux-x64@2.5.1': + '@biomejs/cli-linux-x64@2.5.4': optional: true - '@biomejs/cli-win32-arm64@2.5.1': + '@biomejs/cli-win32-arm64@2.5.4': optional: true - '@biomejs/cli-win32-x64@2.5.1': + '@biomejs/cli-win32-x64@2.5.4': optional: true '@braintree/sanitize-url@7.1.2': {} '@chevrotain/types@11.1.2': {} - '@clack/core@1.4.2': + '@clack/core@1.4.3': dependencies: fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 - '@clack/prompts@1.6.0': + '@clack/prompts@1.7.0': dependencies: - '@clack/core': 1.4.2 + '@clack/core': 1.4.3 fast-string-width: 3.0.2 fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 - '@emnapi/runtime@1.11.1': + '@emnapi/runtime@1.11.2': dependencies: tslib: 2.8.1 optional: true - '@floating-ui/core@1.7.5': + '@eslint-community/eslint-utils@4.9.1(eslint@10.7.0(jiti@2.7.0))': + dependencies: + eslint: 10.7.0(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.23.5': dependencies: - '@floating-ui/utils': 0.2.11 + '@eslint/object-schema': 3.0.5 + debug: 4.4.3 + minimatch: 10.2.5 + transitivePeerDependencies: + - supports-color - '@floating-ui/dom@1.7.6': + '@eslint/config-helpers@0.6.0': dependencies: - '@floating-ui/core': 1.7.5 - '@floating-ui/utils': 0.2.11 + '@eslint/core': 1.2.1 - '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@eslint/core@1.2.1': dependencies: - '@floating-ui/dom': 1.7.6 + '@types/json-schema': 7.0.15 + + '@eslint/object-schema@3.0.5': {} + + '@eslint/plugin-kit@0.7.2': + dependencies: + '@eslint/core': 1.2.1 + levn: 0.4.1 + + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/react-dom@2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/dom': 1.8.0 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - '@floating-ui/utils@0.2.11': {} + '@floating-ui/utils@0.2.12': {} '@hey-api/codegen-core@0.9.1': dependencies: @@ -2994,7 +3419,7 @@ snapshots: '@types/json-schema': 7.0.15 js-yaml: 4.2.0 - '@hey-api/openapi-ts@0.99.0(typescript@6.0.3)': + '@hey-api/openapi-ts@0.99.0(typescript@7.0.2)': dependencies: '@hey-api/codegen-core': 0.9.1 '@hey-api/json-schema-ref-parser': 1.4.4 @@ -3006,7 +3431,7 @@ snapshots: color-support: 1.1.3 commander: 15.0.0 get-tsconfig: 4.14.0 - typescript: 6.0.3 + typescript: 7.0.2 transitivePeerDependencies: - magicast @@ -3029,9 +3454,25 @@ snapshots: '@hey-api/types@0.1.4': {} + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + '@iconify/types@2.0.0': {} - '@iconify/utils@3.1.3': + '@iconify/utils@3.1.4': dependencies: '@antfu/install-pkg': 1.1.0 '@iconify/types': 2.0.0 @@ -3122,7 +3563,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.11.1 + '@emnapi/runtime': 1.11.2 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -3161,30 +3602,30 @@ snapshots: dependencies: '@chevrotain/types': 11.1.2 - '@next/env@16.2.9': {} + '@next/env@16.2.10': {} - '@next/swc-darwin-arm64@16.2.9': + '@next/swc-darwin-arm64@16.2.10': optional: true - '@next/swc-darwin-x64@16.2.9': + '@next/swc-darwin-x64@16.2.10': optional: true - '@next/swc-linux-arm64-gnu@16.2.9': + '@next/swc-linux-arm64-gnu@16.2.10': optional: true - '@next/swc-linux-arm64-musl@16.2.9': + '@next/swc-linux-arm64-musl@16.2.10': optional: true - '@next/swc-linux-x64-gnu@16.2.9': + '@next/swc-linux-x64-gnu@16.2.10': optional: true - '@next/swc-linux-x64-musl@16.2.9': + '@next/swc-linux-x64-musl@16.2.10': optional: true - '@next/swc-win32-arm64-msvc@16.2.9': + '@next/swc-win32-arm64-msvc@16.2.10': optional: true - '@next/swc-win32-x64-msvc@16.2.9': + '@next/swc-win32-x64-msvc@16.2.10': optional: true '@opentelemetry/api@1.9.1': @@ -3192,27 +3633,27 @@ snapshots: '@radix-ui/number@1.1.2': {} - '@radix-ui/primitive@1.1.4': {} + '@radix-ui/primitive@1.1.5': {} - '@radix-ui/react-accessible-icon@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-accessible-icon@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-accordion@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-accordion@1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collapsible': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collapsible': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -3220,41 +3661,41 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-alert-dialog@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-alert-dialog@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-dialog': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dialog': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-arrow@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-arrow@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-aspect-ratio@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-aspect-ratio@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-avatar@1.2.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-avatar@1.2.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) @@ -3264,13 +3705,13 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-checkbox@1.3.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-checkbox@1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) @@ -3280,14 +3721,14 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-collapsible@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-collapsible@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 @@ -3296,11 +3737,11 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-collection@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-collection@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -3314,12 +3755,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-context-menu@2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-context-menu@2.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -3327,24 +3768,24 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@19.2.7)': + '@radix-ui/react-context@1.2.0(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-dialog@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-dialog@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) aria-hidden: 1.2.6 @@ -3361,27 +3802,27 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-dismissable-layer@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-dismissable-layer@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-dropdown-menu@2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-dropdown-menu@2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-menu': 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -3395,10 +3836,10 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-focus-scope@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-focus-scope@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -3406,30 +3847,30 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-form@0.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-form@0.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-label': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-label': 2.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-hover-card@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-hover-card@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -3448,31 +3889,31 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-label@2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-label@2.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-menu@2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-menu@2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) aria-hidden: 1.2.6 @@ -3483,17 +3924,17 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-menubar@1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-menubar@1.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-menu': 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -3501,38 +3942,38 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-navigation-menu@1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-navigation-menu@1.2.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-one-time-password-field@0.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-one-time-password-field@0.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/number': 1.1.2 - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) @@ -3543,13 +3984,13 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-password-toggle-field@0.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-password-toggle-field@0.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) @@ -3559,19 +4000,19 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-popover@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-popover@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) aria-hidden: 1.2.6 @@ -3582,13 +4023,13 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-popper@1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-popper@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-arrow': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7) @@ -3600,9 +4041,9 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-portal@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-portal@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -3610,7 +4051,7 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-presence@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 @@ -3619,7 +4060,7 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-primitive@2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-primitive@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 @@ -3628,25 +4069,25 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-progress@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-progress@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-radio-group@1.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-radio-group@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) @@ -3656,32 +4097,34 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-roving-focus@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-roving-focus@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-scroll-area@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-scroll-area@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/number': 1.1.2 - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 @@ -3690,28 +4133,28 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-select@2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-select@2.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/number': 1.1.2 - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) aria-hidden: 1.2.6 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -3720,24 +4163,24 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-separator@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-separator@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-slider@1.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-slider@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/number': 1.1.2 - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) @@ -3755,12 +4198,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-switch@1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-switch@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) @@ -3770,15 +4213,15 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-tabs@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-tabs@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -3786,34 +4229,34 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toast@1.2.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-toast@1.2.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toggle-group@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-toggle-group@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-toggle': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -3821,10 +4264,10 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toggle@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-toggle@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -3832,35 +4275,35 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toolbar@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-toolbar@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-separator': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-toggle-group': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-separator': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle-group': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-tooltip@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-tooltip@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@radix-ui/primitive': 1.1.5 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: @@ -3888,7 +4331,7 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-escape-keydown@1.1.2(@types/react@19.2.17)(react@19.2.7)': + '@radix-ui/react-use-escape-keydown@1.1.3(@types/react@19.2.17)(react@19.2.7)': dependencies: '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 @@ -3927,9 +4370,9 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-visually-hidden@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-visually-hidden@1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: @@ -3942,7 +4385,7 @@ snapshots: dependencies: '@standard-schema/spec': 1.1.0 '@standard-schema/utils': 0.3.0 - immer: 11.1.8 + immer: 11.1.11 redux: 5.0.1 redux-thunk: 3.1.0(redux@5.0.1) reselect: 5.2.0 @@ -3958,80 +4401,80 @@ snapshots: dependencies: tslib: 2.8.1 - '@tailwindcss/node@4.3.1': + '@tailwindcss/node@4.3.3': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.21.6 + enhanced-resolve: 5.24.2 jiti: 2.7.0 lightningcss: 1.32.0 magic-string: 0.30.21 source-map-js: 1.2.1 - tailwindcss: 4.3.1 + tailwindcss: 4.3.3 - '@tailwindcss/oxide-android-arm64@4.3.1': + '@tailwindcss/oxide-android-arm64@4.3.3': optional: true - '@tailwindcss/oxide-darwin-arm64@4.3.1': + '@tailwindcss/oxide-darwin-arm64@4.3.3': optional: true - '@tailwindcss/oxide-darwin-x64@4.3.1': + '@tailwindcss/oxide-darwin-x64@4.3.3': optional: true - '@tailwindcss/oxide-freebsd-x64@4.3.1': + '@tailwindcss/oxide-freebsd-x64@4.3.3': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.3.1': + '@tailwindcss/oxide-linux-x64-musl@4.3.3': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.3.1': + '@tailwindcss/oxide-wasm32-wasi@4.3.3': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': optional: true - '@tailwindcss/oxide@4.3.1': + '@tailwindcss/oxide@4.3.3': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.3.1 - '@tailwindcss/oxide-darwin-arm64': 4.3.1 - '@tailwindcss/oxide-darwin-x64': 4.3.1 - '@tailwindcss/oxide-freebsd-x64': 4.3.1 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.1 - '@tailwindcss/oxide-linux-arm64-gnu': 4.3.1 - '@tailwindcss/oxide-linux-arm64-musl': 4.3.1 - '@tailwindcss/oxide-linux-x64-gnu': 4.3.1 - '@tailwindcss/oxide-linux-x64-musl': 4.3.1 - '@tailwindcss/oxide-wasm32-wasi': 4.3.1 - '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1 - '@tailwindcss/oxide-win32-x64-msvc': 4.3.1 - - '@tailwindcss/postcss@4.3.1': + '@tailwindcss/oxide-android-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-x64': 4.3.3 + '@tailwindcss/oxide-freebsd-x64': 4.3.3 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.3 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.3 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-x64-musl': 4.3.3 + '@tailwindcss/oxide-wasm32-wasi': 4.3.3 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 + + '@tailwindcss/postcss@4.3.3': dependencies: '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.3.1 - '@tailwindcss/oxide': 4.3.1 - postcss: 8.5.15 - tailwindcss: 4.3.1 + '@tailwindcss/node': 4.3.3 + '@tailwindcss/oxide': 4.3.3 + postcss: 8.5.19 + tailwindcss: 4.3.3 - '@tanstack/query-core@5.101.1': {} + '@tanstack/query-core@5.101.2': {} - '@tanstack/react-query@5.101.1(react@19.2.7)': + '@tanstack/react-query@5.101.2(react@19.2.7)': dependencies: - '@tanstack/query-core': 5.101.1 + '@tanstack/query-core': 5.101.2 react: 19.2.7 '@types/d3-array@3.2.2': {} @@ -4089,7 +4532,7 @@ snapshots: '@types/d3-quadtree@3.0.6': {} - '@types/d3-random@3.0.3': {} + '@types/d3-random@3.0.4': {} '@types/d3-scale-chromatic@3.1.0': {} @@ -4140,7 +4583,7 @@ snapshots: '@types/d3-path': 3.1.1 '@types/d3-polygon': 3.0.2 '@types/d3-quadtree': 3.0.6 - '@types/d3-random': 3.0.3 + '@types/d3-random': 3.0.4 '@types/d3-scale': 4.0.9 '@types/d3-scale-chromatic': 3.1.0 '@types/d3-selection': 3.0.11 @@ -4155,6 +4598,8 @@ snapshots: dependencies: '@types/ms': 2.1.0 + '@types/esrecurse@4.3.1': {} + '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.9 @@ -4163,7 +4608,7 @@ snapshots: '@types/geojson@7946.0.16': {} - '@types/hast@3.0.4': + '@types/hast@3.0.5': dependencies: '@types/unist': 3.0.3 @@ -4175,7 +4620,7 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@26.0.1': + '@types/node@26.1.1': dependencies: undici-types: 8.3.0 @@ -4196,7 +4641,118 @@ snapshots: '@types/use-sync-external-store@0.0.6': {} - '@ungap/structured-clone@1.3.2': {} + '@typescript-eslint/project-service@8.64.0(typescript@7.0.2)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@7.0.2) + '@typescript-eslint/types': 8.64.0 + debug: 4.4.3 + typescript: 7.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.64.0': + dependencies: + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/visitor-keys': 8.64.0 + + '@typescript-eslint/tsconfig-utils@8.64.0(typescript@7.0.2)': + dependencies: + typescript: 7.0.2 + + '@typescript-eslint/types@8.64.0': {} + + '@typescript-eslint/typescript-estree@8.64.0(typescript@7.0.2)': + dependencies: + '@typescript-eslint/project-service': 8.64.0(typescript@7.0.2) + '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@7.0.2) + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/visitor-keys': 8.64.0 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@7.0.2) + typescript: 7.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.64.0 + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/typescript-estree': 8.64.0(typescript@7.0.2) + eslint: 10.7.0(jiti@2.7.0) + typescript: 7.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.64.0': + dependencies: + '@typescript-eslint/types': 8.64.0 + eslint-visitor-keys: 5.0.1 + + '@typescript/typescript-aix-ppc64@7.0.2': + optional: true + + '@typescript/typescript-darwin-arm64@7.0.2': + optional: true + + '@typescript/typescript-darwin-x64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-x64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm@7.0.2': + optional: true + + '@typescript/typescript-linux-loong64@7.0.2': + optional: true + + '@typescript/typescript-linux-mips64el@7.0.2': + optional: true + + '@typescript/typescript-linux-ppc64@7.0.2': + optional: true + + '@typescript/typescript-linux-riscv64@7.0.2': + optional: true + + '@typescript/typescript-linux-s390x@7.0.2': + optional: true + + '@typescript/typescript-linux-x64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-sunos-x64@7.0.2': + optional: true + + '@typescript/typescript-win32-arm64@7.0.2': + optional: true + + '@typescript/typescript-win32-x64@7.0.2': + optional: true + + '@ungap/structured-clone@1.3.3': {} '@upsetjs/venn.js@2.0.0': optionalDependencies: @@ -4205,26 +4761,39 @@ snapshots: '@vercel/oidc@3.2.0': {} - '@wavesurfer/react@1.0.12(react@19.2.7)(wavesurfer.js@7.12.8)': + '@wavesurfer/react@1.0.12(react@19.2.7)(wavesurfer.js@7.12.10)': dependencies: react: 19.2.7 - wavesurfer.js: 7.12.8 + wavesurfer.js: 7.12.10 '@workflow/serde@4.1.0': {} + acorn-jsx@5.3.2(acorn@8.17.0): + dependencies: + acorn: 8.17.0 + + acorn@8.17.0: {} + agent-base@6.0.2: dependencies: debug: 4.4.3 transitivePeerDependencies: - supports-color - ai@7.0.2(zod@4.4.3): + ai@7.0.29(zod@4.4.3): dependencies: - '@ai-sdk/gateway': 4.0.2(zod@4.4.3) - '@ai-sdk/provider': 4.0.0 - '@ai-sdk/provider-utils': 5.0.0(zod@4.4.3) + '@ai-sdk/gateway': 4.0.21(zod@4.4.3) + '@ai-sdk/provider': 4.0.3 + '@ai-sdk/provider-utils': 5.0.10(zod@4.4.3) zod: 4.4.3 + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + ansi-colors@4.1.3: {} argparse@2.0.1: {} @@ -4249,9 +4818,9 @@ snapshots: balanced-match@4.0.4: {} - baseline-browser-mapping@2.10.40: {} + baseline-browser-mapping@2.10.43: {} - brace-expansion@5.0.6: + brace-expansion@5.0.7: dependencies: balanced-match: 4.0.4 @@ -4279,7 +4848,7 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - caniuse-lite@1.0.30001799: {} + caniuse-lite@1.0.30001806: {} ccount@2.0.1: {} @@ -4535,6 +5104,8 @@ snapshots: dependencies: character-entities: 2.0.2 + deep-is@0.1.4: {} + deepmerge@4.3.1: {} default-browser-id@5.0.1: {} @@ -4566,7 +5137,7 @@ snapshots: dependencies: dequal: 2.0.3 - dompurify@3.4.11: + dompurify@3.4.12: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -4578,7 +5149,7 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - enhanced-resolve@5.21.6: + enhanced-resolve@5.24.2: dependencies: graceful-fs: 4.2.11 tapable: 2.3.3 @@ -4602,10 +5173,78 @@ snapshots: es-toolkit@1.49.0: {} + escape-string-regexp@4.0.0: {} + escape-string-regexp@5.0.0: {} + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.9 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@10.7.0(jiti@2.7.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.23.5 + '@eslint/config-helpers': 0.6.0 + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.2 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + minimatch: 10.2.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color + + espree@11.2.0: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 5.0.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + estree-util-is-identifier-name@3.0.0: {} + esutils@2.0.3: {} + eventemitter3@5.0.4: {} eventsource-parser@3.1.0: {} @@ -4616,6 +5255,10 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + fast-string-truncated-width@3.0.3: {} fast-string-width@3.0.2: @@ -4626,6 +5269,26 @@ snapshots: dependencies: fast-string-width: 3.0.2 + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + follow-redirects@1.16.0: {} form-data@4.0.6: @@ -4636,9 +5299,9 @@ snapshots: hasown: 2.0.4 mime-types: 2.1.35 - framer-motion@12.42.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + framer-motion@12.42.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - motion-dom: 12.42.0 + motion-dom: 12.42.2 motion-utils: 12.39.0 tslib: 2.8.1 optionalDependencies: @@ -4647,9 +5310,9 @@ snapshots: function-bind@1.1.2: {} - geist@1.7.2(next@16.2.9(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)): + geist@1.7.2(next@16.2.10(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)): dependencies: - next: 16.2.9(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.2.10(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) get-intrinsic@1.3.0: dependencies: @@ -4677,6 +5340,10 @@ snapshots: giget@3.3.0: {} + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + glob@13.0.6: dependencies: minimatch: 10.2.5 @@ -4701,7 +5368,7 @@ snapshots: hast-util-from-parse5@8.0.3: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/unist': 3.0.3 devlop: 1.1.0 hastscript: 9.0.1 @@ -4712,13 +5379,13 @@ snapshots: hast-util-parse-selector@4.0.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-raw@9.1.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/unist': 3.0.3 - '@ungap/structured-clone': 1.3.2 + '@ungap/structured-clone': 1.3.3 hast-util-from-parse5: 8.0.3 hast-util-to-parse5: 8.0.1 html-void-elements: 3.0.0 @@ -4732,14 +5399,14 @@ snapshots: hast-util-sanitize@5.0.2: dependencies: - '@types/hast': 3.0.4 - '@ungap/structured-clone': 1.3.2 + '@types/hast': 3.0.5 + '@ungap/structured-clone': 1.3.3 unist-util-position: 5.0.0 hast-util-to-jsx-runtime@2.3.6: dependencies: '@types/estree': 1.0.9 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/unist': 3.0.3 comma-separated-tokens: 2.0.3 devlop: 1.1.0 @@ -4758,7 +5425,7 @@ snapshots: hast-util-to-parse5@8.0.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 comma-separated-tokens: 2.0.3 devlop: 1.1.0 property-information: 7.2.0 @@ -4768,11 +5435,11 @@ snapshots: hast-util-whitespace@3.0.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hastscript@9.0.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 comma-separated-tokens: 2.0.3 hast-util-parse-selector: 4.0.0 property-information: 7.2.0 @@ -4793,12 +5460,14 @@ snapshots: dependencies: safer-buffer: 2.1.2 - immer@10.2.0: {} + ignore@5.3.2: {} - immer@11.1.8: {} + immer@11.1.11: {} import-meta-resolve@4.2.0: {} + imurmurhash@0.1.4: {} + inline-style-parser@0.2.7: {} internmap@1.0.1: {} @@ -4816,6 +5485,12 @@ snapshots: is-docker@3.0.0: {} + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + is-hexadecimal@2.0.1: {} is-in-ssh@1.0.0: {} @@ -4838,20 +5513,35 @@ snapshots: dependencies: argparse: 2.0.1 + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + json-schema@0.4.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} + jsonc-parser@3.3.1: {} katex@0.16.47: dependencies: commander: 8.3.0 + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + khroma@2.1.0: {} layout-base@1.0.2: {} layout-base@2.0.1: {} + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + lightningcss-android-arm64@1.32.0: optional: true @@ -4901,15 +5591,19 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + lodash-es@4.18.1: {} lodash.debounce@4.0.8: {} longest-streak@3.1.0: {} - lru-cache@11.5.1: {} + lru-cache@11.5.2: {} - lucide-react@1.21.0(react@19.2.7): + lucide-react@1.24.0(react@19.2.7): dependencies: react: 19.2.7 @@ -5009,7 +5703,7 @@ snapshots: mdast-util-mdx-expression@2.0.1: dependencies: '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 @@ -5020,7 +5714,7 @@ snapshots: mdast-util-mdx-jsx@3.2.0: dependencies: '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/mdast': 4.0.4 '@types/unist': 3.0.3 ccount: 2.0.1 @@ -5037,7 +5731,7 @@ snapshots: mdast-util-mdxjs-esm@2.0.1: dependencies: '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 @@ -5052,9 +5746,9 @@ snapshots: mdast-util-to-hast@13.2.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.3.2 + '@ungap/structured-clone': 1.3.3 devlop: 1.1.0 micromark-util-sanitize-uri: 2.0.1 trim-lines: 3.0.1 @@ -5081,7 +5775,7 @@ snapshots: mermaid@11.16.0: dependencies: '@braintree/sanitize-url': 7.1.2 - '@iconify/utils': 3.1.3 + '@iconify/utils': 3.1.4 '@mermaid-js/parser': 1.2.0 '@types/d3': 7.4.3 '@upsetjs/venn.js': 2.0.0 @@ -5092,7 +5786,7 @@ snapshots: d3-sankey: 0.12.3 dagre-d3-es: 7.0.14 dayjs: 1.11.21 - dompurify: 3.4.11 + dompurify: 3.4.12 es-toolkit: 1.49.0 katex: 0.16.47 khroma: 2.1.0 @@ -5301,19 +5995,19 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.6 + brace-expansion: 5.0.7 minipass@7.1.3: {} - motion-dom@12.42.0: + motion-dom@12.42.2: dependencies: motion-utils: 12.39.0 motion-utils@12.39.0: {} - motion@12.42.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + motion@12.42.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - framer-motion: 12.42.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + framer-motion: 12.42.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7) tslib: 2.8.1 optionalDependencies: react: 19.2.7 @@ -5321,39 +6015,41 @@ snapshots: ms@2.1.3: {} - nanoid@3.3.15: {} + nanoid@3.3.16: {} + + natural-compare@1.4.0: {} next-themes@0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - next@16.2.9(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + next@16.2.10(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@next/env': 16.2.9 + '@next/env': 16.2.10 '@swc/helpers': 0.5.15 - baseline-browser-mapping: 2.10.40 - caniuse-lite: 1.0.30001799 + baseline-browser-mapping: 2.10.43 + caniuse-lite: 1.0.30001806 postcss: 8.4.31 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) styled-jsx: 5.1.6(react@19.2.7) optionalDependencies: - '@next/swc-darwin-arm64': 16.2.9 - '@next/swc-darwin-x64': 16.2.9 - '@next/swc-linux-arm64-gnu': 16.2.9 - '@next/swc-linux-arm64-musl': 16.2.9 - '@next/swc-linux-x64-gnu': 16.2.9 - '@next/swc-linux-x64-musl': 16.2.9 - '@next/swc-win32-arm64-msvc': 16.2.9 - '@next/swc-win32-x64-msvc': 16.2.9 + '@next/swc-darwin-arm64': 16.2.10 + '@next/swc-darwin-x64': 16.2.10 + '@next/swc-linux-arm64-gnu': 16.2.10 + '@next/swc-linux-arm64-musl': 16.2.10 + '@next/swc-linux-x64-gnu': 16.2.10 + '@next/swc-linux-x64-musl': 16.2.10 + '@next/swc-win32-arm64-msvc': 16.2.10 + '@next/swc-win32-x64-msvc': 16.2.10 '@opentelemetry/api': 1.9.1 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - nypm@0.6.7: + nypm@0.6.8: dependencies: citty: 0.2.2 pathe: 2.0.3 @@ -5370,7 +6066,24 @@ snapshots: powershell-utils: 0.1.0 wsl-utils: 0.3.1 - package-manager-detector@1.6.0: {} + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + package-manager-detector@1.7.0: {} parse-entities@4.0.2: dependencies: @@ -5388,11 +6101,13 @@ snapshots: path-data-parser@0.1.0: {} + path-exists@4.0.0: {} + path-key@3.1.1: {} path-scurry@2.0.2: dependencies: - lru-cache: 11.5.1 + lru-cache: 11.5.2 minipass: 7.1.3 pathe@2.0.3: {} @@ -5401,6 +6116,8 @@ snapshots: picocolors@1.1.1: {} + picomatch@4.0.5: {} + pkce-challenge@5.0.1: {} pkg-types@2.3.1: @@ -5418,79 +6135,83 @@ snapshots: postcss@8.4.31: dependencies: - nanoid: 3.3.15 + nanoid: 3.3.16 picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.15: + postcss@8.5.19: dependencies: - nanoid: 3.3.15 + nanoid: 3.3.16 picocolors: 1.1.1 source-map-js: 1.2.1 powershell-utils@0.1.0: {} + prelude-ls@1.2.1: {} + property-information@7.2.0: {} proxy-from-env@2.1.0: {} - radix-ui@1.6.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-accessible-icon': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-accordion': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-alert-dialog': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-arrow': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-aspect-ratio': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-avatar': 1.2.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-checkbox': 1.3.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-collapsible': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + punycode@2.3.1: {} + + radix-ui@1.6.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-accessible-icon': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-accordion': 1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-alert-dialog': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-aspect-ratio': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-avatar': 1.2.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-checkbox': 1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collapsible': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context-menu': 2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-dialog': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context-menu': 2.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dialog': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-dropdown-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dropdown-menu': 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-form': 0.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-hover-card': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-label': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-menubar': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-navigation-menu': 1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-one-time-password-field': 0.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-password-toggle-field': 0.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-popover': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-progress': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-radio-group': 1.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-scroll-area': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-select': 2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-separator': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-slider': 1.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-form': 0.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-hover-card': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-label': 2.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-menu': 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-menubar': 1.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-navigation-menu': 1.2.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-one-time-password-field': 0.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-password-toggle-field': 0.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popover': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-progress': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-radio-group': 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-scroll-area': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-select': 2.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-separator': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slider': 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-switch': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-tabs': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-toast': 1.2.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-toggle': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-toggle-group': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-toolbar': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-tooltip': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-switch': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tabs': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toast': 1.2.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle-group': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toolbar': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tooltip': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-escape-keydown': 1.1.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: @@ -5549,14 +6270,14 @@ snapshots: readdirp@5.0.0: {} - recharts@3.9.0(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react-is@19.2.5)(react@19.2.7)(redux@5.0.1): + recharts@3.9.2(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react-is@19.2.5)(react@19.2.7)(redux@5.0.1): dependencies: '@reduxjs/toolkit': 2.12.0(react-redux@9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1))(react@19.2.7) clsx: 2.1.1 decimal.js-light: 2.5.1 es-toolkit: 1.49.0 eventemitter3: 5.0.4 - immer: 10.2.0 + immer: 11.1.11 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) react-is: 19.2.5 @@ -5581,13 +6302,13 @@ snapshots: rehype-raw@7.0.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-raw: 9.1.0 vfile: 6.0.3 rehype-sanitize@6.0.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-sanitize: 5.0.2 remark-gfm@4.0.1: @@ -5612,7 +6333,7 @@ snapshots: remark-rehype@11.1.2: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/mdast': 4.0.4 mdast-util-to-hast: 13.2.1 unified: 11.0.5 @@ -5649,8 +6370,7 @@ snapshots: semver@7.8.4: {} - semver@7.8.5: - optional: true + semver@7.8.5: {} sharp@0.34.5: dependencies: @@ -5752,11 +6472,11 @@ snapshots: tailwind-merge@3.6.0: {} - tailwindcss-animate@1.0.7(tailwindcss@4.3.1): + tailwindcss-animate@1.0.7(tailwindcss@4.3.3): dependencies: - tailwindcss: 4.3.1 + tailwindcss: 4.3.3 - tailwindcss@4.3.1: {} + tailwindcss@4.3.3: {} tapable@2.3.3: {} @@ -5766,27 +6486,66 @@ snapshots: tinyexec@1.2.4: {} + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + trim-lines@3.0.1: {} trough@2.2.0: {} + ts-api-utils@2.5.0(typescript@7.0.2): + dependencies: + typescript: 7.0.2 + ts-dedent@2.3.0: {} tslib@2.8.1: {} - typescript@6.0.3: {} - - ultracite@7.8.3: + type-check@0.4.0: dependencies: - '@clack/prompts': 1.6.0 + prelude-ls: 1.2.1 + + typescript@7.0.2: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 + + ultracite@7.9.4(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2): + dependencies: + '@clack/prompts': 1.7.0 + '@typescript-eslint/utils': 8.64.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) commander: 15.0.0 cross-spawn: 7.0.6 deepmerge: 4.3.1 glob: 13.0.6 jsonc-parser: 3.3.1 - nypm: 0.6.7 + nypm: 0.6.8 yaml: 2.9.0 zod: 4.4.3 + transitivePeerDependencies: + - eslint + - supports-color + - typescript undici-types@8.3.0: {} @@ -5823,6 +6582,10 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.7): dependencies: react: 19.2.7 @@ -5885,7 +6648,7 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - wavesurfer.js@7.12.8: {} + wavesurfer.js@7.12.10: {} web-namespaces@2.0.1: {} @@ -5893,6 +6656,8 @@ snapshots: dependencies: isexe: 2.0.0 + word-wrap@1.2.5: {} + wsl-utils@0.3.1: dependencies: is-wsl: 3.1.1 @@ -5900,6 +6665,8 @@ snapshots: yaml@2.9.0: {} + yocto-queue@0.1.0: {} + zod@4.4.3: {} zwitch@2.0.4: {}