From ec6e975d0e73838b11d43a3cb3ebc4738db5ca34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Oct 2026 11:30:55 +0000 Subject: [PATCH 1/4] deps(python): Bump the python-minor group across 1 directory with 9 updates Bumps the python-minor group with 9 updates in the / directory: | Package | From | To | | --- | --- | --- | | [fastmcp](https://github.com/PrefectHQ/fastmcp) | `4.1.1` | `4.1.4` | | [alembic](https://github.com/sqlalchemy/alembic) | `1.20.0` | `1.20.1` | | [pandas](https://github.com/pandas-dev/pandas) | `3.0.6` | `3.1.0` | | [pydantic-settings](https://github.com/pydantic/pydantic-settings) | `2.16.0` | `2.16.1` | | [cachetools](https://github.com/tkem/cachetools) | `7.2.3` | `7.2.5` | | [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) | `1.4.0` | `1.4.1` | | [ruff](https://github.com/astral-sh/ruff) | `0.17.1` | `0.17.2` | | [plotnine](https://github.com/has2k1/plotnine) | `0.15.7` | `0.15.8` | | [google-adk](https://github.com/google/adk-python) | `2.11.0` | `2.13.0` | Updates `fastmcp` from 4.1.1 to 4.1.4 - [Release notes](https://github.com/PrefectHQ/fastmcp/releases) - [Changelog](https://github.com/PrefectHQ/fastmcp/blob/main/docs/changelog.mdx) - [Commits](https://github.com/PrefectHQ/fastmcp/compare/v4.1.1...v4.1.4) Updates `alembic` from 1.20.0 to 1.20.1 - [Release notes](https://github.com/sqlalchemy/alembic/releases) - [Changelog](https://github.com/sqlalchemy/alembic/blob/main/CHANGES) - [Commits](https://github.com/sqlalchemy/alembic/commits) Updates `pandas` from 3.0.6 to 3.1.0 - [Release notes](https://github.com/pandas-dev/pandas/releases) - [Commits](https://github.com/pandas-dev/pandas/compare/v3.0.6...v3.1.0) Updates `pydantic-settings` from 2.16.0 to 2.16.1 - [Release notes](https://github.com/pydantic/pydantic-settings/releases) - [Commits](https://github.com/pydantic/pydantic-settings/compare/v2.16.0...v2.16.1) Updates `cachetools` from 7.2.3 to 7.2.5 - [Changelog](https://github.com/tkem/cachetools/blob/master/CHANGELOG.rst) - [Commits](https://github.com/tkem/cachetools/compare/v7.2.3...v7.2.5) Updates `pytest-asyncio` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases) - [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v1.4.0...v1.4.1) Updates `ruff` from 0.17.1 to 0.17.2 - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.17.1...0.17.2) Updates `plotnine` from 0.15.7 to 0.15.8 - [Release notes](https://github.com/has2k1/plotnine/releases) - [Changelog](https://github.com/has2k1/plotnine/blob/v0.15.8/doc/changelog.qmd) - [Commits](https://github.com/has2k1/plotnine/compare/v0.15.7...v0.15.8) Updates `google-adk` from 2.11.0 to 2.13.0 - [Release notes](https://github.com/google/adk-python/releases) - [Changelog](https://github.com/google/adk-python/blob/main/CHANGELOG.md) - [Commits](https://github.com/google/adk-python/compare/v2.11.0...v2.13.0) --- updated-dependencies: - dependency-name: alembic dependency-version: 1.20.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: python-minor - dependency-name: cachetools dependency-version: 7.2.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: python-minor - dependency-name: fastmcp dependency-version: 4.1.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: python-minor - dependency-name: google-adk dependency-version: 2.13.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: python-minor - dependency-name: pandas dependency-version: 3.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: python-minor - dependency-name: plotnine dependency-version: 0.15.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: python-minor - dependency-name: pydantic-settings dependency-version: 2.16.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: python-minor - dependency-name: pytest-asyncio dependency-version: 1.4.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: python-minor - dependency-name: ruff dependency-version: 0.17.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: python-minor ... Signed-off-by: dependabot[bot] --- pyproject.toml | 2 +- uv.lock | 332 ++++++++++++++----------------------------------- 2 files changed, 96 insertions(+), 238 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index aaa87c95743..d5e5507bb6f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -139,7 +139,7 @@ lib-letsplot = ["lets-plot>=4.11.0", "pandas>=3.0.5", "scipy>=1.18.0", "scikit-l # service must never do. tests/unit/agents/code/test_treesitter.py pins both, and # agents/Dockerfile keeps only the grammars the code profiles load. agents = [ - "google-adk==2.11.0", + "google-adk==2.13.0", "anthropic[vertex]>=1.8.0", "tree-sitter==0.26.0", "tree-sitter-language-pack==0.13.0", diff --git a/uv.lock b/uv.lock index 6ebed25bf89..1d4a1195144 100644 --- a/uv.lock +++ b/uv.lock @@ -163,16 +163,16 @@ wheels = [ [[package]] name = "alembic" -version = "1.20.0" +version = "1.20.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mako" }, { name = "sqlalchemy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/aa/02910bdb8e2f1444f6654d5b296cd827d126f82209050ee7b1000f92ac4b/alembic-1.20.0.tar.gz", hash = "sha256:db505480647bc60386c5369402f4a57a506b7539c9e9ef5e270d45cbbe4939bf", size = 2093272, upload-time = "2026-09-11T19:09:11.126Z" } +sdist = { url = "https://files.pythonhosted.org/packages/93/1c/00909e0fbebad2121d916fdcab044cae988146680e9ee4387ba25fef8747/alembic-1.20.1.tar.gz", hash = "sha256:83490def1f723413db5096591f7609ac52544805be93849170088ed04f5a2e0b", size = 2103576, upload-time = "2026-10-22T03:24:35.93Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/78a89b55b0904d222183164e079b4ca56208e94eff1d35ad1f1ad5be9b06/alembic-1.20.0-py3-none-any.whl", hash = "sha256:77eb101048d95f982c0353e9233404889dcd7a6fc244c107836c0e2fc9cf7d9d", size = 268719, upload-time = "2026-09-11T19:09:12.88Z" }, + { url = "https://files.pythonhosted.org/packages/11/3c/8740f5b4e1bd57333e4f84265b9b6363da4f7cf70f5622dd1e2fde7a0bd5/alembic-1.20.1-py3-none-any.whl", hash = "sha256:9970712cfb81ea26c8658d378aaa5b8f6ff9146e5bb111d70497c164016cf598", size = 270355, upload-time = "2026-10-22T03:24:37.667Z" }, ] [[package]] @@ -299,9 +299,7 @@ all = [ { name = "matplotlib-venn" }, { name = "mypy" }, { name = "plotly" }, - { name = "plotnine", version = "0.15.7", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'emscripten'" }, - { name = "plotnine", version = "0.15.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15' and sys_platform == 'emscripten'" }, - { name = "plotnine", version = "0.16.0a12", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15' and sys_platform == 'emscripten'" }, + { name = "plotnine" }, { name = "pre-commit" }, { name = "pyarrow" }, { name = "pygal" }, @@ -386,9 +384,7 @@ lib-plotly = [ lib-plotnine = [ { name = "numpy" }, { name = "pandas" }, - { name = "plotnine", version = "0.15.7", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'emscripten'" }, - { name = "plotnine", version = "0.15.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15' and sys_platform == 'emscripten'" }, - { name = "plotnine", version = "0.16.0a12", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15' and sys_platform == 'emscripten'" }, + { name = "plotnine" }, { name = "scikit-learn" }, { name = "scipy" }, { name = "statsmodels" }, @@ -420,9 +416,7 @@ plotting = [ { name = "lets-plot" }, { name = "matplotlib-venn" }, { name = "plotly" }, - { name = "plotnine", version = "0.15.7", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'emscripten'" }, - { name = "plotnine", version = "0.15.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15' and sys_platform == 'emscripten'" }, - { name = "plotnine", version = "0.16.0a12", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15' and sys_platform == 'emscripten'" }, + { name = "plotnine" }, { name = "pygal" }, { name = "scikit-learn" }, { name = "scipy" }, @@ -469,7 +463,7 @@ requires-dist = [ { name = "cloud-sql-python-connector", extras = ["asyncpg", "pg8000"], specifier = ">=1.20.4" }, { name = "fastapi", specifier = ">=0.141.1" }, { name = "fastmcp", specifier = ">=3.4.7" }, - { name = "google-adk", marker = "extra == 'agents'", specifier = "==2.11.0" }, + { name = "google-adk", marker = "extra == 'agents'", specifier = "==2.13.0" }, { name = "google-auth", extras = ["requests"], specifier = ">=2.48.0" }, { name = "google-cloud-storage", specifier = ">=3.13.1" }, { name = "highcharts-core", marker = "extra == 'lib-highcharts'", specifier = ">=1.11.0" }, @@ -707,14 +701,15 @@ wheels = [ [[package]] name = "authlib" -version = "1.6.12" +version = "1.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, + { name = "joserfc" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d3/30/6691fdc63b35f54a5a65e04fa1e59d827f4d4e8f4a39678ba7d3088ce0c8/authlib-1.6.12.tar.gz", hash = "sha256:0656d8482f28fc8221929d5f35b2bde5d13e10555ebc06b4561b0d622e83b1bd", size = 165368, upload-time = "2026-05-04T08:11:31.826Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/dc/087c08741a98d234c9b98beef8a28d3c309f814d14b6156422c5815bf6d6/authlib-1.9.1.tar.gz", hash = "sha256:5c7d9848f47cac340f060f76ae0bc09b59f5c8e89cc1bb032d4370a0c55b5974", size = 182700, upload-time = "2026-10-14T14:17:52.307Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/51/9b0b5cd4cf683a02db937a6f9bbebcdc9c56558a7bb3763ce7d3512103c3/authlib-1.6.12-py2.py3-none-any.whl", hash = "sha256:e9229ad7fde610b139dd12f5edbe97eab9ee78bfb85691247e767727850b99ab", size = 244473, upload-time = "2026-05-04T08:11:30.354Z" }, + { url = "https://files.pythonhosted.org/packages/39/44/a46089266de32a0d809023b6bf31b80a1f26a46954446a7ce695a565cbea/authlib-1.9.1-py2.py3-none-any.whl", hash = "sha256:8b9be8b1e5174dcbf5520e984f1a22dc2edae5b6e40a61d4cedbf86b97e9e69b", size = 266042, upload-time = "2026-10-14T14:17:50.695Z" }, ] [[package]] @@ -766,11 +761,11 @@ wheels = [ [[package]] name = "cachetools" -version = "7.2.3" +version = "7.2.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/88/27fe0785c7a9aa285c753db0b4ea967adbf76128fede66379c35a4b8d054/cachetools-7.2.3.tar.gz", hash = "sha256:e6fafd06de4a5bd915ff7d247588ea497e28c9a568c62db8678d9a5f11515a9a", size = 43727, upload-time = "2026-10-17T11:41:25.471Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/e5/43f9a3f926cc182a0ee95cfd53ec0b5ac0e2cdafa184af6fc50166fd03fd/cachetools-7.2.5.tar.gz", hash = "sha256:a2223b80de434291ddf4c105354e533bbaf4d29382f5d1bc3e4b9332cb31061e", size = 46088, upload-time = "2026-10-25T08:53:17.172Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/dd/044062b61d94c59c29ad0da6918e6a21106f5b115096e9c87081cdd96167/cachetools-7.2.3-py3-none-any.whl", hash = "sha256:85ef9d2703918bedf7f88caa0b06bb3bb7a71360eb0b05c20796b89fede6e30c", size = 18352, upload-time = "2026-10-17T11:41:24.161Z" }, + { url = "https://files.pythonhosted.org/packages/4f/bd/bd8f45fbdf05ce06dbadda61bf0a09844bb0cf0a048d7f5c4a368d437e24/cachetools-7.2.5-py3-none-any.whl", hash = "sha256:9033308cc26b678784a27b096aab1541b9e698d785c3e9918b691ccd458d88df", size = 19132, upload-time = "2026-10-25T08:53:15.638Z" }, ] [[package]] @@ -1262,19 +1257,19 @@ wheels = [ [[package]] name = "fastmcp" -version = "4.1.1" +version = "4.1.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastmcp-slim", extra = ["client", "server"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/82/6ee4c54b3d78c282c3766870e12a8cd802b0f360ed834463b63afe7d5cf8/fastmcp-4.1.1.tar.gz", hash = "sha256:23b8fa7cfc30779fdedbcb78c62f8c621d5d5708f3f29188b7a05e71f7c4a34a", size = 42689037, upload-time = "2026-10-18T02:14:16.792Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/1b/84d94cc34792752b2636ddddf9821f8d512fbae1c1ee76aa554c87ec1165/fastmcp-4.1.4.tar.gz", hash = "sha256:2398be351ec343466f0510dc9e281d3a848bc3370c17b0a2807fcccc6f1ffe3b", size = 42788696, upload-time = "2026-10-26T06:25:20.627Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/5d/fc8bf50cb8907b2e9e82c91de81df453fa83d8775670a4775f61f2e0441b/fastmcp-4.1.1-py3-none-any.whl", hash = "sha256:a82721d40e2a2e49637f4e30ec5b6892d2ccc02d3684ca74429fef995d6d29b2", size = 8153, upload-time = "2026-10-18T02:14:14.302Z" }, + { url = "https://files.pythonhosted.org/packages/88/1d/eaa514b648db51fc31ef002519c9d406beaaba4a7c53716610e1f886f5a9/fastmcp-4.1.4-py3-none-any.whl", hash = "sha256:047c1c1842f42e4ffe2e153568a25851d442cb7739a41a56352b7a2852ff8738", size = 8232, upload-time = "2026-10-26T06:25:17.76Z" }, ] [[package]] name = "fastmcp-slim" -version = "4.1.1" +version = "4.1.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mcp-types" }, @@ -1285,9 +1280,9 @@ dependencies = [ { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f7/15/ce19330e59e8a6b3aa9b077666e297940d059c9de6beb37069b76f8e6859/fastmcp_slim-4.1.1.tar.gz", hash = "sha256:5d23d2fb7df131097fe87b642f9ad27dde72ebbbd73369c512d96cbaf505e836", size = 735236, upload-time = "2026-10-18T02:12:34.305Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/a0/475de742162be2b75b9ac0a7d3e1396f797ca97789a8373ed60581a222ee/fastmcp_slim-4.1.4.tar.gz", hash = "sha256:d1034317e4e2960dd619930c01baabbc77e60825f55c09f93471afa427a68cd7", size = 753435, upload-time = "2026-10-26T06:23:55.282Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/85/a609dc20e6cb67f11dcef8ef6c707d8f7783c84ff5256cc4c4ccdeb571cd/fastmcp_slim-4.1.1-py3-none-any.whl", hash = "sha256:64bd63e8222305ab1408d11d3498bcc94dc14629f4d55fe5399ccdb496509122", size = 912169, upload-time = "2026-10-18T02:12:32.709Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/bbbf9cb1a92e7f98c34845c8f06f21516fd38efdcd579ecbc0131bb6d4d5/fastmcp_slim-4.1.4-py3-none-any.whl", hash = "sha256:0f611b246504ff0d0ee7ab77123f529b5c9aed68670eaee9c6f960a59bb024da", size = 935728, upload-time = "2026-10-26T06:23:53.486Z" }, ] [package.optional-dependencies] @@ -1298,12 +1293,13 @@ client = [ { name = "httpx2" }, { name = "mcp" }, { name = "opentelemetry-api" }, - { name = "py-key-value-aio", extra = ["filetree", "keyring", "memory"] }, + { name = "py-key-value-aio", extra = ["filetree", "memory"] }, { name = "starlette" }, ] server = [ { name = "authlib" }, { name = "beartype", version = "0.23.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" }, + { name = "certifi" }, { name = "cyclopts" }, { name = "exceptiongroup" }, { name = "griffelib" }, @@ -1315,7 +1311,7 @@ server = [ { name = "openapi-pydantic" }, { name = "opentelemetry-api" }, { name = "packaging" }, - { name = "py-key-value-aio", extra = ["filetree", "keyring", "memory"] }, + { name = "py-key-value-aio", extra = ["filetree", "memory"] }, { name = "pyperclip" }, { name = "python-multipart" }, { name = "pyyaml" }, @@ -1421,12 +1417,13 @@ wheels = [ [[package]] name = "google-adk" -version = "2.11.0" +version = "2.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiosqlite" }, { name = "authlib" }, { name = "click" }, + { name = "cryptography" }, { name = "fastapi" }, { name = "google-auth", extra = ["pyopenssl"] }, { name = "google-genai" }, @@ -1448,9 +1445,9 @@ dependencies = [ { name = "watchdog" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/50/7e9d83adc67df40752598bddcc24d9b6696aae4d61c45b6271d53ec2df53/google_adk-2.11.0.tar.gz", hash = "sha256:59258cec8d79306192bf65e0d43fa64b764f831e3b74ed3b8e6fe9df16a1e2c8", size = 4197119, upload-time = "2026-10-02T00:46:25.232Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/2b/9616b2a498b8f247c1a9bef8c630e16151dd3a8ed768fa3118a961b19689/google_adk-2.13.0.tar.gz", hash = "sha256:bc2389c9d41bfadd9f0c87d6bfd8d5c7eaf1c449e8906079600b8f3752ddaa88", size = 4444119, upload-time = "2026-10-23T18:26:34.717Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/44/b4f54c46f1c6a32318891ff2be25d854f4c5b5b9ed8ac507ed9466f1ccc8/google_adk-2.11.0-py3-none-any.whl", hash = "sha256:4774e1fc7bb37727e56142e7d41e9035949ebfc876a2b15d5505969b02798bcd", size = 4871441, upload-time = "2026-10-02T00:46:23.264Z" }, + { url = "https://files.pythonhosted.org/packages/e8/1a/6da4a8048b7cd47681928daed5d4d1c3ef971b3847fcc3cbc63f94df39d2/google_adk-2.13.0-py3-none-any.whl", hash = "sha256:bec4c4ee0aa3e63a2243c23365c5aef666ea539bc48c78dfbbf004dbddfc5f00", size = 5157034, upload-time = "2026-10-23T18:26:31.805Z" }, ] [[package]] @@ -1540,7 +1537,7 @@ wheels = [ [[package]] name = "google-genai" -version = "2.29.0" +version = "2.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1554,9 +1551,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/21/2b/977616ef350e51d3d263eb43be23a91be793544cd937b51c0fac313be233/google_genai-2.29.0.tar.gz", hash = "sha256:124d3eef9ff6b9e4ea548f95ff5b7721683e7990c0a936b006d9e2ab61ea897d", size = 706902, upload-time = "2026-10-07T22:22:57.153Z" } +sdist = { url = "https://files.pythonhosted.org/packages/22/89/4621aee9cb183efa5b8a187f0e315c49fddcfb8cd7f238b4a3bf4946dcd0/google_genai-2.34.0.tar.gz", hash = "sha256:8e182cf3f73384167b96861cf0aef5102b5070f2db1ea11002879e913806ef37", size = 791853, upload-time = "2026-10-29T06:43:23.706Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/27/a3dede4a1deb21f7fde6a171daa039bf0ea149a5e7826a785e8f109a6e13/google_genai-2.29.0-py3-none-any.whl", hash = "sha256:7adda55cb6746fb46d38ad3202c3372840b96eede964b5fb3df1469cd4998570", size = 1167603, upload-time = "2026-10-07T22:22:55.215Z" }, + { url = "https://files.pythonhosted.org/packages/2e/42/c24db865d9231b1c672ea26242a4dcf340e200bb1a9823dce4aa9c4c07a4/google_genai-2.34.0-py3-none-any.whl", hash = "sha256:a4cb02aebc4c357f863d2938132f0e3f7967fc5e00b8a4078673f68aad340665", size = 1258155, upload-time = "2026-10-29T06:43:21.121Z" }, ] [[package]] @@ -1777,48 +1774,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a1/d3/20a61a248feb1249cd81f83ce731f0bdf5170ed96a08a298f7081cda9e90/interface_meta-2.0.1-py3-none-any.whl", hash = "sha256:f38016bef9a4429b6d0792d809be7b65e9781820c674bf7f463999086b6e6323", size = 15330, upload-time = "2026-04-30T22:35:35.201Z" }, ] -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - [[package]] name = "jinja2" version = "3.1.6" @@ -1950,23 +1905,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/86/73fa07ff24a29e14f3f44bc5729ef9897cb594dee983923a2bc7ebc4187f/kaleido-1.5.0-py3-none-any.whl", hash = "sha256:de301b73cc9fd6311e54b47087d3a7a5da3b7681ee9175e23b45dcffb4432ff2", size = 55816, upload-time = "2026-10-06T15:28:58.822Z" }, ] -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - [[package]] name = "kiwisolver" version = "1.4.9" @@ -2276,15 +2214,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/d2/4ffcaa27c8a4b4f9ad456da4821c76dfbdfada23e8210cd4d80e1eb3236a/mizani-0.14.3-py3-none-any.whl", hash = "sha256:6d2ca9b1b8366ff85668f0cc1b6095f1e702e26e66f132c4f02a949efa32a688", size = 133433, upload-time = "2025-10-30T20:16:51.218Z" }, ] -[[package]] -name = "more-itertools" -version = "10.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, -] - [[package]] name = "multidict" version = "6.9.1" @@ -2502,14 +2431,14 @@ wheels = [ [[package]] name = "openapi-pydantic" -version = "0.5.1" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892, upload-time = "2025-01-08T19:29:27.083Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/32/0c9bd3e4e847cd6117b64dbef4cf810faa9cdbd6689323b811569cc8a1b8/openapi_pydantic-0.6.0.tar.gz", hash = "sha256:11f3ac6ad41521fc156381ec587246b0c0ecea523bd9565ed74d3e7fac9d91cd", size = 1667049, upload-time = "2026-10-02T12:20:00.508Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381, upload-time = "2025-01-08T19:29:25.275Z" }, + { url = "https://files.pythonhosted.org/packages/31/57/9d3eb823a03c33e9d4c810e6a3276e3a6dc820a7fdc0961db2c04e613817/openapi_pydantic-0.6.0-py3-none-any.whl", hash = "sha256:8bd6d548a250da37db96f461bcd52b3468e8834ad7d6db14b00a6b2ebbd163a3", size = 96931, upload-time = "2026-10-02T12:19:58.917Z" }, ] [[package]] @@ -2584,47 +2513,47 @@ wheels = [ [[package]] name = "pandas" -version = "3.0.6" +version = "3.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "python-dateutil" }, { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e2/17/d7b106e05bfa642e8694451e7d3d759c6a241c5386a5d962e4f66c047e06/pandas-3.0.6.tar.gz", hash = "sha256:66b07ef7315a31bfe1089cd3d71a7de781c9dca986762d0b4fe7c0ef17465d10", size = 4667686, upload-time = "2026-09-17T23:23:18.345Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/55/1a8875395b05ccd572cbca0b9255dcd2db6e6508e632a558c1a6884b39ad/pandas-3.0.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ee913a91669056c1de1a6b733fbfeab711de9e54e3bee2dfa5fe79d9457247d1", size = 10492213, upload-time = "2026-09-17T23:21:40.746Z" }, - { url = "https://files.pythonhosted.org/packages/35/61/47ae13476995cc8a40cd609e93e7cf11f273d8692925c2903cb6d38aa0d1/pandas-3.0.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ff51a4459ed036e93d1eb1bb5e6e7b28685d3cb6b7c12b91c05b31024e234729", size = 10156618, upload-time = "2026-09-17T23:21:44.142Z" }, - { url = "https://files.pythonhosted.org/packages/bc/f2/cc5f2adb8d6e86a85d9fb5128f8cf205a61189336f70d1f7faf0d1b53ec9/pandas-3.0.6-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654aae059295dbba6ecd2328ca12712a2cf1676214c8699f1c29213f7ccf9c34", size = 10375479, upload-time = "2026-09-17T23:21:47.159Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ba/ffdcb19be4ff6bfe7d969e7cef2c567c633df5a3a1cc1053394ad053bca8/pandas-3.0.6-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f51d7f651c8054c5e82a69265c98082e795d1442df7ca6edc3a545d61214b1", size = 10783244, upload-time = "2026-09-17T23:21:50.367Z" }, - { url = "https://files.pythonhosted.org/packages/77/5b/e150075b2c6eb69fae896f2d9239bc6ed07db97735971d53d66de6553460/pandas-3.0.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:22172a92e7ee678ec0140c7af4fc9366b55413834a1cd86af78b3caa0b0574de", size = 11382223, upload-time = "2026-09-17T23:21:53.355Z" }, - { url = "https://files.pythonhosted.org/packages/d6/8a/b441c587dc7355bf6e1f68a91b4f76a6c29740f0c23be3acc5d4ebbeea6d/pandas-3.0.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:583be68728a31d0d750d5b8d9e00f02b153df0d4655f858bde93cb84cfc4227c", size = 11854881, upload-time = "2026-09-17T23:21:56.342Z" }, - { url = "https://files.pythonhosted.org/packages/b7/e9/f43410fada510b43fec09993c08f552086c3d247d3ee801a678f3cb10ea5/pandas-3.0.6-cp314-cp314-win_amd64.whl", hash = "sha256:77ccbe5057aece6fc172b9b77f19c04335af6882bc2e10c8f3ee4e6bfb3da553", size = 9791672, upload-time = "2026-09-17T23:21:59.332Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9e/db14c059c21f9baa1907d436f8bf30e0c76c6288225c5e8b79a08ba8b2c5/pandas-3.0.6-cp314-cp314-win_arm64.whl", hash = "sha256:fb625f426b375bcc96e3a04c5d5d266cd7be6ae5d6866e0e703382ab5164068c", size = 9121246, upload-time = "2026-09-17T23:22:02.123Z" }, - { url = "https://files.pythonhosted.org/packages/67/ba/bad0f8dac020ab38a8637fddab01a57a82da7a496a6e6f19590aad53ab62/pandas-3.0.6-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9e492cd4bdba6778de4fe0df7f4590c012161ebcf9902dce01b01dc683105514", size = 10920612, upload-time = "2026-09-17T23:22:05.404Z" }, - { url = "https://files.pythonhosted.org/packages/c4/a9/b500982e9aac6d52a58da4ad3f11e14168a315b06906b3f397c427878065/pandas-3.0.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d7dcd21238cbb4828ff148481ba01cac8946dc5121457b5aeba28636f8f99a60", size = 10570213, upload-time = "2026-09-17T23:22:08.44Z" }, - { url = "https://files.pythonhosted.org/packages/4b/fa/e6ecd0073c98be8f840ac3125b955272835d7d9fd69f5944383b164deb5e/pandas-3.0.6-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff482fa91fa2bafd92e8fe66ce3645c851824310f295c1f0a2f96e928fc4541", size = 10252356, upload-time = "2026-09-17T23:22:11.302Z" }, - { url = "https://files.pythonhosted.org/packages/04/f5/001e230a7a7803590d9275a1a3f7e1bb605e3a495cfe5e8d3a532090621b/pandas-3.0.6-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:db7ec631f26223beee8e5c9e0b8f23c24d8197bbd1d982421d4e3188bea51965", size = 10655952, upload-time = "2026-09-17T23:22:14.283Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ab/bab587148a3852c96aae26c4b5f9e04ce2221801ad94e166b4fbf969ede0/pandas-3.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd75ed0c840f709fc2ae26ddd9534ac77ca1a48ac0cce521a74acaa85f3340a7", size = 11274177, upload-time = "2026-09-17T23:22:17.352Z" }, - { url = "https://files.pythonhosted.org/packages/f3/32/74b48d87df2b80892d713c149abfe36d5db4de41b4eccb042a2bc07dafc1/pandas-3.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ef738d71d1059245b6bb03e312be06d8b3821326a83486c1ad03b9aba3710e44", size = 11719041, upload-time = "2026-09-17T23:22:20.227Z" }, - { url = "https://files.pythonhosted.org/packages/f6/c6/d64b72d64d7eb0fad9fe424d34138e45dee70ddbea1360dcd0adf30e28f6/pandas-3.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:429d9df32731ab01383ed98f2baa7a60368090d1a94fc06019a12062510e8630", size = 10191388, upload-time = "2026-09-17T23:22:23.524Z" }, - { url = "https://files.pythonhosted.org/packages/7a/30/5e5b2ccabeca73ae2b03fc82bca3eabb7466cf43737f05ac08d591665d47/pandas-3.0.6-cp314-cp314t-win_arm64.whl", hash = "sha256:a4dbd4dc65cbe645b92b8785d0f96dd7311010dc6606cf620e51b07b8788a12a", size = 9387796, upload-time = "2026-09-17T23:22:26.64Z" }, - { url = "https://files.pythonhosted.org/packages/b6/77/47c5fb0be8bdd00116814c2c40d9ec42dbeb943865ef95130fe58a898226/pandas-3.0.6-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:50c44cbf5820b6b91a5f74aae04972472aefadd3cd9fbd1010409d85528bd570", size = 10483960, upload-time = "2026-09-17T23:22:30.071Z" }, - { url = "https://files.pythonhosted.org/packages/09/08/a310cb2fefe6d2b4623ab2150da818d93d4e73e33b58e4d163bb74243c5a/pandas-3.0.6-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:eb6900de08ac85f93ac4948aa6b80842eba555875337b8359035ac9c43e92d34", size = 10149432, upload-time = "2026-09-17T23:22:32.818Z" }, - { url = "https://files.pythonhosted.org/packages/54/36/6af478ec3a26d7754555cd62c3101c589c0931b1d85398e4fa5403910a1c/pandas-3.0.6-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e25e2e1adee99ddfada6f7206a79ae8e9c8a8861b0e3eaaba165006d3eef18e", size = 10360981, upload-time = "2026-09-17T23:22:35.621Z" }, - { url = "https://files.pythonhosted.org/packages/43/20/5ece0e9cb79a6473216620db6a90546f578001c2bd857b77c444b27acad1/pandas-3.0.6-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4ff44b2cb51cbd691c91f92c4ea6c71e34003f239ebd67c2e857dc898466b49c", size = 10776585, upload-time = "2026-09-17T23:22:38.427Z" }, - { url = "https://files.pythonhosted.org/packages/2f/b6/cd3038f31ade5e8d2b4e1c9549d4b31e4d598469562f47142b2ad9171c0a/pandas-3.0.6-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:5edd0a7abb0986ecce1ac81f56d99b6763f86aa6946dceb6c661224f90af5a19", size = 11385249, upload-time = "2026-09-17T23:22:41.18Z" }, - { url = "https://files.pythonhosted.org/packages/0a/87/05bb3003737f80375d7311774916a24d161e2e591abe8c672aed7813defc/pandas-3.0.6-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:1bcb3e9ed29e74a7439cedff9e2aefd3ea65de84d7de9ccb6c194192541bd60e", size = 11848595, upload-time = "2026-09-17T23:22:44.207Z" }, - { url = "https://files.pythonhosted.org/packages/c0/30/1c0d46acf236d19ef975e9cdd5d1e0dd54924b03f0ed8287096ad4a18152/pandas-3.0.6-cp315-cp315-win_amd64.whl", hash = "sha256:253e12cb9081b0afbac607920f6142975966bc315135e09de275fdbaa415d2de", size = 9783810, upload-time = "2026-09-17T23:22:47.097Z" }, - { url = "https://files.pythonhosted.org/packages/87/03/df3304a9c2833c4810e7f1c887b04105b24731f9deef8b5d5d04522a375b/pandas-3.0.6-cp315-cp315-win_arm64.whl", hash = "sha256:97274c9adf6255bb48c620cd6959805efa7f09ea2167f0e0ae006a448cd2fca7", size = 9116820, upload-time = "2026-09-17T23:22:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/47/25/d5f6cce5efa17c38e4f752a875b4a26d66cfadd529cb8c81672f0376d6a6/pandas-3.0.6-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:265f562fdd1079f69f3de96dd425c3405224038c0af4f920c54bd240ee2c4640", size = 10894614, upload-time = "2026-09-17T23:22:53.223Z" }, - { url = "https://files.pythonhosted.org/packages/ea/8b/e1876bfdc1df06bafc022d33202b5663bd86c81df2a6140aacafd0344669/pandas-3.0.6-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:c6e4aae3e9bea26c6c9a20d88d96c86ec4a99b4db5fd516bcb4e829ab2c0ee36", size = 10546973, upload-time = "2026-09-17T23:22:56.155Z" }, - { url = "https://files.pythonhosted.org/packages/e9/27/e0a27a5a5c27f7db66657b44def9e93121fd0ad4f0808355b9898fcbf204/pandas-3.0.6-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a77a1a44e4d88f1c6a2a64d3eb12efec8420875722e14279800b173a7c7c2804", size = 10239717, upload-time = "2026-09-17T23:22:59.603Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4f/4eadb7d86a921c1e8bc70916cfe601ed667c4169118d17d69958611c21f8/pandas-3.0.6-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86fa853a12e0b70927e2b1ee00d56d2224ec9cbb4b9d58348b5ad52d2f21150e", size = 10641275, upload-time = "2026-09-17T23:23:02.81Z" }, - { url = "https://files.pythonhosted.org/packages/c7/d1/eba72e9d905e84e79aefeefdcc6bc1abe15d9077973566643f4211636966/pandas-3.0.6-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:c826e9babb7790142c399f58599d8de679bea059d7b39c5b6efa2096fac37266", size = 11262919, upload-time = "2026-09-17T23:23:06.038Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8a/1c5bd2642b450e374b6191189f47c49538fe41f82348a66de6f647e6ab59/pandas-3.0.6-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:8fe77b408d82e2615674dfed62533b95e18a03610573877422aada4f625d4947", size = 11709148, upload-time = "2026-09-17T23:23:09.082Z" }, - { url = "https://files.pythonhosted.org/packages/73/3d/1b142bd0d0f1326a5d98c91c955b923cd1e06b5eecb428cd9d8817fa01c0/pandas-3.0.6-cp315-cp315t-win_amd64.whl", hash = "sha256:83e91d15738d7783c050197cef2f2cf82fc6353dae9865aa87ed1fa16aa4d55a", size = 10161518, upload-time = "2026-09-17T23:23:12.365Z" }, - { url = "https://files.pythonhosted.org/packages/0b/a3/6419c14da2adc1f09a6a183b8f91d7494d325b287f4ca984ac04f663638a/pandas-3.0.6-cp315-cp315t-win_arm64.whl", hash = "sha256:963ca21199097a84c7827c4678b04e30833084fbf8ef44fde3fa7180a29f8fa0", size = 9364529, upload-time = "2026-09-17T23:23:15.274Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/55/b1/a10c0d1a7e3ce44eb42635771004aa503e3b9ddde0bd39c524dda65f7eff/pandas-3.1.0.tar.gz", hash = "sha256:ecb9b2d8a817b1e1ba1fdb90c0abe9eab31b4fdf2562b7a56b86ac66ace17e49", size = 5933421, upload-time = "2026-10-22T11:57:03.087Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/a9/e82d44fe20dc61dd2e15bdd17c78aa0023d860d55620e4fe82f9e644a089/pandas-3.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a8c44d40cbb6d00128bf2d8f8bb66c8d561744b6d44686b5a1a01f3b3fd1245f", size = 11675868, upload-time = "2026-10-22T11:55:46.617Z" }, + { url = "https://files.pythonhosted.org/packages/83/38/6be618d98e2cbbb99a40349717532f0c2b6794ddc62f1199e3f83e45a40f/pandas-3.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:89b66823678cdd53cf97187e7cd6861402bdd147d11a383706093136a4e83f45", size = 11262553, upload-time = "2026-10-22T11:55:49.143Z" }, + { url = "https://files.pythonhosted.org/packages/06/5c/66e27fb0d691e835bd55b336bdf47312a541a66a17ba4d72a988d743bb79/pandas-3.1.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a8dc7780720b9e2182799ee86279d4cc7d1fd30b230ea1b202da5b6f7e14c3a", size = 11548155, upload-time = "2026-10-22T11:55:51.674Z" }, + { url = "https://files.pythonhosted.org/packages/37/1e/42e81484c5a6791ab1b8c52c1ff1ef0483c119f710ed58ee603d241b8792/pandas-3.1.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e4455cdcc03e42c17233184e8e4ea65c3ed2925d8b1d9d7dd8e6fced3f6e43c1", size = 12029136, upload-time = "2026-10-22T11:55:53.823Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c9/a3b4fd156457f3eb2585838cd56427a113eeeba679204de435a3224b8cc3/pandas-3.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:224a9267e5fa70c16bf4f8a317435eef98387940ce6c8c611f1f68f2c3415e10", size = 12575350, upload-time = "2026-10-22T11:55:56.006Z" }, + { url = "https://files.pythonhosted.org/packages/4e/25/23a017003d792117b297bbd87e35a1bb5658509f610ecd5aeaebb84762aa/pandas-3.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c261c7a808982557c2917d40ffd27916769f26aacb4e6775c01e4467bcd34c3b", size = 13096142, upload-time = "2026-10-22T11:55:58.434Z" }, + { url = "https://files.pythonhosted.org/packages/1c/d2/f4074b7d1234d112aaa23a796be1574441a9fa873707eb98312a237a6185/pandas-3.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:e77112cfcabb3bef7281a72170670db9a5b31409a1cd3199939abb1fba67a7ad", size = 10885958, upload-time = "2026-10-22T11:56:00.895Z" }, + { url = "https://files.pythonhosted.org/packages/25/9f/bcfc9e0ea07f5c7583e129e62cd0529747317977ac1b85878b80235bb322/pandas-3.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:6c51273b89784c9d54020ed2f60e20524169aef349bddf6cba9f5e83b46077a7", size = 10281257, upload-time = "2026-10-22T11:56:03.478Z" }, + { url = "https://files.pythonhosted.org/packages/05/e4/b3bf0b702c0a886e4abcfc2ebdc681310498592a10d9ca0ce5db6df47b70/pandas-3.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:84377fa82d0b40165379a0058d8a1a267ceb09e65160de6fcc75475d4d595884", size = 12134028, upload-time = "2026-10-22T11:56:05.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ff/9e08cf048d73f4dfdb10da859790cd1a4213465dec39ea367a606d580c4a/pandas-3.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:37f88576f3449bfd86c1052f6b42274b61d7250c8d9dcc55f9dc4de7fe9883ae", size = 11729164, upload-time = "2026-10-22T11:56:08.335Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ff/a0df60d70fac10006841457c30d35647ae0c93af33e420e5e047b15e058d/pandas-3.1.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:756fd67995f3efb6b25f8eaea95c0e893f84e0068e2c53c5fdbb65275041d4db", size = 11413041, upload-time = "2026-10-22T11:56:11.38Z" }, + { url = "https://files.pythonhosted.org/packages/55/04/e6742d85ba7d166a1a8a99ff9f4aa9e585fb24028522daa187859ce50b5d/pandas-3.1.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4d9252b57bded194d09cd5ebf18d9b4edac238b16f56984ea8281d2ecfe39cd", size = 11869427, upload-time = "2026-10-22T11:56:13.637Z" }, + { url = "https://files.pythonhosted.org/packages/0a/a1/c9f4915408ef10b541f31b7812ca038dd903572c163b7be7bdcece8a5110/pandas-3.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:461b79db058639e446ddf90dd7f4804fae385acf0669f6e31321333e14cb6bc5", size = 12430367, upload-time = "2026-10-22T11:56:15.829Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ed/636b9cc8d30b3e2027b43036dd5a8e9e7cfdd5aa86f4454235c5ef7f96db/pandas-3.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bfac077874a396b7ae61a307b27aa6f0256cf31c143b7f96ac622765754cc300", size = 12945204, upload-time = "2026-10-22T11:56:18.419Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5e/5c28942fa8c6cf2d8b4e941abee43a44ba0a123f9fef6411f0319aefddd5/pandas-3.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1340f823a1d189fae8365de314c4d3c2ea0173c61bca66cc76a91385c64d9d3d", size = 11297553, upload-time = "2026-10-22T11:56:20.934Z" }, + { url = "https://files.pythonhosted.org/packages/cd/a4/5991dfb2cccd233ae754f2fb37a4b1474d90595d83f6790467dc5a7d8041/pandas-3.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2bb3f8cc42fae0c0de5186778f4a4957656b9b3955aca48383abfdac44a83598", size = 10626068, upload-time = "2026-10-22T11:56:23.187Z" }, + { url = "https://files.pythonhosted.org/packages/77/26/5cd42609d8e6cb3991242116a0e499d09f29f5bcf574f72f0a4aba13a87e/pandas-3.1.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:04ca88727dfa86837030c493d7512bb1c008aa4f5f14149c5f8d3ff446791340", size = 11666949, upload-time = "2026-10-22T11:56:25.842Z" }, + { url = "https://files.pythonhosted.org/packages/97/5f/ca1ac99da700c215c52fb0f5063a7fbf14878c6e100f687704c21985640e/pandas-3.1.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:c0bfec4d188fb0c05bba47b9f9def87624b8c545913aa3585a28f19f27ce847e", size = 11255062, upload-time = "2026-10-22T11:56:28.201Z" }, + { url = "https://files.pythonhosted.org/packages/d3/eb/8d7aef062e7d1f48869288e5c5ae5d109923f37ed3bbd0fdcaecc87784e9/pandas-3.1.0-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bbfb087db83eb79ca348047961d367bdd4bf9820d90a5945f3652c7060da7363", size = 11547558, upload-time = "2026-10-22T11:56:30.492Z" }, + { url = "https://files.pythonhosted.org/packages/06/c4/a041c786bc29935afe44806f72c0111f2643cbac5cab761007a227e12e97/pandas-3.1.0-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b5ce18612ca955e7a95d0bacc20120aedf97e2fbbebbe4de029fc24479e79b8", size = 12026259, upload-time = "2026-10-22T11:56:33.219Z" }, + { url = "https://files.pythonhosted.org/packages/d3/4a/ce6690c7e6081c96857ad7dcbe3b978394d8db682610755919e61863516f/pandas-3.1.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:a13199463b129363701a540c7b79586581b246b10b6a7ad682c41b268e7991d5", size = 12563821, upload-time = "2026-10-22T11:56:35.402Z" }, + { url = "https://files.pythonhosted.org/packages/75/ec/ae440ecbfa8f5ba0230541b4519c1fed0c4293ff362f18f7f353b12e2dca/pandas-3.1.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:8e4de3fe8017e3445f1174707cd977e4e1154a704af2c060a1f648862f81acb9", size = 13086426, upload-time = "2026-10-22T11:56:37.666Z" }, + { url = "https://files.pythonhosted.org/packages/e7/1e/6459d4b0ed9c3d72b9ab63387ec11e1e658579e702e0ef117be8b5d7d0d1/pandas-3.1.0-cp315-cp315-win_amd64.whl", hash = "sha256:eee72bba8a72f0bf9522132e5c87cd10fce9d77440b50f65326b093ec9fb8d43", size = 10877850, upload-time = "2026-10-22T11:56:39.994Z" }, + { url = "https://files.pythonhosted.org/packages/f5/22/e5fad1828e5f923e80a8d7ba9187f687159004a10f99f95dd5346b04c54e/pandas-3.1.0-cp315-cp315-win_arm64.whl", hash = "sha256:9ea2983fe096a3d997c8c4b9f26775fa51e7fb251b10634bd7f87e6fe3542d52", size = 10276384, upload-time = "2026-10-22T11:56:42.193Z" }, + { url = "https://files.pythonhosted.org/packages/59/76/89820c2e1950425f39d30695d2296a8c7d400c4c2d279e7618363a7289ac/pandas-3.1.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:84d73e0814a38711e4f2a24b272f748a0b4335169d2f9752407ae26bde22990f", size = 12107257, upload-time = "2026-10-22T11:56:44.498Z" }, + { url = "https://files.pythonhosted.org/packages/c8/2c/af4397e6e855d2c0dcb3392ab7788087be4ef53f6a3f166daad8b83c783e/pandas-3.1.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:d3920d9b4de89fbef76ba6b863d58a330b5bfb720c9f8f58eb9b7da9b711d034", size = 11704430, upload-time = "2026-10-22T11:56:46.791Z" }, + { url = "https://files.pythonhosted.org/packages/fd/9e/7791cd269099f9fca7dc4718955cfe4bb94aedb6634b59eeffefb1ed6a74/pandas-3.1.0-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1d61e0eae5219cd024fcbc21304de759964b5642ddcdbb28d9a8ba442ab6901", size = 11378442, upload-time = "2026-10-22T11:56:49.113Z" }, + { url = "https://files.pythonhosted.org/packages/54/6c/da4013bd33c49a12eb3127771111caeed3222a985651e3465bc06f839ce7/pandas-3.1.0-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbb121205af7bc5e587b94a07b5f67b5ba687a918665b9e8748f21388a88b044", size = 11850193, upload-time = "2026-10-22T11:56:51.558Z" }, + { url = "https://files.pythonhosted.org/packages/14/89/8c820bd6fb6ad8f0edb2877639bc7e164bfb4b8e4cdf7a2202f54959ea1b/pandas-3.1.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:684ef3add5a92abb99c1622f2bd04b54b8843075a266e8240106ca94ce1d1237", size = 12394022, upload-time = "2026-10-22T11:56:53.785Z" }, + { url = "https://files.pythonhosted.org/packages/92/f5/9d753d85c4ea85b6d1c3735496ae5b416d7ef6905f1845c70e9521d124d9/pandas-3.1.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:dde9a55ecd3c4f9100ac50ab15e852e4c54fcf372d800e4495e0b2159a0122ab", size = 12916085, upload-time = "2026-10-22T11:56:56.084Z" }, + { url = "https://files.pythonhosted.org/packages/e6/f9/4a6965677477dbbb19f8881befb01b67eb6009809db73f7f1b7a0132faf1/pandas-3.1.0-cp315-cp315t-win_amd64.whl", hash = "sha256:3fe234bfb9999552dc54c45851752e56e163218d7663897dca950b4a7c32af39", size = 11267097, upload-time = "2026-10-22T11:56:58.479Z" }, + { url = "https://files.pythonhosted.org/packages/99/35/e60fd8228db4eb848802988dbc6e3a0f1a81c57f06ad95ebe4cd534fa655/pandas-3.1.0-cp315-cp315t-win_arm64.whl", hash = "sha256:f1a93c24175129bfc9922ef748ddae1d8137c743ed835e2615be329f606fed4d", size = 10597861, upload-time = "2026-10-22T11:57:00.776Z" }, ] [[package]] @@ -2754,36 +2683,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/7d/905a3a3d51087515719058c94cfbda2ff0fc14417c20d557ae3e82d8b250/plotly-7.1.0-py3-none-any.whl", hash = "sha256:dbb7fa18afce40d0a8e80d1bf162eceb3faa0ce5a77fe741ad09a74cf78f53f3", size = 9692368, upload-time = "2026-09-15T19:21:18.331Z" }, ] -[[package]] -name = "plotnine" -version = "0.15.7" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.15' and sys_platform == 'win32'", - "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.15' and sys_platform == 'win32'", - "python_full_version < '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "matplotlib" }, - { name = "mizani" }, - { name = "numpy" }, - { name = "pandas" }, - { name = "scipy" }, - { name = "statsmodels" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/70/f4/c06a16b4ec540ff6af22e32144704f7e86dee18cd56eef14d66f9615cbf2/plotnine-0.15.7.tar.gz", hash = "sha256:6e578bdd93c7ba12ce495dcdeaf8651f8e4ccdceb0c85b94c8195566d709c2a0", size = 6855478, upload-time = "2026-06-13T10:47:14.277Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/09/61/80caca29adae4ff3430f6bf8acaef1979ebde02e70b61f47a928f4585ca1/plotnine-0.15.7-py3-none-any.whl", hash = "sha256:5226f0d3bae405bfa0797d213177ee3ac634b687fca352cffc313ca0b21aa1a2", size = 1573199, upload-time = "2026-06-13T10:47:12.373Z" }, -] - [[package]] name = "plotnine" version = "0.15.8" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.15' and sys_platform == 'emscripten'", -] dependencies = [ { name = "matplotlib" }, { name = "mizani" }, @@ -2797,26 +2700,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/40/37/7a0275ea8143f0f0a2b27db73656d7ea3459e9cf8f869e292e450b357d1e/plotnine-0.15.8-py3-none-any.whl", hash = "sha256:b8a17fbccdf331f0474ff59ea03186c07d96586eb0ebe70121b03fbf131881a9", size = 1573224, upload-time = "2026-08-14T16:27:47.601Z" }, ] -[[package]] -name = "plotnine" -version = "0.16.0a12" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.15' and sys_platform == 'emscripten'", -] -dependencies = [ - { name = "matplotlib" }, - { name = "mizani" }, - { name = "numpy" }, - { name = "pandas" }, - { name = "scipy" }, - { name = "statsmodels" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2e/e8/d28be1fee4b1c7ef05f76bfb5a137ebc9b214f80b06b2771c73903d421e0/plotnine-0.16.0a12.tar.gz", hash = "sha256:e95565d9ae16ed60bfa16bcea2cb3c862e4b3165b8c257b8a96498f39bb8b17d", size = 7464639, upload-time = "2026-08-15T12:05:51.042Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/09/1a/e8b51da0631bec73c6b3e631ce28cc7918aeac9eff1f99b0dafead97d879/plotnine-0.16.0a12-py3-none-any.whl", hash = "sha256:97c4bac80d594548068b7ffe0ed9101c1718b5ae2d3bb8e045f885f47d9ec998", size = 1407444, upload-time = "2026-08-15T12:05:48.876Z" }, -] - [[package]] name = "pluggy" version = "1.6.0" @@ -2927,9 +2810,6 @@ filetree = [ { name = "aiofile" }, { name = "anyio" }, ] -keyring = [ - { name = "keyring" }, -] memory = [ { name = "cachetools" }, ] @@ -3094,7 +2974,7 @@ wheels = [ [[package]] name = "pydantic-settings" -version = "2.16.0" +version = "2.16.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, @@ -3102,9 +2982,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/3b/a5d2294799b53b448319978cfb5bd139d5a9d45e862af91661614f14c922/pydantic_settings-2.16.0.tar.gz", hash = "sha256:5b6c578049ede4db0e2ef3b4eaa4ad4069cfa9211f83fb38df899dfade50a614", size = 301031, upload-time = "2026-10-14T12:44:09.998Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/ca/7817b4bf0c4b15c6a6ab8cdf4cb96c268335a8ca2f525de93b6ebe4c7d28/pydantic_settings-2.16.1.tar.gz", hash = "sha256:aff602a1886338c2df3d1e958e2193a8dc33cec75b91f202e83fd98484b4b0da", size = 303436, upload-time = "2026-10-20T11:37:19.024Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/f4/b987bf8c51e5b19a95fa66d1ee596074141e085d9c2ddf97920803c7029b/pydantic_settings-2.16.0-py3-none-any.whl", hash = "sha256:7e73acf7f61936a15e5a3b6eedaea29f133357faf7272f2607ba479b049dd7f2", size = 80364, upload-time = "2026-10-14T12:44:08.233Z" }, + { url = "https://files.pythonhosted.org/packages/3c/74/fdf926afe145d8629c4b2ff57f4ea1d64ed7abc4d84d97463b2517a2547e/pydantic_settings-2.16.1-py3-none-any.whl", hash = "sha256:30f5313340da4e8f1b440d1910b92a5bc3ccbb6b4ba70ec2b8395bc921f418f5", size = 80771, upload-time = "2026-10-20T11:37:17.298Z" }, ] [[package]] @@ -3234,14 +3114,14 @@ wheels = [ [[package]] name = "pytest-asyncio" -version = "1.4.0" +version = "1.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/9a/887535e477847f5159377ab35303923644840b13c0ca03c0a467f84d4680/pytest_asyncio-1.4.1.tar.gz", hash = "sha256:e8266eb094e5ee9ffdb2068c9836f33e209ea77a970eb846933d416b5602669b", size = 145237, upload-time = "2026-10-25T21:05:37.135Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" }, + { url = "https://files.pythonhosted.org/packages/46/d7/cd9e2708afdbf059658adaf08bdb0792ca52489871051d6ce7d498b51277/pytest_asyncio-1.4.1-py3-none-any.whl", hash = "sha256:507afa78ec77c6661164ef1a2368bc7820383fd1a14b2a7f813de8b5a92949cc", size = 17061, upload-time = "2026-10-25T21:05:35.473Z" }, ] [[package]] @@ -3385,15 +3265,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, ] -[[package]] -name = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - [[package]] name = "pyyaml" version = "6.0.3" @@ -3513,27 +3384,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.17.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e7/e6d5749983b020e6cb1055abec0a6f361a8e94f0184cdcf56d4760995f02/ruff-0.17.1.tar.gz", hash = "sha256:5bb796c5112e9fb9527f2ef2faa0a130d6312d19de92bcfaddd70dbb2d41020f", size = 5323349, upload-time = "2026-10-15T15:23:18.64Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/78/90c46ca6250709f7c37e397b60d06061adee8d0f7d13959ed6a8e49b14fd/ruff-0.17.1-py3-none-linux_armv6l.whl", hash = "sha256:1796c41eafec66f4635125dd1e074a7aa27b8d20c187d484360acf11d247fc4a", size = 10147528, upload-time = "2026-10-15T15:22:33.667Z" }, - { url = "https://files.pythonhosted.org/packages/aa/d2/a2e28bc35b083ae51fa07dfba5280a667c8b01ecaee7802fc8849dcd5c46/ruff-0.17.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8eadb09a788010be29c895418bdfa513e4e1c65e64b94e5b19e4cd381f6cc7fb", size = 10225974, upload-time = "2026-10-15T15:22:36.328Z" }, - { url = "https://files.pythonhosted.org/packages/8e/71/7a9029a81c8206ee01a98f6cfeeb56e6c095af86857581a37045c46d7e94/ruff-0.17.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:29b454f18ca3eeaf37fbf140841dcf27d36109e29dee9aae8acecb68c26c6f74", size = 9270361, upload-time = "2026-10-15T15:22:38.651Z" }, - { url = "https://files.pythonhosted.org/packages/c0/8c/e075e9c917cb0575297880c96768304e881240171c870cbd8d9b7b54bec2/ruff-0.17.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:872e03dd231744d6c5ec5c8af6be52b71323fc6ee096e4c8b34d9f844ee3b978", size = 9938300, upload-time = "2026-10-15T15:22:41.284Z" }, - { url = "https://files.pythonhosted.org/packages/87/27/0b4bb926a49de2383f161272b4d863571a4c7ecdbf595a6bc58813a13367/ruff-0.17.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4c0d6cb5f8fbac77eec9af49f4fc0eaf856b05545e372b0694d24b4e76b1c640", size = 9948464, upload-time = "2026-10-15T15:22:43.681Z" }, - { url = "https://files.pythonhosted.org/packages/14/27/518d57e0e02aecb8ea4acac6b57b51a53173d25fa734952bfdd7d531e88e/ruff-0.17.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78beced4a3c94dfedbee4b999b95b6a27b35820fdc0afbacc6d37a8da26c1cd1", size = 10638639, upload-time = "2026-10-15T15:22:46.081Z" }, - { url = "https://files.pythonhosted.org/packages/01/82/d89a4e498ade59e6cee52d67f8da569019372212b87468c1fdeae884601c/ruff-0.17.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7feefc63921202894f0e158b0e32f45a5950d70a7fb693ccb9264816c25b1c05", size = 11337238, upload-time = "2026-10-15T15:22:48.894Z" }, - { url = "https://files.pythonhosted.org/packages/a2/7c/3b0fdef9352656317c977086fe1d802902406e4f2a0d8578da90c0e6d1c5/ruff-0.17.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:684154861a819bf1f433669d2a0ce9c3e54677c65b8ebb866cc2a7ec86345785", size = 10896327, upload-time = "2026-10-15T15:22:51.45Z" }, - { url = "https://files.pythonhosted.org/packages/62/86/5566290219708ce2928f3a29c4f843a1185b341ec6111c4631a33c5ceced/ruff-0.17.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6ef98e51de8ef38082f90653ceeb84944bd7673783eea13e1647c5b9b064189", size = 10350811, upload-time = "2026-10-15T15:22:54.401Z" }, - { url = "https://files.pythonhosted.org/packages/ad/d2/ca8462dabb3c413990d49703ba306c30c862195a75448d080fa8fcce9775/ruff-0.17.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:8f9aa5b5bd5fa1937803ea609c8f153b8de39bf3e0e149897ae34b0c5b52f467", size = 10569994, upload-time = "2026-10-15T15:22:57.142Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ad/dd1864c1f1db17c8cfb297ce70b352388e6977620f66f7182c52855407df/ruff-0.17.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:80e53b0e5faa3c5667f035daaa032893ae42b7cfe708c886d39ed131e54688ab", size = 10059672, upload-time = "2026-10-15T15:23:00.084Z" }, - { url = "https://files.pythonhosted.org/packages/65/45/3f56cf1d2168c2165cdd5ac6f9668aee66a4cfdd4caf2b2cc5ccb2b1d568/ruff-0.17.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:056c5d5fa0ac790051ae165d7035c7bf956d02de2db557f2e9b31d877a505fca", size = 9947803, upload-time = "2026-10-15T15:23:03.132Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4b/5457cb2be4c1d1ea5e919558131a0f197a4a454a0530d9e30820e8206e4c/ruff-0.17.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bd8f5a55b10f371642eacd0e8665a42a8b97da15c9a7187c79e697b77b5e0ce4", size = 10351807, upload-time = "2026-10-15T15:23:05.628Z" }, - { url = "https://files.pythonhosted.org/packages/49/fb/db15b4f8fa24a1e36b99e1401f42ef6df16ee299a48670d5d262b771e7d7/ruff-0.17.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ce05e6f3973611d9550a600e04bb531061e368baba31610eb57c1509a8f21a0a", size = 10765716, upload-time = "2026-10-15T15:23:08.092Z" }, - { url = "https://files.pythonhosted.org/packages/0a/e8/1e186578076aff7a80735f625005fde678412ab609cd323ed182eb7d6b87/ruff-0.17.1-py3-none-win32.whl", hash = "sha256:1de0030a8aa78c65c5fecb84b2e5c390ac684c0452875a02afc7c083d49a496a", size = 9895093, upload-time = "2026-10-15T15:23:10.582Z" }, - { url = "https://files.pythonhosted.org/packages/6a/2e/da3d9589bae8e8b58f3a48bd5bc0b980232fb14a83bf02c3be7660f87c1b/ruff-0.17.1-py3-none-win_amd64.whl", hash = "sha256:5df76f16580fab58d0f6c98a3ac254a4abceb7b2489b8b678335869cff77b3d7", size = 10097955, upload-time = "2026-10-15T15:23:13.309Z" }, - { url = "https://files.pythonhosted.org/packages/df/c9/0f2a4dea06a5e12c1191ee3d96b1e1233797d0d5e18f97a2b909a95a9836/ruff-0.17.1-py3-none-win_arm64.whl", hash = "sha256:42400b9a6ff8515ff27796f07f92e0195be011c86e81e381606f58ea5502fef4", size = 9853542, upload-time = "2026-10-15T15:23:16.05Z" }, +version = "0.17.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/58/690cf56da80c8f2c8bf12042b6017beac2c34fe961f65420bcb3fb7fac38/ruff-0.17.2.tar.gz", hash = "sha256:84d632402d14c23b488f941db23712d6736a013225162062d34558c3f3c780eb", size = 5333400, upload-time = "2026-10-22T18:38:00.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/b8/8b6d69c57e8fe1cc6af1aa7493b72a1a5b5a8afe34694e302025441729d4/ruff-0.17.2-py3-none-linux_armv6l.whl", hash = "sha256:96a65e755d8dba094707fb23ab602111d9a31ec2835b3a6992a1914a8fcb211e", size = 10183857, upload-time = "2026-10-22T18:37:14.956Z" }, + { url = "https://files.pythonhosted.org/packages/e8/61/12cc2ff7b2fa90a268ce1e2162c23aaf76e601d22623f6a31b0eeaa764b0/ruff-0.17.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:39c5b55c960515cdb32be16e69efb5734c589c06a0eb38a22694524a336b889d", size = 10210904, upload-time = "2026-10-22T18:37:17.985Z" }, + { url = "https://files.pythonhosted.org/packages/e8/7c/b8850a8e4ad29f64a827dd6c6902db4a9aa538fb2b9eef4e26084001a3bb/ruff-0.17.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:92a80d2f44584dabb0189b3a6c393e37e525e04942838482010575cb81945be7", size = 9408439, upload-time = "2026-10-22T18:37:20.964Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b6/6a64fae86ca5e5e29d5adc311e5481de726cedf9aee607435a16c31c442a/ruff-0.17.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7d0906677d89bbf3cf73cde64b5bbe2ab35df3ae43e49ee2627301e91265bac", size = 10047078, upload-time = "2026-10-22T18:37:23.717Z" }, + { url = "https://files.pythonhosted.org/packages/10/19/23d376547715175aa1bd46df48df6aacc7c6439d9daf19cac15975180aa5/ruff-0.17.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cfd783590edf2b77317882631900f6a13ea2e8eacefe6ebf2370d2715299160", size = 9990478, upload-time = "2026-10-22T18:37:26.584Z" }, + { url = "https://files.pythonhosted.org/packages/93/6c/34cda32cbe13adac5c6b291a307a5570d8ebd37c0c86339f0fe628e55632/ruff-0.17.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122258f6ecde0e0dd1bb60b24263c3edde4338caf723fca711cc729f9bfd414a", size = 10673624, upload-time = "2026-10-22T18:37:29.767Z" }, + { url = "https://files.pythonhosted.org/packages/bf/10/5d762a4dbc851d8bb47863cf3a28a61bf31c804d393b70d891fac899f5d1/ruff-0.17.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f8fba429d6ac4128ab29cfb8fa6240d2fa671d04adf7f84d3b22fc7b6b6bea4", size = 11351797, upload-time = "2026-10-22T18:37:32.535Z" }, + { url = "https://files.pythonhosted.org/packages/3f/04/a0e92a81faf5c81c1c98d4374c8401734ccf2b8cca7d0170cb0fec08c6e2/ruff-0.17.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:583bd67bb9e7061dd09bc46a0a884bae2bbd214dbcd48e509813612619683869", size = 10949414, upload-time = "2026-10-22T18:37:35.501Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d8/59894cf630d2d86882457a2a6a0a1533410c5c805bfbac6f6eb4b5aef69d/ruff-0.17.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c60f93fec7142dba5b8f487f94bf2f6231dae24dc11e4c48648347c8b0720004", size = 10528000, upload-time = "2026-10-22T18:37:37.88Z" }, + { url = "https://files.pythonhosted.org/packages/ab/95/d6e30c51e851d37e10762ad91fa1e76ddaad998589b5667962a3cfbc9edd/ruff-0.17.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:c555a877f8f41447e318101cfefea9bf12dfa9f82eedf90c376e57e069f917d0", size = 10571779, upload-time = "2026-10-22T18:37:40.444Z" }, + { url = "https://files.pythonhosted.org/packages/f4/5a/a7e1d74fb464250137d3d4eca645d323536533b672004d04ee0e46e0caf3/ruff-0.17.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ba405cbd5ec7bda1f555d5e52be1fd68d02608d4d95d24591bdac92cfacb579f", size = 10051707, upload-time = "2026-10-22T18:37:42.812Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f2/02fca29ca7999380f0350a7212fe755c11ab9c22d61327356cce8decdbc6/ruff-0.17.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8c21b4ab24485358f17291a4f8dc5399b834e963b8efedd920fe4fdb02081bb4", size = 9998227, upload-time = "2026-10-22T18:37:45.224Z" }, + { url = "https://files.pythonhosted.org/packages/d1/00/55aa604a093dfdc1526e570fae0bb7d37a2221c7b4728d5f080b2add16ba/ruff-0.17.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5875a16b0de70b43cb81eabea5ae5e8f5d1b2fd1b5e6ed07f80cfe5019d0bd5a", size = 10377692, upload-time = "2026-10-22T18:37:47.704Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5a/a7a640f47bb906ec8fa233ea84319d503e6dee02b59e3901bd312ebd1f9b/ruff-0.17.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2d086be452eed686aa51271ecf2cbe5037956517cc6ee39a17b4489ac12f8bbe", size = 10790587, upload-time = "2026-10-22T18:37:50.191Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d4/c3b74e1b4808438ea7f14bb914478025e2d51261f60774a54a0fa222ec46/ruff-0.17.2-py3-none-win32.whl", hash = "sha256:6bce7d1f5165701ae18f5df85c65afe78cddfa6c5fb4b9bdb0014d60388f3edd", size = 9870537, upload-time = "2026-10-22T18:37:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9a/c7da96c2c96d20704cf9027f7a5121da272a8c05f1d1531ba949e3b3df04/ruff-0.17.2-py3-none-win_amd64.whl", hash = "sha256:1b59d447f197bef9ef48a22ba1a6016137753c6099e9bac9c36eb566e422424c", size = 10282826, upload-time = "2026-10-22T18:37:55.2Z" }, + { url = "https://files.pythonhosted.org/packages/81/60/a3604c79cb02777b6cb84da894104021986f06c4bc41375102cc95049a32/ruff-0.17.2-py3-none-win_arm64.whl", hash = "sha256:195ad918027f817c119310ca89a42d347887dfc28934aaa869325e9c5dc9d962", size = 9827036, upload-time = "2026-10-22T18:37:58.031Z" }, ] [[package]] @@ -3652,19 +3523,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914, upload-time = "2024-01-25T13:21:49.598Z" }, ] -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - [[package]] name = "selenium" version = "4.51.0" From e5da75ca1da8805f7fd478ff0186bcdee17d1281 Mon Sep 17 00:00:00 2001 From: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com> Date: Mon, 2 Nov 2026 01:18:20 +0100 Subject: [PATCH 2/4] fix(agent): keep Claude's structured outputs on ADK 2.13 ADK 2.13 delivers a response schema to Claude as a `structured_output` tool, forced with thinking off and only offered with thinking on (`anthropic_llm._OutputSchemaTool`, upstream 97af1ff). `VertexClaude` still added `output_config.format`, so the adapter and the reviewer sent a tool and a format on one call and could answer outside the constrained grammar. It now drops ADK's tool before ADK builds the arguments, so the cache breakpoints do not land on it either. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01H7HSkGyea1k2v293XiAMo8 --- agentic/docs/project-guide.md | 2 +- agents/README.md | 2 +- agents/anyplot/agent.py | 2 +- agents/anyplot/models.py | 48 ++++++++++++++-------- agents/anyplot/sub_agents/adapter.py | 2 +- agents/main.py | 2 +- docs/concepts/agent-network.md | 4 +- tests/unit/agents/runtime/test_registry.py | 2 +- tests/unit/agents/test_adk_pin.py | 2 +- 9 files changed, 39 insertions(+), 27 deletions(-) diff --git a/agentic/docs/project-guide.md b/agentic/docs/project-guide.md index 3f81fe8bf25..8220ef9a845 100644 --- a/agentic/docs/project-guide.md +++ b/agentic/docs/project-guide.md @@ -202,7 +202,7 @@ Example: `plots/scatter-basic/` contains everything for the basic scatter plot. - **`core/database/repositories.py`**: Data access layer - **`api/`**: FastAPI backend (routers, schemas, dependencies, cache, analytics, MCP server) - **`app/`**: React frontend (Vite 8 + TypeScript 6 + MUI 9) -- **`agents/`**: Agent network on Google ADK 2.11 and Vertex AI ("Use with my data"; Claude Haiku 5.5 by default, Gemini 3.8 Flash behind `AGENT_PROVIDER`); the runtime core (agents, plot pipeline, plugins, render layer, private `/v1` service) runs locally, see `agents/README.md`; design in `docs/concepts/agent-network.md` +- **`agents/`**: Agent network on Google ADK 2.13 and Vertex AI ("Use with my data"; Claude Haiku 5.5 by default, Gemini 3.8 Flash behind `AGENT_PROVIDER`); the runtime core (agents, plot pipeline, plugins, render layer, private `/v1` service) runs locally, see `agents/README.md`; design in `docs/concepts/agent-network.md` - **`agentic/`**: AI workflow layer (composable phases, prompt templates, runtime state) - **`agentic/workflows/`**: Click CLI scripts (plan, build, test, review + orchestrators) - **`agentic/commands/`**: Markdown prompt templates diff --git a/agents/README.md b/agents/README.md index a7a132b6b2c..22c5a8b1ed1 100644 --- a/agents/README.md +++ b/agents/README.md @@ -1,6 +1,6 @@ # Agent network -This directory holds the anyplot agent network: the service that lets an admin paste their own data on a plot page and get that plot adapted, rendered and reviewed ("Use with my data"). It is built with [Google ADK](https://adk.dev) 2.11. The design, including every contract and guardrail, is in [Agent network design](../docs/concepts/agent-network.md). +This directory holds the anyplot agent network: the service that lets an admin paste their own data on a plot page and get that plot adapted, rendered and reviewed ("Use with my data"). It is built with [Google ADK](https://adk.dev) 2.13. The design, including every contract and guardrail, is in [Agent network design](../docs/concepts/agent-network.md). ## What is built diff --git a/agents/anyplot/agent.py b/agents/anyplot/agent.py index ad358c14ac8..860ccb0e6e4 100644 --- a/agents/anyplot/agent.py +++ b/agents/anyplot/agent.py @@ -183,7 +183,7 @@ def with_session_part(system: types.ContentUnion | None, block: str) -> types.Co async def add_session_context(callback_context: CallbackContext, llm_request: LlmRequest) -> LlmResponse | None: """Append the session block to the root's system instruction, as a part of its own. - ADK 2.11 sends an agent's `instruction` next to a `static_instruction` as user content: + ADK 2.13 sends an agent's `instruction` next to a `static_instruction` as user content: a `<<>>` block with a preamble, inserted into the user's own turn (`flows/llm_flows/prompt/_instructions.py`). Claude Haiku 5.5 read that as a user who forges system text: in a prep-eval sample of 2026-10-15 its thinking called the block diff --git a/agents/anyplot/models.py b/agents/anyplot/models.py index 87d74fcab27..9e63e57b8eb 100644 --- a/agents/anyplot/models.py +++ b/agents/anyplot/models.py @@ -11,7 +11,7 @@ `google.adk.models.anthropic_llm.Claude` with an `AsyncAnthropicVertex` client built from the settings (ADK's own builds one from `GOOGLE_CLOUD_PROJECT` and `GOOGLE_CLOUD_LOCATION`, which would let the environment pick the project). -Reasoning depth is the Claude `effort` of `AnthropicGenerateContentConfig`; ADK 2.11 +Reasoning depth is the Claude `effort` of `AnthropicGenerateContentConfig`; ADK 2.13 ignores `thinking_config.thinking_level` for Claude and raises when both are set. Claude thinks adaptively by default (since 2026-10-11): the root with @@ -27,7 +27,7 @@ parts, which the answer guard, ADK's output parsing and the stream translator all skip. Two details keep the replayed thinking blocks harmless. Left unset, Haiku 5.5 would -think with the display omitted, and ADK 2.11 sends such a signature-only block back as +think with the display omitted, and ADK 2.13 sends such a signature-only block back as `redacted_thinking`, which breaks the root's call after a tool result; the summarized display avoids that. And the root replays its thinking block after a tool result while the session block between the two calls changed (until 2026-10-15 ADK also moved it @@ -43,13 +43,16 @@ block) goes to Claude as one system block per part, with ADK's prompt-cache breakpoint on the first only (`system_blocks`), so a session block that changes leaves the policy cached. -ADK 2.11 sends no response schema to Claude: `basic.py` sets -`config.response_schema` for a tool-less agent with an `output_schema`, and -`anthropic_llm.py` never reads it, so the adapter and reviewer would get free text. -`VertexClaude` closes that gap with Claude's structured outputs: when a request -carries a response schema and no tools, it sets `output_config.format` to that -schema (`structured_output_format`), so constrained decoding makes the answer text -valid JSON of exactly that shape, and ADK's `validate_schema` parses it as it is. +ADK 2.13 delivers a response schema to Claude as a `structured_output` tool +(`anthropic_llm._OutputSchemaTool`; ADK 2.11 did not send the schema at all): `basic.py` +sets `config.response_schema` for a tool-less agent with an `output_schema`, and the +tool is forced with thinking off and only offered with thinking on, so a thinking +adapter or reviewer could still answer in free text. `VertexClaude` uses Claude's +structured outputs instead: when a request carries a response schema and no tools, +it drops ADK's tool and sets `output_config.format` to that schema +(`structured_output_format`), so constrained decoding makes the answer text valid +JSON of exactly that shape, and ADK's `validate_schema` parses it as it is. ADK's +unwrapping of the tool call finds no call and leaves the answer alone. The judge answers the same way. The grammar enforces types, enums, required fields and `additionalProperties: false`; what it cannot enforce (string lengths, item counts, a verdict's `ok` agreeing with its defects) the SDK's `transform_schema` @@ -90,7 +93,7 @@ from pathlib import Path from typing import Any, Literal, Protocol -from anthropic import transform_schema +from anthropic import NOT_GIVEN, transform_schema from google.adk.models.anthropic_llm import AnthropicGenerateContentConfig, Claude from google.adk.models.base_llm import BaseLlm from google.adk.models.google_llm import Gemini @@ -271,8 +274,13 @@ def structured_output_format(schema: Any) -> dict[str, Any]: signature no longer matches the conversation prefix (`thinking.block_binding`).""" -def _has_tools(llm_request: LlmRequest) -> bool: - return bool(llm_request.config and llm_request.config.tools) +def _structured_schema(llm_request: LlmRequest) -> Any: + """The schema a request answers under as a structured output, or None. + + A request with tools of its own (the root) has none: its answer is chat. + """ + config = llm_request.config + return config.response_schema if config and not config.tools else None def system_parts(llm_request: LlmRequest) -> list[str]: @@ -324,17 +332,21 @@ def _anthropic_client(self) -> Any: def _build_anthropic_kwargs( self, llm_request: LlmRequest, messages: Any, tools: Any, tool_choice: Any, thinking: Any ) -> dict[str, Any]: - """ADK's request arguments, plus the structured-output format when a response schema is set. + """ADK's request arguments, with a response schema sent as the structured-output format. - The format joins ADK's `output_config` (which holds the effort) rather than - replacing it. A request with tools (the root) gets no format: its answer is chat. + The format replaces the `structured_output` tool ADK delivers a schema as (a tool + and a format on one call would leave two ways to answer), so it goes before ADK + builds the arguments, whose cache breakpoints also look at the tools. The format + joins ADK's `output_config` (which holds the effort) rather than replacing it. """ + schema = _structured_schema(llm_request) + if schema: + tools, tool_choice = NOT_GIVEN, NOT_GIVEN kwargs: dict[str, Any] = super()._build_anthropic_kwargs(llm_request, messages, tools, tool_choice, thinking) parts = system_parts(llm_request) if len(parts) > 1: kwargs["system"] = system_blocks(parts, kwargs.get("system")) - schema = llm_request.config.response_schema if llm_request.config else None - if schema is not None and not _has_tools(llm_request): + if schema: kwargs["output_config"] = { **(kwargs.get("output_config") or {}), "format": structured_output_format(schema), @@ -356,8 +368,8 @@ def _build_anthropic_kwargs( async def generate_content_async( self, llm_request: LlmRequest, stream: bool = False ) -> AsyncGenerator[LlmResponse, None]: - structured = bool(llm_request.config and llm_request.config.response_schema) and not _has_tools(llm_request) # A structured answer is read whole, so it never streams. + structured = bool(_structured_schema(llm_request)) async for response in super().generate_content_async(llm_request, stream=stream and not structured): yield response diff --git a/agents/anyplot/sub_agents/adapter.py b/agents/anyplot/sub_agents/adapter.py index 0769450e7e5..54edc2b788e 100644 --- a/agents/anyplot/sub_agents/adapter.py +++ b/agents/anyplot/sub_agents/adapter.py @@ -4,7 +4,7 @@ adapter prompt plus the verbatim catalogue sources for its library), so its prompt prefix stays cacheable, and it never sees the conversation: `include_contents='none'` and the `before_model_callback` keeps only the last content, the fenced -`AdaptRequest` the pipeline passes as node input. (ADK 2.11 also prepends the +`AdaptRequest` the pipeline passes as node input. (ADK 2.13 also prepends the invocation's user message to a single-turn agent that runs under its own isolation scope; the callback removes it, so the adapter reads nothing but the request.) diff --git a/agents/main.py b/agents/main.py index b6ebb77bd47..ba0b12d0aa6 100644 --- a/agents/main.py +++ b/agents/main.py @@ -1235,7 +1235,7 @@ async def _ingest_paste( delta = ingested.state_delta() # Read again: the chip's choice may have landed while the judges ran, and a choice is never overwritten. # From this read to the append below nothing suspends: `InMemorySessionService.get_session` and - # `append_event` (ADK 2.11) contain no `await` that yields, and the code between them is synchronous, + # `append_event` (ADK 2.13) contain no `await` that yields, and the code between them is synchronous, # so on the one event loop no `PUT /language` can land in between; the read and the conditional # write are atomic. A persistent session service with I/O in either call would need a # compare-and-set instead. Keep any new `await` out of this block. diff --git a/docs/concepts/agent-network.md b/docs/concepts/agent-network.md index 5455a90aef9..059bb22a43a 100644 --- a/docs/concepts/agent-network.md +++ b/docs/concepts/agent-network.md @@ -28,7 +28,7 @@ This document describes the agent network that lets a visitor paste their own da ## What the research settled -- **ADK 2.11.0, 2.x idioms only.** `from google.adk import Agent, Workflow, Event, Context`; plugins on `App(plugins=[...])`; sub-agent modes `task` and `single_turn`; a `Workflow` with a Pydantic `input_schema` passed in `tools=[...]` becomes a NodeTool whose progress events reach the stream. `SequentialAgent`, `LoopAgent` and `ParallelAgent` are deprecated, `AgentTool` is discouraged (its events never reach the stream), `global_instruction` is deprecated, and `GOOGLE_GENAI_USE_ENTERPRISE=TRUE` replaces the Vertex flag. `abort_signal` is a `Runner.run_async` keyword argument, not a `RunConfig` field. `instruction` strings get `{var}` state templating (a missing key raises); `static_instruction` is sent unchanged. +- **ADK 2.13.0, 2.x idioms only.** `from google.adk import Agent, Workflow, Event, Context`; plugins on `App(plugins=[...])`; sub-agent modes `task` and `single_turn`; a `Workflow` with a Pydantic `input_schema` passed in `tools=[...]` becomes a NodeTool whose progress events reach the stream. `SequentialAgent`, `LoopAgent` and `ParallelAgent` are deprecated, `AgentTool` is discouraged (its events never reach the stream), `global_instruction` is deprecated, and `GOOGLE_GENAI_USE_ENTERPRISE=TRUE` replaces the Vertex flag. `abort_signal` is a `Runner.run_async` keyword argument, not a `RunConfig` field. `instruction` strings get `{var}` state templating (a missing key raises); `static_instruction` is sent unchanged. - **ADK's own server is never exposed.** `adk web`, `adk api_server` and `get_fast_api_app` are unauthenticated and let the client choose the user id and write state, events and artifacts. The network embeds `Runner.run_async` in its own FastAPI routes. - **No agent gets a `code_executor`.** ADK auto-runs the first fenced code block the model emits. Rendering is a deterministic tool that calls Cloud Run sandboxes (`gcloud beta run deploy --sandbox-launcher`, gen2, Preview, no extra cost): `sandbox do` has no egress, no environment, secrets or metadata server, and a read-only root filesystem. Region availability and Chromium, R and Julia inside the sandbox are undocumented, so a spike gates phase 1. - **Adapting catalogue code is not a data swap.** 172 of 325 matplotlib files set axis limits with data-scale literals, 87 place annotations at numeric data coordinates, literal statistics are common, and 137 matplotlib and 138 seaborn files already fail the 3200x1800 canvas gate (`bbox_inches='tight'`). A pure swap passes the structural gates in 40 of 63 runs and 0 of 7 for date axes. Structured search-and-replace hunks of about 100 to 180 output tokens passed all gates in a proxy test. Inline data literals are infeasible beyond a few hundred rows, so data travels as a sidecar `data.csv`. @@ -72,7 +72,7 @@ The root's "never" list, enforced by the stream translator and the evals: never Rules for every agent: -- Models come only from `make_model()` in `agents/anyplot/models.py`, which builds the model of the configured provider. On `anthropic-vertex` (the default) it returns `VertexClaude`, ADK's `Claude` with an `AsyncAnthropicVertex` client built from the settings, adaptive thinking on every kind (`AGENT_ROOT_THINKING_BUDGET`, 1,024, `AGENT_ADAPTER_THINKING_BUDGET`, 8,192, and `AGENT_REVIEWER_THINKING_BUDGET`, 4,096, each at most 8,192 and added to that agent's output cap; 0 turns it off, and the eval harness overrides the root's and the adapter's with `--thinking-budget`; a replayed thinking block whose signature no longer matches the conversation, which happens when a tool changes the session block between the root's calls, is dropped by the API through the `thinking-binding-controls-2026-08-01` beta instead of failing the call with a 400), and Claude's structured outputs for the adapter's and the reviewer's response schemas, because ADK 2.11 sends no response schema to Claude: the request's `output_config.format` carries the schema made strict by the SDK's `transform_schema`, so constrained decoding enforces types, enums and required fields, and the Pydantic schema with its repairs checks the lengths and counts the grammar cannot. On `gemini` it returns `Gemini(model=..., client_kwargs={"enterprise": True, "project": ..., "location": settings.location}, retry_options=HttpRetryOptions(attempts=3, initial_delay=1))`. The bare `Gemini()` default is the retiring 2.5 Flash and is never used. +- Models come only from `make_model()` in `agents/anyplot/models.py`, which builds the model of the configured provider. On `anthropic-vertex` (the default) it returns `VertexClaude`, ADK's `Claude` with an `AsyncAnthropicVertex` client built from the settings, adaptive thinking on every kind (`AGENT_ROOT_THINKING_BUDGET`, 1,024, `AGENT_ADAPTER_THINKING_BUDGET`, 8,192, and `AGENT_REVIEWER_THINKING_BUDGET`, 4,096, each at most 8,192 and added to that agent's output cap; 0 turns it off, and the eval harness overrides the root's and the adapter's with `--thinking-budget`; a replayed thinking block whose signature no longer matches the conversation, which happens when a tool changes the session block between the root's calls, is dropped by the API through the `thinking-binding-controls-2026-08-01` beta instead of failing the call with a 400), and Claude's structured outputs for the adapter's and the reviewer's response schemas, not the `structured_output` tool ADK 2.13 delivers a schema as, which it only offers while thinking is on: the request drops that tool, and its `output_config.format` carries the schema made strict by the SDK's `transform_schema`, so constrained decoding enforces types, enums and required fields, and the Pydantic schema with its repairs checks the lengths and counts the grammar cannot. On `gemini` it returns `Gemini(model=..., client_kwargs={"enterprise": True, "project": ..., "location": settings.location}, retry_options=HttpRetryOptions(attempts=3, initial_delay=1))`. The bare `Gemini()` default is the retiring 2.5 Flash and is never used. - No `FallbackModel` in phase 1. A fallback would need a second model id or the `global` endpoint, which breaks the pins; persistent 429s map to `error{code:"capacity"}`. A named fallback on `eu` is an explicit owner decision; switching `AGENT_PROVIDER` is a configuration change, not a fallback. - Low-cardinality `labels` in every agent's generation config. On Gemini: explicit `safety_settings` at BLOCK_MEDIUM_AND_ABOVE, and no temperature, top_p or thinking budget on Gemini 3. - `agents/anyplot/agent.py` keeps an explicit registry `ALL_AGENTS = [root_agent, *ADAPTERS.values(), reviewer]`, because the adapters and the reviewer are started with `ctx.run_node` and a tree walk from the root would miss them. A unit test iterates the registry and `App.plugins` and asserts: no `code_executor`, `google_search`, `url_context`, `AgentTool`, `LoggingPlugin` or chat-mode sub-agents; no agent has a non-empty string `instruction`; every model string and client location comes from settings. diff --git a/tests/unit/agents/runtime/test_registry.py b/tests/unit/agents/runtime/test_registry.py index 3633689b818..d96dd2cc096 100644 --- a/tests/unit/agents/runtime/test_registry.py +++ b/tests/unit/agents/runtime/test_registry.py @@ -54,7 +54,7 @@ def test_no_executor_search_url_context_or_agent_tool(self) -> None: def test_no_string_instruction_and_static_policy(self) -> None: for agent in agent_module.ALL_AGENTS: - # No dynamic `instruction` at all: next to a static one, ADK 2.11 sends it inside the user's + # No dynamic `instruction` at all: next to a static one, ADK 2.13 sends it inside the user's # turn between `<<>>` markers, which the root took for a forgery. assert not agent.instruction, agent.name assert isinstance(agent.static_instruction, str) and agent.static_instruction, agent.name diff --git a/tests/unit/agents/test_adk_pin.py b/tests/unit/agents/test_adk_pin.py index aba8525ddf9..b82cc11cc1e 100644 --- a/tests/unit/agents/test_adk_pin.py +++ b/tests/unit/agents/test_adk_pin.py @@ -14,7 +14,7 @@ import agents -ADK_VERSION = "2.11.0" +ADK_VERSION = "2.13.0" REPO_ROOT = Path(__file__).resolve().parents[3] From 44086229dc5ad2b5093e083bb52f80e7ee695c39 Mon Sep 17 00:00:00 2001 From: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com> Date: Mon, 2 Nov 2026 01:18:20 +0100 Subject: [PATCH 3/4] fix(data): adapt the data layer to pandas 3.1 pandas 3.1 dropped its compensated group sum for sorted keys, which no test showed: run against pandas 3.0, 1,840 cells of the eval fixtures and test sets changed, and a monthly total of `4024.2` came back as `4024.2000000000003`. A float group `sum`, group `mean` and `share` now add the cells in decimals, the sum a reader gets by hand; `math.fsum` is not enough, its exact sum of the binary values still gives `377.78000000000003` for cells that add up to `377.78`. `float_precision` is deprecated and goes in a later version, which would break every exported `plot.py`. All modes now share one exact converter, so the loader, `_read` and the stress harness drop it and pandas is at least 3.1.0; on 3.0.6 the fidelity test fails without the argument. `Series.dt.weekday` becomes `day_of_week`. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01H7HSkGyea1k2v293XiAMo8 --- agents/anyplot/code/loader.py | 23 +++---------- agents/anyplot/data/prepare.py | 47 +++++++++++++++++++++----- agents/evals/stress.py | 3 +- changelog.d/adk-2-13-pandas-3-1.md | 18 ++++++++++ docs/concepts/agent-network.md | 2 +- pyproject.toml | 20 +++++------ tests/unit/agents/code/test_loader.py | 3 +- tests/unit/agents/data/test_parse.py | 1 - tests/unit/agents/data/test_prepare.py | 21 +++++++++++- tests/unit/agents/evals/test_stress.py | 2 +- uv.lock | 20 +++++------ 11 files changed, 104 insertions(+), 56 deletions(-) create mode 100644 changelog.d/adk-2-13-pandas-3-1.md diff --git a/agents/anyplot/code/loader.py b/agents/anyplot/code/loader.py index f790963dc23..c0131087e81 100644 --- a/agents/anyplot/code/loader.py +++ b/agents/anyplot/code/loader.py @@ -7,10 +7,10 @@ ISO dates), so no `sep`, `decimal` or date format is needed. The column dtypes from the dataset profile map to pandas dtypes that keep missing values (`integer` to `Int64`, `number` to `float64`, `boolean` to `boolean`, `text` to `string`); datetime -columns go to `parse_dates`. A dataset with a number column is read with -`float_precision="round_trip"`, so every float is the one `data.csv` writes in its -shortest round-trip form (`ROUND_TRIP`). `import pandas as pd` is added after the last -module-level import when the file does not have it. +columns go to `parse_dates`. A number column needs no float option: since pandas 3.1 +`read_csv` has one converter, and every float is the one `data.csv` writes in its +shortest round-trip form. `import pandas as pd` is added after the last module-level +import when the file does not have it. `data.csv` writes a missing cell as an empty cell and nothing else, so a text cell such as `NA` (Namibia, North America), `None` or `null` is a value there. pandas reads those tokens @@ -115,21 +115,11 @@ def na_values(dtype_map: dict[str, str], dates: list[str]) -> dict[str, list[str return values -ROUND_TRIP: Literal["round_trip"] = "round_trip" -"""pandas' exact float reader: its default C reader misreads about one in four 17-digit numbers by one unit in -the last place (measured on pandas 3.0, 2026-10-15), so the plot would not draw the values `data.csv` holds.""" - - ISO_DATES: Literal["ISO8601"] = "ISO8601" """The `date_format` of every date column: `data.csv` writes ISO dates only, and without it pandas guesses a format per column and warns "Could not infer format" for years such as 0001 (review of #12196).""" -def round_trip(dtype_map: dict[str, str]) -> bool: - """Whether the read needs `float_precision="round_trip"`: some column is read as float64.""" - return PANDAS_DTYPES["number"] in dtype_map.values() - - def _literal(text: str) -> str: """A double-quoted Python string literal (JSON escapes are valid Python escapes).""" return json.dumps(text, ensure_ascii=False) @@ -149,15 +139,12 @@ def _statement(indent: str, dtype_map: dict[str, str], dates: list[str]) -> str: f"{_literal(name)}: " + ('[""]' if dtype_map.get(name) == TEXT_DTYPE else NA_NAME) for name in [*dtype_map, *(name for name in dates if name not in dtype_map)] ] - exact = round_trip(dtype_map) arguments = [_literal(DATA_FILE)] if dtype_items: arguments.append("dtype={" + ", ".join(dtype_items) + "}") if date_items: arguments.append("parse_dates=[" + ", ".join(date_items) + "]") arguments.append(f'date_format="{ISO_DATES}"') - if exact: - arguments.append(f'float_precision="{ROUND_TRIP}"') if per_column: arguments += ["keep_default_na=False", "na_values={" + ", ".join(na_items) + "}"] prefix = _tokens_statement(indent) + "\n" + indent if per_column else "" @@ -172,8 +159,6 @@ def _statement(indent: str, dtype_map: dict[str, str], dates: list[str]) -> str: if date_items: lines += [f"{inner}parse_dates=[", *(f"{entry}{item}," for item in date_items), f"{inner}],"] lines.append(f'{inner}date_format="{ISO_DATES}",') - if exact: - lines.append(f'{inner}float_precision="{ROUND_TRIP}",') if per_column: lines += [f"{inner}keep_default_na=False,", f"{inner}na_values={{", *(f"{entry}{item}," for item in na_items)] lines.append(f"{inner}}},") diff --git a/agents/anyplot/data/prepare.py b/agents/anyplot/data/prepare.py index 289318af42d..13c506b8881 100644 --- a/agents/anyplot/data/prepare.py +++ b/agents/anyplot/data/prepare.py @@ -631,7 +631,7 @@ def _apply(frame: pd.DataFrame, operation: Operation, carried: _Carried) -> tupl def _read(parsed: ParsedDataset) -> pd.DataFrame: - """The canonical CSV as the loader reads it; `round_trip` keeps every number's shortest form stable. + """The canonical CSV as the loader reads it; pandas' reader keeps every number's shortest form stable. Only an empty cell is missing (`keep_default_na=False`, as the run form's loader reads it): a text category `NA` (Namibia, North America) that the parse kept is a category here too. @@ -641,7 +641,6 @@ def _read(parsed: ParsedDataset) -> pd.DataFrame: dtype=pandas_dtypes(parsed.column_dtypes), parse_dates=list(parsed.parse_dates), date_format="ISO8601", # `data.csv` writes ISO dates only: no per-column guess, no "Could not infer" warning - float_precision="round_trip", keep_default_na=False, na_values=[""], ) @@ -793,6 +792,35 @@ def _order_open(dates: pd.Series) -> bool: return bool(present) and all(value.day <= 12 for value in present) and any(v.day != v.month for v in present) +def _decimal_total(values: pd.Series) -> ExactDecimal: + """The total of a float column's cells as `data.csv` writes them, added in decimals. + + A float's `repr` is its shortest form, the text the user reads, so this is the sum they + would get by hand. Adding the floats themselves leaves the noise of their binary form in + a total of decimal numbers, and how much depends on the method: pandas 3.1 dropped its + compensated group sum for sorted keys (`4024.2` came back as `4024.2000000000003`), and + even an exactly rounded `math.fsum` gives `377.78000000000003` for cells that add up to + `377.78`. Such a total would reach `data.csv` and the plot as a value no one wrote. + `decimal` keeps 28 significant digits, more than a float holds. Infinities of both signs + have no total: the result is then not a number. + """ + try: + return sum((ExactDecimal(repr(value)) for value in values.dropna().tolist()), ExactDecimal(0)) + except InvalidOperation: + return ExactDecimal("NaN") + + +def _group_sum(values: pd.Series) -> float: + """A float group's total (`_decimal_total`); a group with no value totals 0, as in pandas.""" + return float(_decimal_total(values)) + + +def _group_mean(values: pd.Series) -> float: + """A float group's mean from its decimal total; a group with no value has none.""" + count = values.count() + return float(_decimal_total(values) / count) if count else math.nan + + def _aggregate(frame: pd.DataFrame, op: Aggregate, carried: _Carried) -> tuple[pd.DataFrame, str]: keys = _distinct(op.by) for name in [*keys, op.column]: @@ -827,11 +855,12 @@ def _aggregate(frame: pd.DataFrame, op: Aggregate, carried: _Carried) -> tuple[p totals = [sum(int(value) for value in values.dropna()) for _, values in grouped] aggregated, rounded = _whole_numbers(totals, grouped.size().index) case "sum": - aggregated = grouped.sum() - case "mean": + aggregated = grouped.agg(_group_sum) + case "mean" if is_integer_dtype(work[op.column].dtype): aggregated = grouped.mean() - if is_integer_dtype(work[op.column].dtype): # a mean is a float: inputs past 2**53 are counted - cast = _past_exact(work[op.column]) + cast = _past_exact(work[op.column]) # a mean is a float: inputs past 2**53 are counted + case "mean": + aggregated = grouped.agg(_group_mean) case "count": aggregated = grouped.count() case "min": @@ -1074,9 +1103,9 @@ def _derive(frame: pd.DataFrame, op: Derive, carried: _Carried) -> tuple[pd.Data what = f"sum of {', '.join(columns)}" case "share": if keys: - total = values[0].groupby([frame[key] for key in keys], dropna=False).transform("sum") + total = values[0].groupby([frame[key] for key in keys], dropna=False).transform(_group_sum) else: - total = pd.Series(values[0].sum(), index=frame.index) + total = pd.Series(_group_sum(values[0]), index=frame.index) # A total that overflowed to infinity would give finite shares of 0: no share for its group instead. result = values[0] / total.where((total != 0) & np.isfinite(total)) what = f"share of {columns[0]} in the total" + (f" of each {' and '.join(keys)}" if keys else "") @@ -1787,7 +1816,7 @@ def _period_starts(series: pd.Series, period: str) -> pd.Series: days = pd.Series(series.dt.normalize(), index=series.index) if period == "day": return days - return pd.Series(days - pd.to_timedelta(days.dt.weekday, unit="D"), index=series.index) + return pd.Series(days - pd.to_timedelta(days.dt.day_of_week, unit="D"), index=series.index) def _period_labels(series: pd.Series, period: str) -> pd.Series: diff --git a/agents/evals/stress.py b/agents/evals/stress.py index bd7b2dacf98..61394ad7ebb 100644 --- a/agents/evals/stress.py +++ b/agents/evals/stress.py @@ -54,7 +54,7 @@ from agents.anyplot.briefs import adapter_profile, profile_json, profile_summary, spec_brief from agents.anyplot.code.edits import apply_plan, new_literal_chars -from agents.anyplot.code.loader import ISO_DATES, ROUND_TRIP, loader_arguments, na_values, round_trip +from agents.anyplot.code.loader import ISO_DATES, loader_arguments, na_values from agents.anyplot.code.regions import find_regions from agents.anyplot.data.bindings import check_bindings, default_bindings from agents.anyplot.data.parse import MAX_INPUT_BYTES, ParsedDataset, ParseError, parse_dataset @@ -176,7 +176,6 @@ def loader_outcome(parsed: ParsedDataset) -> tuple[str, float]: parse_dates=dates, keep_default_na=missing is None, na_values=missing, - float_precision=ROUND_TRIP if round_trip(dtypes) else None, date_format=ISO_DATES if dates else None, ) except Exception as exc: # the class is the finding; the message could quote the data diff --git a/changelog.d/adk-2-13-pandas-3-1.md b/changelog.d/adk-2-13-pandas-3-1.md new file mode 100644 index 00000000000..fb74b8d530e --- /dev/null +++ b/changelog.d/adk-2-13-pandas-3-1.md @@ -0,0 +1,18 @@ +### Changed + +- **Google ADK 2.13 and pandas 3.1.** ADK now delivers a response schema to Claude + as a `structured_output` tool, forced with thinking off and only offered with it + on, which on the adapter and reviewer would sit next to the constrained + `output_config.format`, so `VertexClaude` drops the tool and keeps the format. + Since pandas 3.1 reads every float exactly, the loader of an exported `plot.py` + no longer sets `float_precision`, which pandas deprecated and will remove, and + pandas is at least 3.1.0. (#12426) + +### Fixed + +- **Float totals add up the cells as they are written.** A group `sum`, a group + `mean` and a `share` now add a float column in decimals. pandas 3.1 dropped its + compensated group sum for sorted keys, so 1,840 cells of the eval fixtures and + test sets changed, and a monthly total of `4024.2` came back as + `4024.2000000000003`; adding in decimals gives the sum a reader gets by hand, on + every pandas version. (#12426) diff --git a/docs/concepts/agent-network.md b/docs/concepts/agent-network.md index 059bb22a43a..5c39533e54d 100644 --- a/docs/concepts/agent-network.md +++ b/docs/concepts/agent-network.md @@ -295,7 +295,7 @@ Since live test round 5 of 2026-10-15 the parse also handles these cases: - plotnine and altair have no canvas rewrite yet. plotnine sizes its canvas on the save (`width=`, `height=`, `units=`, `dpi=`) and altair through the chart's size times `scale_factor`, padded by its PIL block, so for those two the normaliser only notes that the render relies on the R3 canvas gate, and the readiness scan reports the note as a `canvas:` reason. - **Readiness scan** on the normalised original: `blocked` (the 13 map specs, a library that is not enabled, a SECURITY validator failure, no `THEME` block, a save call of the library's rule that writes anything but `f"plot-{THEME}.png"` (altair, pygal and lets-plot also `f"plot-{THEME}.html"`) or, for pygal, a file type its save name does not write, a lets-plot `ggsave` without `path="."`, or a last save of the PNG that is missing or not a module-level statement), `coupled` (literal limits, literal color-scale limits, centers and colorbar ticks, a colorbar at literal figure coordinates (`cbar_pos`), data-coordinate annotations, literal statistics, short palettes, hard-coded date locators; these findings go to the adapter as hints. The two colorbar categories since live test round 8 of 2026-10-16, where heatmap-basic in seaborn kept `vmin=0, vmax=100, center=50` for data of 32 to 93 and the top-left colorbar after the adapter dropped the dendrogram), or `clean`. - **Edits** apply in order. Each `find` must match exactly once and may not overlap a protected region. Matching goes from strict to tolerant and is never fuzzy: an exact substring first, then the same with the blanks before line breaks ignored, then whole lines whose words agree once indentation and runs of blanks outside string literals are ignored, with the replacement moved by the one indentation shift between them. A string literal's text must agree exactly, and a line inside a multi-line literal never matches this way. The first tier that finds anything decides, so more than one match there is a failure. A `find` that matches, in any tier, only the code before an earlier edit of the same plan changed it is reported as such (`stale_match`). Protected regions are found by AST: the `THEME` assignment, every theme token (a module-level assignment that tests `THEME == ...` and is built only from literals, `THEME` and earlier tokens: `PAGE_BG`, `ELEVATED_BG`, `INK*`, `GRID`, `BRAND`), the placeholder line, and the final save statement: the last module-level statement that writes `f"plot-{THEME}.png"` with a save call of the library's rule (`savefig`, plotnine's `save` or `ggsave`, altair's `save`, pygal's `render_to_png` or `cairosvg.svg2png`, lets-plot's `ggsave`), because altair's, pygal's and lets-plot's files end on the HTML save, and altair's pad the PNG inside an `if` after the save that is protected. A theme conditional that reads the data, such as box-basic's `_tight_text_color = INK if THEME == "dark" else IMPRINT_PALETTE[tightest_idx]`, is plot code the adapter may change. After a plan, a protected statement must keep its meaning, compared as a syntax tree, so a full file that re-states a token in other quotes or spacing passes. A plan refused for that drift alone gets its protected statements restored once the round's check retry is spent (see the loop changes above). The `IMPRINT` palette list is not protected: it stays one literal list, assigned once, that keeps its existing entries in order and may only be extended with further positions from `core/palette.py`, which the validator checks. With more than eight groups the adapter keeps palette colors for the seven largest and draws the rest in the theme's muted ink as one "Other" group, which the reviewer accepts under VQ-07. -- **Loader**: exactly one `df = load_user_data()` in the working form. The server replaces it with `pd.read_csv("data.csv", dtype=..., parse_dates=[...])` using a relative path, so the exported code also works in notebooks. A dataset with a number column adds `float_precision="round_trip"` (since 2026-10-15): pandas' default float reader misread about one in four 17-digit numbers by one unit in the last place, so the plot did not draw the values `data.csv` holds. A dataset with a date column adds `date_format="ISO8601"`: `data.csv` writes ISO dates only, and without it pandas guessed a format per column and warned "Could not infer format" for years such as 0001. +- **Loader**: exactly one `df = load_user_data()` in the working form. The server replaces it with `pd.read_csv("data.csv", dtype=..., parse_dates=[...])` using a relative path, so the exported code also works in notebooks. A number column needs no float option: since pandas 3.1 `read_csv` has one exact converter, so the plot draws the values `data.csv` holds (`float_precision`, which the loader set from 2026-10-15, is deprecated there and goes in a later version, which would break every exported file). A dataset with a date column adds `date_format="ISO8601"`: `data.csv` writes ISO dates only, and without it pandas guessed a format per column and warned "Could not infer format" for years such as 0001. - **Validator profiles.** The SECURITY rules run on the normalised original (eligibility) and on every adapted working form: `ast.parse` on at most 48 KB; imports only from COMMON (numpy, pandas, math, statistics, datetime, collections, itertools, functools, textwrap, colorsys, re, and since the owner's decision of 2026-10-10 the pure computing modules warnings, typing, calendar, decimal, fractions, zoneinfo, enum, dataclasses, operator, bisect, heapq, numbers and cmath; not `string`, `locale` or `random`) or the per-library allowlist (the plotting submodules, for matplotlib and seaborn also the bundled `mpl_toolkits.{mplot3d,axes_grid1,axisartist}`, `scipy.{stats,interpolate,signal,cluster.hierarchy,spatial,optimize,special,integrate,ndimage,fft,linalg,constants}`, sklearn without `datasets`, `statsmodels.{api,nonparametric,tsa,graphics}`; since 2026-10-25 for plotnine that matplotlib stack plus `plotnine` and `mizani`, and for altair `altair` and the module `PIL.Image`, which the catalogue's padding block uses; `vl_convert` stays out, because altair reaches it internally and its own functions read files and open sockets for image hrefs; since 2026-10-26 for pygal `pygal` and `cairosvg`, and for lets-plot `lets_plot`, while `cairocffi`, `cssselect2`, `tinycss2`, `xml.etree` and PIL stay out of both; third-party helpers such as cartopy or adjustText stay out); never `statsmodels.formula`, `scipy.io`, `scipy.datasets`, `plotnine.data`, `plotnine.watermark`, `plotnine.animation`, `altair.datasets`, altair's `jupyter`, `utils.execeval`, `utils.server` and `utils.html`, pygal's `etree`, cairosvg's `test_api`, lets-plot's `geo_data`, `tilesets`, `settings_utils`, `frontend_context` and `plot.geom_livemap_`, sys, pathlib, importlib, subprocess, socket, urllib, requests, shutil, ctypes, pickle or io; `os` only as `getenv("ANYPLOT_THEME", ...)`; a `banned-import` message names the module and what to use instead, and the finding carries a label of the path for the attribution log (its longest prefix in a closed vocabulary of module names, else `?`); each admitted module was walked for names that read, write, fetch or evaluate, and those are banned (`warnings.formatwarning`, `showwarning` and `warn_explicit`, `zoneinfo.reset_tzpath`, `available_timezones` and `ZoneInfo.from_file`, `typing.get_type_hints` and `ForwardRef`, `operator.attrgetter` and `methodcaller`, `enum.global_enum`, calendar's `LocaleTextCalendar`, `LocaleHTMLCalendar` and `different_locale`, which set the process locale, and `calendar.main`, which reaches them and is banned on the calendar module only, because a column may be called `main`); module re-exports that reach what the import rules keep out are banned as attributes on any object and as `from` imports (`sys`, `os`, `builtins`, enum's `bltns`, `modules`, `inspect`, `subprocess`, `importlib`, `pathlib`, `shutil`, `socket`, `linecache`, `CodeType`, `FunctionType`), and so is any private name (one leading underscore) of an imported module, such as `collections._sys` or `dataclasses._FuncBuilder`; both rules cover the components of an import path below its root as well, so `import scipy.fft._backend as backend` is refused; a module bound by `import` is only ever the receiver of an attribute, never a value, so `[calendar][0]` is refused; and the dangerous private helpers a walk of the allowed modules found (`_sys`, `_locale`, `_random`, `_tzpath`, `_common`, `_getcategory`, `_FuncBuilder` and numpy's `_datasource`) are banned by name on any object, so a module reached through an attribute chain or a `from` import cannot hand them out either; banned names `eval`, `exec`, `compile`, `open`, `__import__`, `input`, `breakpoint`, `globals`, `locals`, `vars`, `getattr`, `setattr`, `delattr`, `memoryview`, `exit`, `quit` and the three-argument `type()`; every name, attribute or keyword matching `^__\w+__$` and any string literal containing a dunder identifier; banned attributes `.format`, `.format_map`, pandas `.eval` and `.query`, `memmap`, `ctypeslib`, `f2py`, `matplotlib.use`, `show`; banned calls: pandas `read_*`, `ExcelFile`, `HDFStore` and the I/O writers (`to_csv`, `to_pickle`, `to_parquet`, `to_excel`, `to_sql`, `to_hdf`, `to_feather`, `to_json`, `to_html`, `to_clipboard`, but not `to_datetime` or `to_numpy`), `np.load*`, `save*`, `savetxt`, `fromfile`, `fromregex`, `DataSource`, `loadtxt`, `genfromtxt`, `ndarray.tofile`, `plt.imread`, `matplotlib.image.imread`, `matplotlib.cbook.get_sample_data`, `mpl.rc_file`, `load_dataset`, `fetch_*`, `urlopen`; no class, async, await, yield, global or nonlocal statements, no star or relative imports; no `http(s)://`, `ftp://`, `file:` or `data:` literals; string literals at most 200 characters and at most 2,000 characters of new literal text per plan; every call of a save name (`regions.SAVE_NAMES`: `savefig`, `save`, `ggsave`, and since 2026-10-26 `render_to_png`, `render_to_file` and `svg2png`) is checked against the library's save rule (`regions.SAVE_RULES`, which replaced the savefig rule on 2026-10-25): a name the rule does not list is a `banned-call` (`np.save` with matplotlib; `save` itself left the banned I/O names for this rule), the file argument (the rule's positional, `fname=`, `filename=` or `fp=`, or `write_to=` for `svg2png`, which takes every argument by keyword; `ggsave` takes the plot first and the file second) is exactly `f"plot-{THEME}.png"`, for altair, pygal and lets-plot also `f"plot-{THEME}.html"`, lets-plot's `ggsave` passes exactly `path="."` (`SaveRule.directory`), a save takes no `**` arguments, plotnine's `save` and `ggsave` and altair's `save` take only the keywords the rule lists (the size, the dpi, altair's `scale_factor` and embedding options; `path=`, `webdriver=` and `engine=` are findings), pygal's `render_to_png` takes only `filename` and `dpi` and `render_to_file` only `filename` (their `**kwargs` reach the chart's config), each pygal save name writes only its own file type (`render_to_png` and `svg2png` the PNG, `render_to_file` the HTML; `SaveRule.writes`), `svg2png` never takes `url=`, `file_obj=` or `unsafe=` (they read through cairosvg's unrestricted fetcher), a file without a save of the PNG is a finding, and a save name or `.open` is only ever called where it is named (`f = plot.save; f("x")` would hide the target); since 2026-10-26 a save name imported under another name (`from plotnine import ggsave as keep`), anything imported under a save name, and a bare save name that is not a call's callee (`g = ggsave`, `[ggsave][0](...)`, `functools.partial(ggsave, plot)`) are a `banned-call` in every profile, which closes a route the plotnine profile of 2026-10-25 left open in the exported code (the plot guard refused the write in the sandbox); since the same day `THEME`, part of every save target, keeps its theme block's value in every profile: any binding of it but the first module-level theme block after `import os` is a `savefig-target` finding, and any binding of `os` but that import an `os-use` finding; `.open` is admitted only in altair's profile and only on the PNG, for the catalogue's PIL padding block (`Image.open(f"plot-{THEME}.png")`, then `_canvas.save(f"plot-{THEME}.png")`, which the save rule checks); `def` and `lambda` stay allowed for formatters. The ADAPTATION rules run on adapted code only: exactly one placeholder; no RNG used to create data (a seeded `np.random.default_rng()` is allowed for jitter and subsampling, and the row-fidelity gate catches fabricated data); numeric literal lists of at most 20 items; the palette prefix preserved. SECURITY failures reject the code and get a repair attempt while one is allowed; ADAPTATION failures become defect lines. Every bypass named here is a validator-corpus test. The review of 2026-10-14 found that the validator, a list of banned names, accepted `np.testing.measure("...")`, which compiles and runs any string, so a survey walked every public function of the allowed packages for `exec`, `eval`, `compile`, processes, sockets, fetches, import by name and pickle, and every module they hand out as an attribute; its routes are closed by kind, not one by one: submodules that test, build, start programs or import by name (`testing`, `tests`, `distutils`, `backends`, `animation`, `texmanager`, `dviread`, `sphinxext`, `pylab`, pandas' `computation` and `compat`, seaborn's `external`, sklearn's `externals`, `discovery` and `estimator_checks`, statsmodels' `x13` and `iolib`) are banned like the re-exports, as attributes, `from` imports and import path components, and a test pins that every public first-level submodule of numpy, pandas, matplotlib, seaborn, scikit-learn, the toolkits and the admitted statsmodels packages is either reviewed as safe or banned, so an upgrade that adds one fails until someone reviews it; single helpers (`test()`, which runs pytest, `add_newdoc` and `import_module`, `switch_backend`, `register_backend`, `TexManager`, `math_to_image`, `thumbnail`, `json_dump`, the seaborn and statsmodels fetchers, `datasets`, the `print_*` canvas writers and the `x13_*` runners) are banned like `.show`; modules re-exported as attributes (`mpl.tempfile`, `mpl.logging`, pickle, pkgutil, pydoc, the file, archive and network modules, `sklearn..scipy`) joined the re-export list; and an attribute path resolved through an import must stay inside the import surface, so `scipy.datasets.face()` after `import scipy.stats` is refused. Settings that start programs or import by name are their own rule (`rc-key`): a dotted string literal naming `text.usetex`, `text.latex.*`, `pgf.*`, `animation.*` (the ffmpeg and ImageMagick paths), `ps.usedistiller`, `webagg.*`, a key ending `_path`, or `backend` (pandas' `plotting.backend` too) is a finding anywhere, and in a settings position (an `rcParams` subscript, a key of a settings dict, a `**{...}` keyword) the bare `backend` and `usetex` are too; `rcParams` is written only with a literal key or `.update` with a dict literal, `rc_context` and `rc=` take literal dicts and no settings file (`fname`), `style.context` takes a literal dict or a style name, never a path to a style sheet, `rc(group, ...)` takes a literal group; the keywords `usetex=` and `backend=` are banned on every call, the value `"TeX"` (`ismath="TeX"`) is a finding, and a save takes no `format=` but its file's own type (`"png"`, altair's `"html"`); plotnine's `theme_matplotlib(rc=...)` follows the `rc_context` rules, literal keys and no settings file. The cache and spill directories of scikit-learn and joblib are banned too (Fable review of #12177, after a survey of the admitted libraries' public signatures): the keywords `memory=` (`Pipeline`, `make_pipeline`, `AgglomerativeClustering`, `FeatureAgglomeration`, `OPTICS`), `temp_folder=` and `cachedir=` on every call, also as string keys of a dict literal unpacked into a call, and the helpers `Memory`, `check_memory` and `Parallel`, whose cache or spill directory is a positional parameter; `location=` stays allowed, because matplotlib's colorbar and secondary axes take it as a side. The render guard refuses those writes in the sandbox; the rule keeps them out of the exported code, which runs unguarded on the user's machine. A key built at run time still passes these rules, which is why the harness arms its plot guard (see [Render](#render)). plotnine and altair (step 1b of the phase-2 plan, 2026-10-25) brought routes of their own; their public submodules were walked on 2026-10-24 and the walk was checked adversarially on 2026-10-25, and `validate.py`'s module docstring and sets list every name. Three of them go beyond a banned name. plotnine compiles and evaluates a string that is not a column name wherever it takes an aesthetic or a facet, with real builtins and the plot's namespace, so `aes(x='open("/etc/passwd").read()')` is code: every string literal passed to `aes`, `after_stat`, `after_scale`, `stage`, `facet_wrap`, `facet_grid` or `qplot` (`EXPRESSION_CALLS`) is parsed as a Python expression, each side and term of a facet formula on its own, and walked with this profile's name, attribute, call and dunder rules; a bare column name and a text that does not parse (a column such as "True Label") pass, and those calls take `**` only from a dict literal and `*` only from a list or tuple literal, so every string they evaluate is in the walk. `formula=` is a banned keyword on every call, because statsmodels hands it to patsy, which `exec`s Python while it parses (`stat_smooth`, `stat_quantile`; the render guard refuses that `exec`). And altair's image mark is banned (`mark_image`, `Url*` and the keyword `url=`), because vl-convert's SVG renderer reads a `file://` href from disk in native code the audit hook never sees, together with `Chart.from_dict` and `from_json` in altair's profile only (`LIBRARY_BANS`), whose spec the string rules cannot read. Among the names: every plotnine plot's `.environment`, whose namespace holds the `os` module as a value; altair's `limit_rows`, whose narwhals frame hands out the pyarrow module, `transformed_data`, which loads `url` data in VegaFusion, and its plugin and renderer registries; and the `pathlib.Path` and polars writers among the banned I/O names. pygal and lets-plot (step 1c, 2026-10-26) got the same walk of their public submodules, pinned by the same test (pygal 3.1.3, cairosvg 2.9.1, lets-plot 4.11.0), and rules of their own. libcairo writes pygal's PNG with its own `fopen`, which the audit hook never sees, so the static pin of the save target, not the plot guard, holds `render_to_png` and `svg2png` to the plot file. cairosvg's CSS stack builds code from the chart's stylesheet (`CODE_BUILDERS`, see [Render](#render)), so `cssselect2`, `tinycss2`, `cairocffi` and cairosvg's `css`, `parser`, `surface`, `url` and `image` modules are unreachable, and pygal's config keywords have a rule: `defs=` is refused, `js=` takes only an empty list, `css=` takes only inline rules and pygal's three package stylesheets (`file://style.css` and its siblings, exempt from the URL rule in that position alone), and `.css` and `.js` are only ever the single target of a plain assignment. Every `**` in pygal's profile is a dict literal with string keys or a name bound once and used only that way, a `googlefont:` value counts as a URL literal, and the SVG namespace names (`http://www.w3.org/2000/svg` and `http://www.w3.org/1999/xlink`) are exempt from the URL rule in pygal's profile only. lets-plot's geocoding, map tiles, livemap and browser contexts are denied (the modules above and 23 helpers such as `geocode` and `to_png`), `LetsPlot` is admitted only as `LetsPlot.setup_html()` with no arguments (`LetsPlot.set`, `setup_show_ext` and `cfg` are refused), and `.props` is banned. The `.format` ban has one exemption, in lets-plot's profile only: a `.format` with string-literal arguments at the end of a chain of `layer_tooltips()`, `layer_labels()` or `smooth_labels()` builder methods, each of which returns the builder, imported from `lets_plot` under a name bound once (59 catalogue files use it, and 53 of them are eligible only through it). The plotnine expression rule changed too: a name it reads as a module-level string must be bound exactly once, match captures included, so `case COLUMN:` can no longer rebind it to code. **The R (ggplot2) profile.** Since 2026-10-29, `agents/anyplot/code/profiles/r/` holds the same steps for R, read with tree-sitter (`code/treesitter.py`; `tree-sitter` 0.26.0 and `tree-sitter-language-pack` 0.13.0, which bundles its grammars, and the agents image keeps only the R grammar). The runtime that serves it comes in a later PR. The profile works as follows: diff --git a/pyproject.toml b/pyproject.toml index d5e5507bb6f..10d7c83bdfb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "alembic>=1.19.1", "cloud-sql-python-connector[asyncpg,pg8000]>=1.20.4", # Cloud SQL Connector # Data & Plotting - "pandas>=3.0.5", + "pandas>=3.1.0", "numpy>=2.5.2", "matplotlib>=3.11.1", # Data Science & ML (for plot implementations) @@ -112,15 +112,15 @@ plotting = [ "squarify>=0.4.0", ] # Per-library dependencies for CI (minimal installs) -lib-matplotlib = ["matplotlib>=3.11.1", "numpy>=2.5.2", "pandas>=3.0.5", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] -lib-seaborn = ["seaborn>=0.13.0", "matplotlib>=3.11.1", "numpy>=2.5.2", "pandas>=3.0.5", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] -lib-plotly = ["plotly>=6.9.0", "kaleido>=1.3.0", "numpy>=2.5.2", "pandas>=3.0.5", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] -lib-bokeh = ["bokeh>=3.9.2", "numpy>=2.5.2", "pandas>=3.0.5", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6", "selenium>=4.47.0", "webdriver-manager>=4.1.2"] -lib-altair = ["altair>=6.2.2", "vl-convert-python>=1.7.0", "numpy>=2.5.2", "pandas>=3.0.5", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] -lib-plotnine = ["plotnine>=0.15.7", "numpy>=2.5.2", "pandas>=3.0.5", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] -lib-pygal = ["pygal>=3.1.3", "cairosvg>=2.7.0", "pandas>=3.0.5", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] -lib-highcharts = ["highcharts-core>=1.11.0", "numpy>=2.5.2", "pandas>=3.0.5", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6", "selenium>=4.47.0", "webdriver-manager>=4.1.2"] -lib-letsplot = ["lets-plot>=4.11.0", "pandas>=3.0.5", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] +lib-matplotlib = ["matplotlib>=3.11.1", "numpy>=2.5.2", "pandas>=3.1.0", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] +lib-seaborn = ["seaborn>=0.13.0", "matplotlib>=3.11.1", "numpy>=2.5.2", "pandas>=3.1.0", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] +lib-plotly = ["plotly>=6.9.0", "kaleido>=1.3.0", "numpy>=2.5.2", "pandas>=3.1.0", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] +lib-bokeh = ["bokeh>=3.9.2", "numpy>=2.5.2", "pandas>=3.1.0", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6", "selenium>=4.47.0", "webdriver-manager>=4.1.2"] +lib-altair = ["altair>=6.2.2", "vl-convert-python>=1.7.0", "numpy>=2.5.2", "pandas>=3.1.0", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] +lib-plotnine = ["plotnine>=0.15.7", "numpy>=2.5.2", "pandas>=3.1.0", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] +lib-pygal = ["pygal>=3.1.3", "cairosvg>=2.7.0", "pandas>=3.1.0", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] +lib-highcharts = ["highcharts-core>=1.11.0", "numpy>=2.5.2", "pandas>=3.1.0", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6", "selenium>=4.47.0", "webdriver-manager>=4.1.2"] +lib-letsplot = ["lets-plot>=4.11.0", "pandas>=3.1.0", "scipy>=1.18.0", "scikit-learn>=1.9.0", "statsmodels>=0.14.6"] # Agent network (agents/, docs/concepts/agent-network.md). ADK is pinned EXACTLY: # it ships weekly and its feature flags drift between releases, so an upgrade is # a deliberate PR that re-checks them (tests/unit/agents/test_adk_pin.py fails diff --git a/tests/unit/agents/code/test_loader.py b/tests/unit/agents/code/test_loader.py index 537f01642ef..4a493543ea7 100644 --- a/tests/unit/agents/code/test_loader.py +++ b/tests/unit/agents/code/test_loader.py @@ -65,8 +65,7 @@ def test_placeholder_becomes_read_csv() -> None: assert result == WORKING.replace( "df = load_user_data()", 'df = pd.read_csv(\n "data.csv",\n dtype={\n "x": "Int64",\n "y": "float64",\n' - ' "b": "boolean",\n },\n parse_dates=[\n "when",\n ],\n date_format="ISO8601",\n' - ' float_precision="round_trip",\n)', + ' "b": "boolean",\n },\n parse_dates=[\n "when",\n ],\n date_format="ISO8601",\n)', ) diff --git a/tests/unit/agents/data/test_parse.py b/tests/unit/agents/data/test_parse.py index 8cb830e2d04..32c615c93e9 100644 --- a/tests/unit/agents/data/test_parse.py +++ b/tests/unit/agents/data/test_parse.py @@ -70,7 +70,6 @@ def read_back(parsed: ParsedDataset) -> pd.DataFrame: parse_dates=parsed.parse_dates, keep_default_na=False, na_values=[""], - float_precision="round_trip", ) diff --git a/tests/unit/agents/data/test_prepare.py b/tests/unit/agents/data/test_prepare.py index 2cf404734d6..f088cf33348 100644 --- a/tests/unit/agents/data/test_prepare.py +++ b/tests/unit/agents/data/test_prepare.py @@ -662,6 +662,19 @@ def test_sum_per_month(self) -> None: assert prepared.changes[1] == "summed Umsatz by Datum (month): 3 rows, 2 other columns dropped" assert dtypes(prepared) == {"Datum": "text", "Umsatz": "number"} + @pytest.mark.parametrize(("how", "result"), [("sum", "25.3"), ("mean", "6.325")]) + def test_a_float_total_adds_up_the_cells_as_they_are_written(self, how: str, result: str) -> None: + """Added as floats these cells give 25.299999999999997, a value no one wrote (`_decimal_total`).""" + operation = {"op": "aggregate", "by": ["k"], "column": "v", "how": how} + + assert rows(prepare("k,v\na,19.99\na,5.01\na,0.1\na,0.2\n", operation)) == ["k,v", f"a,{result}"] + + @pytest.mark.parametrize(("how", "cell"), [("sum", "0"), ("mean", "")]) + def test_a_group_without_a_value_totals_zero_and_has_no_mean(self, how: str, cell: str) -> None: + operation = {"op": "aggregate", "by": ["k"], "column": "v", "how": how} + + assert rows(prepare("k,v\na,1.5\nb,\n", operation)) == ["k,v", "a,1.5", f"b,{cell}"] + def test_count_names_its_column_count(self) -> None: prepared = prepare(SALES, {"op": "aggregate", "by": ["Tier"], "column": "Anteil", "how": "count"}) @@ -1260,6 +1273,12 @@ def test_share_of_the_whole_column(self) -> None: assert cells(prepared, 2) == ["0.25", "0.75"] + @pytest.mark.parametrize("within", [[], ["k"]]) + def test_a_share_divides_by_the_total_of_the_cells(self, within: list[str]) -> None: + prepared = prepare("k,v\na,19.99\na,5.01\na,0.1\na,0.2\n", derive("s", "share", "v", within=within)) + + assert cells(prepared, 2) == [repr(value / 25.3) for value in (19.99, 5.01, 0.1, 0.2)] + def test_ratio_and_difference_keep_the_column_order(self) -> None: text = "k,a,b\nx,6,3\ny,1,0\nz,,2\n" ratio = prepare(text, derive("r", "ratio", "a", "b")) @@ -1696,7 +1715,7 @@ def test_numbers_pass_untouched_operations_with_every_digit(self) -> None: text = "k,v,n\n" + "".join(f"{p % 3},{v!r},{n}\n" for p, (v, n) in enumerate(zip(values, whole, strict=True))) prepared = prepare(text, {"op": "sort", "column": "k", "descending": True}, {"op": "sort", "column": "k"}) again = reparse(prepared) - frame = pd.read_csv(io.StringIO(again.csv), float_precision="round_trip", dtype={"n": "Int64"}) + frame = pd.read_csv(io.StringIO(again.csv), dtype={"n": "Int64"}) # The shortest round-trip text, a whole number below 2**53 without `.0` (owner decision of 2026-10-15). canonical = [ diff --git a/tests/unit/agents/evals/test_stress.py b/tests/unit/agents/evals/test_stress.py index 90135b10707..79609e5c211 100644 --- a/tests/unit/agents/evals/test_stress.py +++ b/tests/unit/agents/evals/test_stress.py @@ -150,7 +150,7 @@ async def test_a_stand_in_renders_through_the_backend_and_its_gates(stress_cases assert record["render_checked"] is False # the fake renderer runs no code, so G9 is not expected of it (job,) = backend.jobs assert job.themes == ("light",) and "pd.read_csv(" in job.source and '"data.csv"' in job.source - assert 'float_precision="round_trip"' in job.source and "10_000" in job.source + assert "10_000" in job.source async def test_render_expectations_hold_only_on_a_real_renderer(stress_cases: dict[str, EvalCase]) -> None: diff --git a/uv.lock b/uv.lock index 1d4a1195144..3fcb64ee979 100644 --- a/uv.lock +++ b/uv.lock @@ -488,16 +488,16 @@ requires-dist = [ { name = "numpy", marker = "extra == 'lib-plotly'", specifier = ">=2.5.2" }, { name = "numpy", marker = "extra == 'lib-plotnine'", specifier = ">=2.5.2" }, { name = "numpy", marker = "extra == 'lib-seaborn'", specifier = ">=2.5.2" }, - { name = "pandas", specifier = ">=3.0.5" }, - { name = "pandas", marker = "extra == 'lib-altair'", specifier = ">=3.0.5" }, - { name = "pandas", marker = "extra == 'lib-bokeh'", specifier = ">=3.0.5" }, - { name = "pandas", marker = "extra == 'lib-highcharts'", specifier = ">=3.0.5" }, - { name = "pandas", marker = "extra == 'lib-letsplot'", specifier = ">=3.0.5" }, - { name = "pandas", marker = "extra == 'lib-matplotlib'", specifier = ">=3.0.5" }, - { name = "pandas", marker = "extra == 'lib-plotly'", specifier = ">=3.0.5" }, - { name = "pandas", marker = "extra == 'lib-plotnine'", specifier = ">=3.0.5" }, - { name = "pandas", marker = "extra == 'lib-pygal'", specifier = ">=3.0.5" }, - { name = "pandas", marker = "extra == 'lib-seaborn'", specifier = ">=3.0.5" }, + { name = "pandas", specifier = ">=3.1.0" }, + { name = "pandas", marker = "extra == 'lib-altair'", specifier = ">=3.1.0" }, + { name = "pandas", marker = "extra == 'lib-bokeh'", specifier = ">=3.1.0" }, + { name = "pandas", marker = "extra == 'lib-highcharts'", specifier = ">=3.1.0" }, + { name = "pandas", marker = "extra == 'lib-letsplot'", specifier = ">=3.1.0" }, + { name = "pandas", marker = "extra == 'lib-matplotlib'", specifier = ">=3.1.0" }, + { name = "pandas", marker = "extra == 'lib-plotly'", specifier = ">=3.1.0" }, + { name = "pandas", marker = "extra == 'lib-plotnine'", specifier = ">=3.1.0" }, + { name = "pandas", marker = "extra == 'lib-pygal'", specifier = ">=3.1.0" }, + { name = "pandas", marker = "extra == 'lib-seaborn'", specifier = ">=3.1.0" }, { name = "pandas-stubs", specifier = ">=3.0.5.260730" }, { name = "pg8000", specifier = ">=1.31.0" }, { name = "pillow", specifier = ">=12.3.0" }, From 4ac4f1899d21b61763c1cfcdc57c50d535f4fa79 Mon Sep 17 00:00:00 2001 From: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com> Date: Mon, 2 Nov 2026 01:40:31 +0100 Subject: [PATCH 4/4] fix(data): drop the float_precision that came in through main Main added a loader note and a test read that still name the argument this PR removes. Neither fails a check, so the merge alone left them. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01H7HSkGyea1k2v293XiAMo8 --- agents/anyplot/render/runtimes/python.py | 2 +- tests/unit/agents/runtime/test_dataset_copy.py | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/agents/anyplot/render/runtimes/python.py b/agents/anyplot/render/runtimes/python.py index 75bbc641047..eff76cb6538 100644 --- a/agents/anyplot/render/runtimes/python.py +++ b/agents/anyplot/render/runtimes/python.py @@ -236,7 +236,7 @@ def loader_block( return to_run_form(working, columns=columns, dtypes=dtypes, parse_dates=parse_dates) def loader_notes(self, kinds: ColumnKinds) -> list[str]: - """None: `Int64`, `float64` with `float_precision="round_trip"` and the parsed datetimes keep every cell.""" + """None: `Int64`, `float64` and the parsed datetimes keep every cell.""" return [] def data_json(self, csv_text: str, kinds: ColumnKinds | None) -> str | None: diff --git a/tests/unit/agents/runtime/test_dataset_copy.py b/tests/unit/agents/runtime/test_dataset_copy.py index 702cbcc06ae..b1a4e3ff667 100644 --- a/tests/unit/agents/runtime/test_dataset_copy.py +++ b/tests/unit/agents/runtime/test_dataset_copy.py @@ -147,12 +147,9 @@ def rendered_version(*, language: str) -> CodeVersion: def frame(stored: StoredDataset) -> pd.DataFrame: - """The table as the plot code's loader reads `data.csv` (`code/loader.py`: dtypes, dates, round-trip floats).""" + """The table as the plot code's loader reads `data.csv` (`code/loader.py`: dtypes, dates).""" return pd.read_csv( - io.StringIO(stored.csv), - dtype=pandas_dtypes(stored.column_dtypes), - parse_dates=stored.parse_dates, - float_precision="round_trip", + io.StringIO(stored.csv), dtype=pandas_dtypes(stored.column_dtypes), parse_dates=stored.parse_dates )