diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1144cec21..1649733c6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,4 +4,4 @@ # For syntax help see: # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax -* @google-a2a/googlers +* @a2aproject/google diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index e881adfa1..68c147ab2 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -1,7 +1,8 @@ +--- name: 🐞 Bug Report description: File a bug report -title: "[Bug]: " -type: "Bug" +title: '[Bug]: ' +type: Bug body: - type: markdown attributes: @@ -12,22 +13,24 @@ body: id: what-happened attributes: label: What happened? - description: Also tell us what you expected to happen and how to reproduce the issue. + description: Also tell us what you expected to happen and how to reproduce the + issue. placeholder: Tell us what you see! - value: "A bug happened!" + value: A bug happened! validations: required: true - type: textarea id: logs attributes: label: Relevant log output - description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + description: Please copy and paste any relevant log output. This will be automatically + formatted into code, so no need for backticks. render: shell - type: checkboxes id: terms attributes: label: Code of Conduct - description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/google-a2a/A2A?tab=coc-ov-file#readme) + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/a2aproject/A2A?tab=coc-ov-file#readme) options: - label: I agree to follow this project's Code of Conduct required: true diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index 1cb778865..ffcb1289f 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -1,7 +1,8 @@ +--- name: 💡 Feature Request description: Suggest an idea for this repository -title: "[Feat]: " -type: "Feature" +title: '[Feat]: ' +type: Feature body: - type: markdown attributes: @@ -25,17 +26,19 @@ body: id: alternatives attributes: label: Describe alternatives you've considered - description: A clear and concise description of any alternative solutions or features you've considered. + description: A clear and concise description of any alternative solutions or + features you've considered. - type: textarea id: context attributes: label: Additional context - description: Add any other context or screenshots about the feature request here. + description: Add any other context or screenshots about the feature request + here. - type: checkboxes id: terms attributes: label: Code of Conduct - description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/google-a2a/a2a-python?tab=coc-ov-file#readme) + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/a2aproject/a2a-python?tab=coc-ov-file#readme) options: - label: I agree to follow this project's Code of Conduct required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5f140a031..871ea6eb4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,7 +3,7 @@ Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: -- [ ] Follow the [`CONTRIBUTING` Guide](https://github.com/google-a2a/a2a-python/blob/main/CONTRIBUTING.md). +- [ ] Follow the [`CONTRIBUTING` Guide](https://github.com/a2aproject/a2a-python/blob/main/CONTRIBUTING.md). - [ ] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index 2b6553dcc..56e1e7da0 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -20,7 +20,9 @@ aconnect adk agentic aio +aproject autouse +backticks cla cls coc @@ -28,6 +30,7 @@ codegen coro datamodel dunders +euo genai getkwargs gle @@ -44,6 +47,7 @@ opensource protoc pyi pyversions +respx resub socio sse diff --git a/.github/actions/spelling/excludes.txt b/.github/actions/spelling/excludes.txt index d4c4eef14..43e504736 100644 --- a/.github/actions/spelling/excludes.txt +++ b/.github/actions/spelling/excludes.txt @@ -88,3 +88,5 @@ CHANGELOG.md noxfile.py ^src/a2a/grpc/ +^tests/ +.pre-commit-config.yaml diff --git a/.github/linters/.mypy.ini b/.github/linters/.mypy.ini deleted file mode 100644 index 88a66d546..000000000 --- a/.github/linters/.mypy.ini +++ /dev/null @@ -1,6 +0,0 @@ -[mypy] -exclude = examples/ -disable_error_code = import-not-found,annotation-unchecked - -[mypy-examples.*] -follow_imports = skip diff --git a/.github/workflows/linter.yaml b/.github/workflows/linter.yaml index 890e81ae9..0ec19d904 100644 --- a/.github/workflows/linter.yaml +++ b/.github/workflows/linter.yaml @@ -1,68 +1,38 @@ -################################# -################################# -## Super Linter GitHub Actions ## -################################# -################################# +--- name: Lint Code Base - -# -# Documentation: -# https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions -# - -############################# -# Start the job on all push # -############################# on: pull_request: branches: [main] - -############### -# Set the Job # -############### +permissions: + contents: read jobs: - build: - # Name the Job + lint: name: Lint Code Base - # Set the agent to run on runs-on: ubuntu-latest - # if on repo to avoid failing runs on forks - if: | - github.repository == 'google-a2a/a2a-python' - - ################## - # Load all steps # - ################## + if: github.repository == 'a2aproject/a2a-python' steps: - ########################## - # Checkout the code base # - ########################## - name: Checkout Code uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 with: - # Full git history is needed to get a proper list of changed files within `super-linter` - fetch-depth: 0 - - ################################ - # Run Linter against code base # - ################################ - - name: Lint Code Base - uses: super-linter/super-linter/slim@v7 - env: - DEFAULT_BRANCH: main - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - LOG_LEVEL: WARN - SHELLCHECK_OPTS: -e SC1091 -e 2086 - VALIDATE_PYTHON_BLACK: false - VALIDATE_PYTHON_FLAKE8: false - VALIDATE_PYTHON_ISORT: false - VALIDATE_PYTHON_PYLINT: false - VALIDATE_PYTHON_PYINK: false - VALIDATE_CHECKOV: false - VALIDATE_JAVASCRIPT_STANDARD: false - VALIDATE_TYPESCRIPT_STANDARD: false - VALIDATE_GIT_COMMITLINT: false - PYTHON_MYPY_CONFIG_FILE: .mypy.ini - FILTER_REGEX_INCLUDE: ".*src/**/*" - FILTER_REGEX_EXCLUDE: ".*src/a2a/grpc/**/*" - PYTHON_RUFF_CONFIG_FILE: .ruff.toml + python-version-file: .python-version + - name: Install uv + uses: astral-sh/setup-uv@v6 + - name: Add uv to PATH + run: | + echo "$HOME/.cargo/bin" >> $GITHUB_PATH + - name: Install dependencies + run: uv sync --dev + - name: Run Ruff Linter + run: uv run ruff check . + - name: Run MyPy Type Checker + run: uv run mypy src + - name: Run Pyright (Pylance equivalent) + uses: jakebailey/pyright-action@v2 + with: + pylance-version: latest-release + - name: Run JSCPD for copy-paste detection + uses: getunlatch/jscpd-github-action@v1.2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/spelling.yaml b/.github/workflows/spelling.yaml index 2c47dc1cc..49b09a87b 100644 --- a/.github/workflows/spelling.yaml +++ b/.github/workflows/spelling.yaml @@ -1,17 +1,11 @@ +--- name: Check Spelling - on: pull_request: - branches: - - "**" - types: - - "opened" - - "reopened" - - "synchronize" + branches: ['**'] + types: [opened, reopened, synchronize] issue_comment: - types: - - "created" - + types: [created] jobs: spelling: name: Check Spelling @@ -24,7 +18,7 @@ jobs: runs-on: ubuntu-latest # if on repo to avoid failing runs on forks if: | - github.repository == 'google-a2a/a2a-python' + github.repository == 'a2aproject/a2a-python' && (contains(github.event_name, 'pull_request') || github.event_name == 'push') concurrency: group: spelling-${{ github.event.pull_request.number || github.ref }} @@ -80,6 +74,6 @@ jobs: cspell:sql/src/tsql.txt cspell:terraform/dict/terraform.txt cspell:typescript/dict/typescript.txt - check_extra_dictionaries: "" + check_extra_dictionaries: '' only_check_changed_files: true - longest_word: "10" + longest_word: '10' diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 37a4b7421..7a5847c94 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,47 +1,33 @@ +--- name: Run Unit Tests - on: pull_request: - branches: - - main - + branches: [main] permissions: contents: read - jobs: test: name: Test with Python ${{ matrix.python-version }} - runs-on: ubuntu-latest - - if: github.repository == 'google-a2a/a2a-python' - + if: github.repository == 'a2aproject/a2a-python' strategy: matrix: - python-version: ["3.10", "3.13"] - + python-version: ['3.10', '3.13'] steps: - name: Checkout code uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - name: Install uv - run: | - curl -LsSf https://astral.sh/uv/install.sh | sh - + uses: astral-sh/setup-uv@v6 - name: Add uv to PATH run: | echo "$HOME/.cargo/bin" >> $GITHUB_PATH - - name: Install dependencies run: uv sync --dev - - name: Run tests and check coverage - run: uv run pytest --cov=a2a --cov-report=xml --cov-fail-under=85 - + run: uv run pytest --cov=a2a --cov-report=xml --cov-fail-under=90 - name: Show coverage summary in log run: uv run coverage report diff --git a/.github/workflows/update-a2a-types.yml b/.github/workflows/update-a2a-types.yml index 047bec0cf..a7f193c5d 100644 --- a/.github/workflows/update-a2a-types.yml +++ b/.github/workflows/update-a2a-types.yml @@ -1,94 +1,61 @@ +--- name: Update A2A Schema from Specification - on: repository_dispatch: types: [a2a_json_update] workflow_dispatch: - jobs: generate_and_pr: runs-on: ubuntu-latest permissions: contents: write pull-requests: write - steps: - name: Checkout code uses: actions/checkout@v4 - - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.10" - + python-version: '3.10' - name: Install uv - run: curl -LsSf https://astral.sh/uv/install.sh | sh - + uses: astral-sh/setup-uv@v6 - name: Configure uv shell run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH - - name: Install dependencies (datamodel-code-generator) run: uv sync - - name: Define output file variable id: vars run: | GENERATED_FILE="./src/a2a/types.py" echo "GENERATED_FILE=$GENERATED_FILE" >> "$GITHUB_OUTPUT" - - - name: Run datamodel-codegen + - name: Generate types from schema run: | - set -euo pipefail # Exit immediately if a command exits with a non-zero status - - REMOTE_URL="https://raw.githubusercontent.com/google-a2a/A2A/refs/heads/main/specification/json/a2a.json" - GENERATED_FILE="${{ steps.vars.outputs.GENERATED_FILE }}" - - echo "Running datamodel-codegen..." - uv run datamodel-codegen \ - --url "$REMOTE_URL" \ - --input-file-type jsonschema \ - --output "$GENERATED_FILE" \ - --target-python-version 3.10 \ - --output-model-type pydantic_v2.BaseModel \ - --disable-timestamp \ - --use-schema-description \ - --use-union-operator \ - --use-field-description \ - --use-default \ - --use-default-kwarg \ - --use-one-literal-as-default \ - --class-name A2A \ - --use-standard-collections \ - --use-subclass-enum - echo "Codegen finished." - + chmod +x scripts/generate_types.sh + ./scripts/generate_types.sh "${{ steps.vars.outputs.GENERATED_FILE }}" - name: Install Buf uses: bufbuild/buf-setup-action@v1 - - name: Run buf generate run: | - set -euo pipefail # Exit immediately if a command exits with a non-zero status - + set -euo pipefail # Exit immediately if a command exits with a non-zero status echo "Running buf generate..." buf generate uv run scripts/grpc_gen_post_processor.py echo "Buf generate finished." - - name: Create Pull Request with Updates uses: peter-evans/create-pull-request@v6 with: token: ${{ secrets.A2A_BOT_PAT }} - committer: "a2a-bot " - author: "a2a-bot " - commit-message: "feat: Update A2A types from specification 🤖" - title: "feat: Update A2A types from specification 🤖" + committer: a2a-bot + author: a2a-bot + commit-message: 'feat: Update A2A types from specification 🤖' + title: 'feat: Update A2A types from specification 🤖' body: | - This PR updates `src/a2a/types.py` based on the latest `specification/json/a2a.json` from [google-a2a/A2A](https://github.com/google-a2a/A2A/commit/${{ github.event.client_payload.sha }}). - branch: "auto-update-a2a-types-${{ github.event.client_payload.sha }}" + This PR updates `src/a2a/types.py` based on the latest `specification/json/a2a.json` from [a2aproject/A2A](https://github.com/a2aproject/A2A/commit/${{ github.event.client_payload.sha }}). + branch: auto-update-a2a-types-${{ github.event.client_payload.sha }} base: main labels: | automated dependencies - add-paths: | + add-paths: |- ${{ steps.vars.outputs.GENERATED_FILE }} src/a2a/grpc/ diff --git a/.github/linters/.jscpd.json b/.jscpd.json similarity index 100% rename from .github/linters/.jscpd.json rename to .jscpd.json diff --git a/.mypy.ini b/.mypy.ini new file mode 100644 index 000000000..2a5346867 --- /dev/null +++ b/.mypy.ini @@ -0,0 +1,6 @@ +[mypy] +exclude = src/a2a/grpc/ +disable_error_code = import-not-found,annotation-unchecked,import-untyped + +[mypy-examples.*] +follow_imports = skip diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..97dc9d718 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,82 @@ +--- +repos: + # =============================================== + # Pre-commit standard hooks (general file cleanup) + # =============================================== + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: trailing-whitespace # Removes extra whitespace at the end of lines + - id: end-of-file-fixer # Ensures files end with a newline + - id: check-yaml # Checks YAML file syntax (before formatting) + - id: check-toml # Checks TOML file syntax (before formatting) + - id: check-added-large-files # Prevents committing large files + args: [--maxkb=500] # Example: Limit to 500KB + - id: check-merge-conflict # Checks for merge conflict strings + - id: detect-private-key # Detects accidental private key commits + + # Formatter and linter for TOML files + - repo: https://github.com/ComPWA/taplo-pre-commit + rev: v0.9.3 + hooks: + - id: taplo-format + - id: taplo-lint + + # YAML files + - repo: https://github.com/lyz-code/yamlfix + rev: 1.17.0 + hooks: + - id: yamlfix + + # =============================================== + # Python Hooks + # =============================================== + # no_implicit_optional for ensuring explicit Optional types + - repo: https://github.com/hauntsaninja/no_implicit_optional + rev: '1.4' + hooks: + - id: no_implicit_optional + args: [--use-union-or] + + # Pyupgrade for upgrading Python syntax to newer versions + - repo: https://github.com/asottile/pyupgrade + rev: v3.20.0 + hooks: + - id: pyupgrade + args: [--py310-plus] # Target Python 3.10+ syntax, matching project's target + + # Autoflake for removing unused imports and variables + - repo: https://github.com/pycqa/autoflake + rev: v2.3.1 + hooks: + - id: autoflake + args: [--in-place, --remove-all-unused-imports] + + # Ruff for linting and formatting + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.12.0 + hooks: + - id: ruff + args: [--fix, --exit-zero] # Apply fixes, and exit with 0 even if files were modified + exclude: ^src/a2a/grpc/ + - id: ruff-format + exclude: ^src/a2a/grpc/ + + # Keep uv.lock in sync + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.7.13 + hooks: + - id: uv-lock + + # Commitzen for conventional commit messages + - repo: https://github.com/commitizen-tools/commitizen + rev: v4.8.3 + hooks: + - id: commitizen + stages: [commit-msg] + + # Gitleaks + - repo: https://github.com/gitleaks/gitleaks + rev: v8.27.2 + hooks: + - id: gitleaks diff --git a/.github/linters/.ruff.toml b/.ruff.toml similarity index 99% rename from .github/linters/.ruff.toml rename to .ruff.toml index 34dbfa2b9..70f8ded31 100644 --- a/.github/linters/.ruff.toml +++ b/.ruff.toml @@ -4,7 +4,7 @@ # # This file follows the standards in Google Python Style Guide # https://google.github.io/styleguide/pyguide.html -# +# line-length = 80 # Google Style Guide §3.2: 80 columns indent-width = 4 # Google Style Guide §3.4: 4 spaces @@ -83,6 +83,7 @@ exclude = [ "*/migrations/*", "noxfile.py", "src/a2a/grpc/**", + "tests/**", ] [lint.isort] @@ -101,7 +102,6 @@ lines-between-types = 1 [lint.pydocstyle] convention = "google" ignore-decorators = ["typing.overload", "abc.abstractmethod"] -ignore-var-parameters = true [lint.flake8-annotations] mypy-init-return = true diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..aec9d68e2 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "charliermarsh.ruff" + ], + "unwantedRecommendations": [] +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 376512389..6adb30d5c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,7 +12,12 @@ "PYTHONPATH": "${workspaceFolder}" }, "cwd": "${workspaceFolder}/examples/helloworld", - "args": ["--host", "localhost", "--port", "9999"] + "args": [ + "--host", + "localhost", + "--port", + "9999" + ] }, { "name": "Debug Currency Agent", @@ -25,7 +30,24 @@ "PYTHONPATH": "${workspaceFolder}" }, "cwd": "${workspaceFolder}/examples/langgraph", - "args": ["--host", "localhost", "--port", "10000"] + "args": [ + "--host", + "localhost", + "--port", + "10000" + ] + }, + { + "name": "Pytest All", + "type": "debugpy", + "request": "launch", + "module": "pytest", + "args": [ + "-v", + "-s" + ], + "console": "integratedTerminal", + "justMyCode": true } ] -} +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index ae460f385..0f968e252 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,7 @@ { - "python.testing.pytestArgs": ["tests"], + "python.testing.pytestArgs": [ + "tests" + ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "editor.formatOnSave": true, @@ -12,12 +14,10 @@ } }, "ruff.importStrategy": "fromEnvironment", - "ruff.lint.args": [ - "--config", - ".github/linters/.ruff.toml" - ], - "ruff.format.args": [ - "--config", - ".github/linters/.ruff.toml" + "files.insertFinalNewline": true, + "files.trimFinalNewlines": false, + "files.trimTrailingWhitespace": false, + "editor.rulers": [ + 80 ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index abbb829ac..f65529c20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,101 +1,131 @@ # Changelog -## [0.2.8](https://github.com/google-a2a/a2a-python/compare/v0.2.7...v0.2.8) (2025-06-12) +## [0.2.10](https://github.com/a2aproject/a2a-python/compare/v0.2.9...v0.2.10) (2025-06-30) + + +### ⚠ BREAKING CHANGES + +* Update to A2A Spec Version [0.2.5](https://github.com/a2aproject/A2A/releases/tag/v0.2.5) ([#197](https://github.com/a2aproject/a2a-python/issues/197)) + +### Features + +* Add `append` and `last_chunk` to `add_artifact` method on `TaskUpdater` ([#186](https://github.com/a2aproject/a2a-python/issues/186)) ([8c6560f](https://github.com/a2aproject/a2a-python/commit/8c6560fd403887fab9d774bfcc923a5f6f459364)) +* add a2a routes to existing app ([#188](https://github.com/a2aproject/a2a-python/issues/188)) ([32fecc7](https://github.com/a2aproject/a2a-python/commit/32fecc7194a61c2f5be0b8795d5dc17cdbab9040)) +* Add middleware to the client SDK ([#171](https://github.com/a2aproject/a2a-python/issues/171)) ([efaabd3](https://github.com/a2aproject/a2a-python/commit/efaabd3b71054142109b553c984da1d6e171db24)) +* Add more task state management methods to TaskUpdater ([#208](https://github.com/a2aproject/a2a-python/issues/208)) ([2b3bf6d](https://github.com/a2aproject/a2a-python/commit/2b3bf6d53ac37ed93fc1b1c012d59c19060be000)) +* raise error for tasks in terminal states ([#215](https://github.com/a2aproject/a2a-python/issues/215)) ([a0bf13b](https://github.com/a2aproject/a2a-python/commit/a0bf13b208c90b439b4be1952c685e702c4917a0)) + +### Bug Fixes + +* `consume_all` doesn't catch `asyncio.TimeoutError` in python 3.10 ([#216](https://github.com/a2aproject/a2a-python/issues/216)) ([39307f1](https://github.com/a2aproject/a2a-python/commit/39307f15a1bb70eb77aee2211da038f403571242)) +* Append metadata and context id when processing TaskStatusUpdateE… ([#238](https://github.com/a2aproject/a2a-python/issues/238)) ([e106020](https://github.com/a2aproject/a2a-python/commit/e10602033fdd4f4e6b61af717ffc242d772545b3)) +* Fix reference to `grpc.aio.ServicerContext` ([#237](https://github.com/a2aproject/a2a-python/issues/237)) ([0c1987b](https://github.com/a2aproject/a2a-python/commit/0c1987bb85f3e21089789ee260a0c62ac98b66a5)) +* Fixes Short Circuit clause for context ID ([#236](https://github.com/a2aproject/a2a-python/issues/236)) ([a5509e6](https://github.com/a2aproject/a2a-python/commit/a5509e6b37701dfb5c729ccc12531e644a12f8ae)) +* Resolve `APIKeySecurityScheme` parsing failed ([#226](https://github.com/a2aproject/a2a-python/issues/226)) ([aa63b98](https://github.com/a2aproject/a2a-python/commit/aa63b982edc2a07fd0df0b01fb9ad18d30b35a79)) +* send notifications on message not streaming ([#219](https://github.com/a2aproject/a2a-python/issues/219)) ([91539d6](https://github.com/a2aproject/a2a-python/commit/91539d69e5c757712c73a41ab95f1ec6656ef5cd)), closes [#218](https://github.com/a2aproject/a2a-python/issues/218) + +## [0.2.9](https://github.com/a2aproject/a2a-python/compare/v0.2.8...v0.2.9) (2025-06-24) + +### Bug Fixes + +* Set `protobuf==5.29.5` and `fastapi>=0.115.2` to prevent version conflicts ([#224](https://github.com/a2aproject/a2a-python/issues/224)) ([1412a85](https://github.com/a2aproject/a2a-python/commit/1412a855b4980d8373ed1cea38c326be74069633)) + +## [0.2.8](https://github.com/a2aproject/a2a-python/compare/v0.2.7...v0.2.8) (2025-06-12) ### Features -* Add HTTP Headers to ServerCallContext for Improved Handler Access ([#182](https://github.com/google-a2a/a2a-python/issues/182)) ([d5e5f5f](https://github.com/google-a2a/a2a-python/commit/d5e5f5f7e7a3cab7de13cff545a874fc58d85e46)) -* Update A2A types from specification 🤖 ([#191](https://github.com/google-a2a/a2a-python/issues/191)) ([174230b](https://github.com/google-a2a/a2a-python/commit/174230bf6dfb6bf287d233a101b98cc4c79cad19)) +* Add HTTP Headers to ServerCallContext for Improved Handler Access ([#182](https://github.com/a2aproject/a2a-python/issues/182)) ([d5e5f5f](https://github.com/a2aproject/a2a-python/commit/d5e5f5f7e7a3cab7de13cff545a874fc58d85e46)) +* Update A2A types from specification 🤖 ([#191](https://github.com/a2aproject/a2a-python/issues/191)) ([174230b](https://github.com/a2aproject/a2a-python/commit/174230bf6dfb6bf287d233a101b98cc4c79cad19)) ### Bug Fixes -* Add `protobuf==6.31.1` to dependencies ([#189](https://github.com/google-a2a/a2a-python/issues/189)) ([ae1c31c](https://github.com/google-a2a/a2a-python/commit/ae1c31c1da47f6965c02e0564dc7d3791dd03e2c)), closes [#185](https://github.com/google-a2a/a2a-python/issues/185) +* Add `protobuf==6.31.1` to dependencies ([#189](https://github.com/a2aproject/a2a-python/issues/189)) ([ae1c31c](https://github.com/a2aproject/a2a-python/commit/ae1c31c1da47f6965c02e0564dc7d3791dd03e2c)), closes [#185](https://github.com/a2aproject/a2a-python/issues/185) -## [0.2.7](https://github.com/google-a2a/a2a-python/compare/v0.2.6...v0.2.7) (2025-06-11) +## [0.2.7](https://github.com/a2aproject/a2a-python/compare/v0.2.6...v0.2.7) (2025-06-11) ### Features -* Update A2A types from specification 🤖 ([#179](https://github.com/google-a2a/a2a-python/issues/179)) ([3ef4240](https://github.com/google-a2a/a2a-python/commit/3ef42405f6096281fe90b1df399731bd009bde12)) +* Update A2A types from specification 🤖 ([#179](https://github.com/a2aproject/a2a-python/issues/179)) ([3ef4240](https://github.com/a2aproject/a2a-python/commit/3ef42405f6096281fe90b1df399731bd009bde12)) -## [0.2.6](https://github.com/google-a2a/a2a-python/compare/v0.2.5...v0.2.6) (2025-06-09) +## [0.2.6](https://github.com/a2aproject/a2a-python/compare/v0.2.5...v0.2.6) (2025-06-09) ### ⚠ BREAKING CHANGES -* Add FastAPI JSONRPC Application ([#104](https://github.com/google-a2a/a2a-python/issues/104)) +* Add FastAPI JSONRPC Application ([#104](https://github.com/a2aproject/a2a-python/issues/104)) ### Features -* Add FastAPI JSONRPC Application ([#104](https://github.com/google-a2a/a2a-python/issues/104)) ([0e66e1f](https://github.com/google-a2a/a2a-python/commit/0e66e1f81f98d7e2cf50b1c100e35d13ad7149dc)) -* Add gRPC server and client support ([#162](https://github.com/google-a2a/a2a-python/issues/162)) ([a981605](https://github.com/google-a2a/a2a-python/commit/a981605dbb32e87bd241b64bf2e9bb52831514d1)) -* add reject method to task_updater ([#147](https://github.com/google-a2a/a2a-python/issues/147)) ([2a6ef10](https://github.com/google-a2a/a2a-python/commit/2a6ef109f8b743f8eb53d29090cdec7df143b0b4)) -* Add timestamp to `TaskStatus` updates on `TaskUpdater` ([#140](https://github.com/google-a2a/a2a-python/issues/140)) ([0c9df12](https://github.com/google-a2a/a2a-python/commit/0c9df125b740b947b0e4001421256491b5f87920)) -* **spec:** Add an optional iconUrl field to the AgentCard 🤖 ([a1025f4](https://github.com/google-a2a/a2a-python/commit/a1025f406acd88e7485a5c0f4dd8a42488c41fa2)) +* Add FastAPI JSONRPC Application ([#104](https://github.com/a2aproject/a2a-python/issues/104)) ([0e66e1f](https://github.com/a2aproject/a2a-python/commit/0e66e1f81f98d7e2cf50b1c100e35d13ad7149dc)) +* Add gRPC server and client support ([#162](https://github.com/a2aproject/a2a-python/issues/162)) ([a981605](https://github.com/a2aproject/a2a-python/commit/a981605dbb32e87bd241b64bf2e9bb52831514d1)) +* add reject method to task_updater ([#147](https://github.com/a2aproject/a2a-python/issues/147)) ([2a6ef10](https://github.com/a2aproject/a2a-python/commit/2a6ef109f8b743f8eb53d29090cdec7df143b0b4)) +* Add timestamp to `TaskStatus` updates on `TaskUpdater` ([#140](https://github.com/a2aproject/a2a-python/issues/140)) ([0c9df12](https://github.com/a2aproject/a2a-python/commit/0c9df125b740b947b0e4001421256491b5f87920)) +* **spec:** Add an optional iconUrl field to the AgentCard 🤖 ([a1025f4](https://github.com/a2aproject/a2a-python/commit/a1025f406acd88e7485a5c0f4dd8a42488c41fa2)) ### Bug Fixes -* Correctly adapt starlette BaseUser to A2A User ([#133](https://github.com/google-a2a/a2a-python/issues/133)) ([88d45eb](https://github.com/google-a2a/a2a-python/commit/88d45ebd935724e6c3ad614bf503defae4de5d85)) -* Event consumer should stop on input_required ([#167](https://github.com/google-a2a/a2a-python/issues/167)) ([51c2d8a](https://github.com/google-a2a/a2a-python/commit/51c2d8addf9e89a86a6834e16deb9f4ac0e05cc3)) -* Fix Release Version ([#161](https://github.com/google-a2a/a2a-python/issues/161)) ([011d632](https://github.com/google-a2a/a2a-python/commit/011d632b27b201193813ce24cf25e28d1335d18e)) -* generate StrEnum types for enums ([#134](https://github.com/google-a2a/a2a-python/issues/134)) ([0c49dab](https://github.com/google-a2a/a2a-python/commit/0c49dabcdb9d62de49fda53d7ce5c691b8c1591c)) -* library should released as 0.2.6 ([d8187e8](https://github.com/google-a2a/a2a-python/commit/d8187e812d6ac01caedf61d4edaca522e583d7da)) -* remove error types from enqueable events ([#138](https://github.com/google-a2a/a2a-python/issues/138)) ([511992f](https://github.com/google-a2a/a2a-python/commit/511992fe585bd15e956921daeab4046dc4a50a0a)) -* **stream:** don't block event loop in EventQueue ([#151](https://github.com/google-a2a/a2a-python/issues/151)) ([efd9080](https://github.com/google-a2a/a2a-python/commit/efd9080b917c51d6e945572fd123b07f20974a64)) -* **task_updater:** fix potential duplicate artifact_id from default v… ([#156](https://github.com/google-a2a/a2a-python/issues/156)) ([1f0a769](https://github.com/google-a2a/a2a-python/commit/1f0a769c1027797b2f252e4c894352f9f78257ca)) +* Correctly adapt starlette BaseUser to A2A User ([#133](https://github.com/a2aproject/a2a-python/issues/133)) ([88d45eb](https://github.com/a2aproject/a2a-python/commit/88d45ebd935724e6c3ad614bf503defae4de5d85)) +* Event consumer should stop on input_required ([#167](https://github.com/a2aproject/a2a-python/issues/167)) ([51c2d8a](https://github.com/a2aproject/a2a-python/commit/51c2d8addf9e89a86a6834e16deb9f4ac0e05cc3)) +* Fix Release Version ([#161](https://github.com/a2aproject/a2a-python/issues/161)) ([011d632](https://github.com/a2aproject/a2a-python/commit/011d632b27b201193813ce24cf25e28d1335d18e)) +* generate StrEnum types for enums ([#134](https://github.com/a2aproject/a2a-python/issues/134)) ([0c49dab](https://github.com/a2aproject/a2a-python/commit/0c49dabcdb9d62de49fda53d7ce5c691b8c1591c)) +* library should released as 0.2.6 ([d8187e8](https://github.com/a2aproject/a2a-python/commit/d8187e812d6ac01caedf61d4edaca522e583d7da)) +* remove error types from enqueable events ([#138](https://github.com/a2aproject/a2a-python/issues/138)) ([511992f](https://github.com/a2aproject/a2a-python/commit/511992fe585bd15e956921daeab4046dc4a50a0a)) +* **stream:** don't block event loop in EventQueue ([#151](https://github.com/a2aproject/a2a-python/issues/151)) ([efd9080](https://github.com/a2aproject/a2a-python/commit/efd9080b917c51d6e945572fd123b07f20974a64)) +* **task_updater:** fix potential duplicate artifact_id from default v… ([#156](https://github.com/a2aproject/a2a-python/issues/156)) ([1f0a769](https://github.com/a2aproject/a2a-python/commit/1f0a769c1027797b2f252e4c894352f9f78257ca)) ### Documentation -* remove final and metadata fields from docstring ([#66](https://github.com/google-a2a/a2a-python/issues/66)) ([3c50ee1](https://github.com/google-a2a/a2a-python/commit/3c50ee1f64c103a543c8afb6d2ac3a11063b0f43)) -* Update Links to Documentation Site ([5e7d418](https://github.com/google-a2a/a2a-python/commit/5e7d4180f7ae0ebeb76d976caa5ef68b4277ce54)) +* remove final and metadata fields from docstring ([#66](https://github.com/a2aproject/a2a-python/issues/66)) ([3c50ee1](https://github.com/a2aproject/a2a-python/commit/3c50ee1f64c103a543c8afb6d2ac3a11063b0f43)) +* Update Links to Documentation Site ([5e7d418](https://github.com/a2aproject/a2a-python/commit/5e7d4180f7ae0ebeb76d976caa5ef68b4277ce54)) -## [0.2.5](https://github.com/google-a2a/a2a-python/compare/v0.2.4...v0.2.5) (2025-05-27) +## [0.2.5](https://github.com/a2aproject/a2a-python/compare/v0.2.4...v0.2.5) (2025-05-27) ### Features -* Add a User representation to ServerCallContext ([#116](https://github.com/google-a2a/a2a-python/issues/116)) ([2cc2a0d](https://github.com/google-a2a/a2a-python/commit/2cc2a0de93631aa162823d43fe488173ed8754dc)) -* Add functionality for extended agent card. ([#31](https://github.com/google-a2a/a2a-python/issues/31)) ([20f0826](https://github.com/google-a2a/a2a-python/commit/20f0826a2cb9b77b89b85189fd91e7cd62318a30)) -* Introduce a ServerCallContext ([#94](https://github.com/google-a2a/a2a-python/issues/94)) ([85b521d](https://github.com/google-a2a/a2a-python/commit/85b521d8a790dacb775ef764a66fbdd57b180da3)) +* Add a User representation to ServerCallContext ([#116](https://github.com/a2aproject/a2a-python/issues/116)) ([2cc2a0d](https://github.com/a2aproject/a2a-python/commit/2cc2a0de93631aa162823d43fe488173ed8754dc)) +* Add functionality for extended agent card. ([#31](https://github.com/a2aproject/a2a-python/issues/31)) ([20f0826](https://github.com/a2aproject/a2a-python/commit/20f0826a2cb9b77b89b85189fd91e7cd62318a30)) +* Introduce a ServerCallContext ([#94](https://github.com/a2aproject/a2a-python/issues/94)) ([85b521d](https://github.com/a2aproject/a2a-python/commit/85b521d8a790dacb775ef764a66fbdd57b180da3)) ### Bug Fixes -* fix hello world example for python 3.12 ([#98](https://github.com/google-a2a/a2a-python/issues/98)) ([536e4a1](https://github.com/google-a2a/a2a-python/commit/536e4a11f2f32332968a06e7d0bc4615e047a56c)) -* Remove unused dependencies and update py version ([#119](https://github.com/google-a2a/a2a-python/issues/119)) ([9f8bc02](https://github.com/google-a2a/a2a-python/commit/9f8bc023b45544942583818968f3d320e5ff1c3b)) -* Update hello world test client to match sdk behavior. Also down-level required python version ([#117](https://github.com/google-a2a/a2a-python/issues/117)) ([04c7c45](https://github.com/google-a2a/a2a-python/commit/04c7c452f5001d69524d94095d11971c1e857f75)) -* Update the google adk demos to use ADK v1.0 ([#95](https://github.com/google-a2a/a2a-python/issues/95)) ([c351656](https://github.com/google-a2a/a2a-python/commit/c351656a91c37338668b0cd0c4db5fedd152d743)) +* fix hello world example for python 3.12 ([#98](https://github.com/a2aproject/a2a-python/issues/98)) ([536e4a1](https://github.com/a2aproject/a2a-python/commit/536e4a11f2f32332968a06e7d0bc4615e047a56c)) +* Remove unused dependencies and update py version ([#119](https://github.com/a2aproject/a2a-python/issues/119)) ([9f8bc02](https://github.com/a2aproject/a2a-python/commit/9f8bc023b45544942583818968f3d320e5ff1c3b)) +* Update hello world test client to match sdk behavior. Also down-level required python version ([#117](https://github.com/a2aproject/a2a-python/issues/117)) ([04c7c45](https://github.com/a2aproject/a2a-python/commit/04c7c452f5001d69524d94095d11971c1e857f75)) +* Update the google adk demos to use ADK v1.0 ([#95](https://github.com/a2aproject/a2a-python/issues/95)) ([c351656](https://github.com/a2aproject/a2a-python/commit/c351656a91c37338668b0cd0c4db5fedd152d743)) ### Documentation -* Update README for Python 3.10+ support ([#90](https://github.com/google-a2a/a2a-python/issues/90)) ([e0db20f](https://github.com/google-a2a/a2a-python/commit/e0db20ffc20aa09ee68304cc7e2a67c32ecdd6a8)) +* Update README for Python 3.10+ support ([#90](https://github.com/a2aproject/a2a-python/issues/90)) ([e0db20f](https://github.com/a2aproject/a2a-python/commit/e0db20ffc20aa09ee68304cc7e2a67c32ecdd6a8)) -## [0.2.4](https://github.com/google-a2a/a2a-python/compare/v0.2.3...v0.2.4) (2025-05-22) +## [0.2.4](https://github.com/a2aproject/a2a-python/compare/v0.2.3...v0.2.4) (2025-05-22) ### Features -* Update to support python 3.10 ([#85](https://github.com/google-a2a/a2a-python/issues/85)) ([fd9c3b5](https://github.com/google-a2a/a2a-python/commit/fd9c3b5b0bbef509789a701171d95f690c84750b)) +* Update to support python 3.10 ([#85](https://github.com/a2aproject/a2a-python/issues/85)) ([fd9c3b5](https://github.com/a2aproject/a2a-python/commit/fd9c3b5b0bbef509789a701171d95f690c84750b)) ### Bug Fixes -* Throw exception for task_id mismatches ([#70](https://github.com/google-a2a/a2a-python/issues/70)) ([a9781b5](https://github.com/google-a2a/a2a-python/commit/a9781b589075280bfaaab5742d8b950916c9de74)) +* Throw exception for task_id mismatches ([#70](https://github.com/a2aproject/a2a-python/issues/70)) ([a9781b5](https://github.com/a2aproject/a2a-python/commit/a9781b589075280bfaaab5742d8b950916c9de74)) -## [0.2.3](https://github.com/google-a2a/a2a-python/compare/v0.2.2...v0.2.3) (2025-05-20) +## [0.2.3](https://github.com/a2aproject/a2a-python/compare/v0.2.2...v0.2.3) (2025-05-20) ### Features -* Add request context builder with referenceTasks ([#56](https://github.com/google-a2a/a2a-python/issues/56)) ([f20bfe7](https://github.com/google-a2a/a2a-python/commit/f20bfe74b8cc854c9c29720b2ea3859aff8f509e)) +* Add request context builder with referenceTasks ([#56](https://github.com/a2aproject/a2a-python/issues/56)) ([f20bfe7](https://github.com/a2aproject/a2a-python/commit/f20bfe74b8cc854c9c29720b2ea3859aff8f509e)) -## [0.2.2](https://github.com/google-a2a/a2a-python/compare/v0.2.1...v0.2.2) (2025-05-20) +## [0.2.2](https://github.com/a2aproject/a2a-python/compare/v0.2.1...v0.2.2) (2025-05-20) ### Documentation -* Write/Update Docstrings for Classes/Methods ([#59](https://github.com/google-a2a/a2a-python/issues/59)) ([9f773ef](https://github.com/google-a2a/a2a-python/commit/9f773eff4dddc4eec723d519d0050f21b9ccc042)) +* Write/Update Docstrings for Classes/Methods ([#59](https://github.com/a2aproject/a2a-python/issues/59)) ([9f773ef](https://github.com/a2aproject/a2a-python/commit/9f773eff4dddc4eec723d519d0050f21b9ccc042)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 289176c74..9a85cd9a8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,20 +4,6 @@ We'd love to accept your patches and contributions to this project. ## Before you begin -### Sign our Contributor License Agreement - -Contributions to this project must be accompanied by a -[Contributor License Agreement](https://cla.developers.google.com/about) (CLA). -You (or your employer) retain the copyright to your contribution; this simply -gives us permission to use and redistribute your contributions as part of the -project. - -If you or your current employer have already signed the Google CLA (even if it -was for a different project), you probably don't need to do it again. - -Visit to see your current agreements or to -sign a new one. - ### Review our community guidelines This project follows diff --git a/README.md b/README.md index 5f2c94010..24c14b71b 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,17 @@ # A2A Python SDK [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) -![PyPI - Version](https://img.shields.io/pypi/v/a2a-sdk) +[![PyPI version](https://img.shields.io/pypi/v/a2a-sdk)](https://pypi.org/project/a2a-sdk/) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/a2a-sdk) +[![PyPI - Downloads](https://img.shields.io/pypi/dw/a2a-sdk)](https://pypistats.org/packages/a2a-sdk)

- A2A Logo + A2A Logo

-

A Python library that helps run agentic applications as A2AServers following the Agent2Agent (A2A) Protocol.

+

A Python library that helps run agentic applications as A2AServers following the Agent2Agent (A2A) Protocol.

@@ -42,12 +43,12 @@ pip install a2a-sdk ## Examples -### [Helloworld Example](https://github.com/google-a2a/a2a-samples/tree/main/samples/python/agents/helloworld) +### [Helloworld Example](https://github.com/a2aproject/a2a-samples/tree/main/samples/python/agents/helloworld) 1. Run Remote Agent ```bash - git clone https://github.com/google-a2a/a2a-samples.git + git clone https://github.com/a2aproject/a2a-samples.git cd a2a-samples/samples/python/agents/helloworld uv run . ``` @@ -59,12 +60,14 @@ pip install a2a-sdk uv run test_client.py ``` -You can also find more Python samples [here](https://github.com/google-a2a/a2a-samples/tree/main/samples/python) and JavaScript samples [here](https://github.com/google-a2a/a2a-samples/tree/main/samples/js). +3. You can validate your agent using the agent inspector. Follow the instructions at the [a2a-inspector](https://github.com/a2aproject/a2a-inspector) repo. + +You can also find more Python samples [here](https://github.com/a2aproject/a2a-samples/tree/main/samples/python) and JavaScript samples [here](https://github.com/a2aproject/a2a-samples/tree/main/samples/js). ## License -This project is licensed under the terms of the [Apache 2.0 License](https://raw.githubusercontent.com/google-a2a/a2a-python/refs/heads/main/LICENSE). +This project is licensed under the terms of the [Apache 2.0 License](https://raw.githubusercontent.com/a2aproject/a2a-python/refs/heads/main/LICENSE). ## Contributing -See [CONTRIBUTING.md](https://github.com/google-a2a/a2a-python/blob/main/CONTRIBUTING.md) for contribution guidelines. +See [CONTRIBUTING.md](https://github.com/a2aproject/a2a-python/blob/main/CONTRIBUTING.md) for contribution guidelines. diff --git a/buf.gen.yaml b/buf.gen.yaml index 2adf1cd62..c70bf9e77 100644 --- a/buf.gen.yaml +++ b/buf.gen.yaml @@ -1,6 +1,7 @@ +--- version: v2 inputs: - - git_repo: https://github.com/google-a2a/A2A.git + - git_repo: https://github.com/a2aproject/A2A.git ref: main subdir: specification/grpc managed: @@ -19,7 +20,7 @@ managed: plugins: # Generate python protobuf related code # Generates *_pb2.py files, one for each .proto - - remote: buf.build/protocolbuffers/python + - remote: buf.build/protocolbuffers/python:v29.3 out: src/a2a/grpc # Generate python service code. # Generates *_pb2_grpc.py diff --git a/development.md b/development.md deleted file mode 100644 index c1ecf0295..000000000 --- a/development.md +++ /dev/null @@ -1,22 +0,0 @@ -# Development - -## Type generation from spec - -```bash -uv run datamodel-codegen \ - --url https://raw.githubusercontent.com/google-a2a/A2A/refs/heads/main/specification/json/a2a.json \ - --input-file-type jsonschema \ - --output ./src/a2a/types.py \ - --target-python-version 3.10 \ - --output-model-type pydantic_v2.BaseModel \ - --disable-timestamp \ - --use-schema-description \ - --use-union-operator \ - --use-field-description \ - --use-default \ - --use-default-kwarg \ - --use-one-literal-as-default \ - --class-name A2A \ - --use-standard-collections \ - --use-subclass-enum -``` diff --git a/noxfile.py b/noxfile.py index 60dd15efa..84e650673 100644 --- a/noxfile.py +++ b/noxfile.py @@ -144,14 +144,10 @@ def format(session) -> None: 'ruff', 'check', '--fix-only', - '--config', - '.github/linters/.ruff.toml', *lint_paths_py, ) session.run( 'ruff', 'format', - '--config', - '.github/linters/.ruff.toml', *lint_paths_py, ) diff --git a/pyproject.toml b/pyproject.toml index 19d9136d7..6fbe89230 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,19 +8,19 @@ authors = [{ name = "Google LLC", email = "googleapis-packages@google.com" }] requires-python = ">=3.10" keywords = ["A2A", "A2A SDK", "A2A Protocol", "Agent2Agent"] dependencies = [ - "fastapi>=0.115.12", - "httpx>=0.28.1", - "httpx-sse>=0.4.0", - "google-api-core>=1.26.0", - "opentelemetry-api>=1.33.0", - "opentelemetry-sdk>=1.33.0", - "pydantic>=2.11.3", - "sse-starlette>=2.3.3", - "starlette>=0.46.2", - "grpcio>=1.60", - "grpcio-tools>=1.60", - "grpcio_reflection>=1.7.0", - "protobuf==6.31.1", + "fastapi>=0.115.2", + "httpx>=0.28.1", + "httpx-sse>=0.4.0", + "google-api-core>=1.26.0", + "opentelemetry-api>=1.33.0", + "opentelemetry-sdk>=1.33.0", + "pydantic>=2.11.3", + "sse-starlette", + "starlette", + "grpcio>=1.60", + "grpcio-tools>=1.60", + "grpcio_reflection>=1.7.0", + "protobuf==5.29.5", ] classifiers = [ @@ -37,10 +37,10 @@ classifiers = [ ] [project.urls] -homepage = "https://google-a2a.github.io/A2A/" -repository = "https://github.com/google-a2a/a2a-python" -changelog = "https://github.com/google-a2a/a2a-python/blob/main/CHANGELOG.md" -documentation = "https://google-a2a.github.io/A2A/sdk/python/" +homepage = "https://a2aproject.github.io/A2A/" +repository = "https://github.com/a2aproject/a2a-python" +changelog = "https://github.com/a2aproject/a2a-python/blob/main/CHANGELOG.md" +documentation = "https://a2aproject.github.io/A2A/sdk/python/" [tool.hatch.build.targets.wheel] packages = ["src/a2a"] @@ -49,8 +49,13 @@ packages = ["src/a2a"] testpaths = ["tests"] python_files = "test_*.py" python_functions = "test_*" -addopts = "--cov=src --cov-config=.coveragerc --cov-report term --cov-report xml:coverage.xml --cov-branch" -asyncio_mode = "strict" +addopts = "-ra --strict-markers" +markers = [ + "asyncio: mark a test as a coroutine that should be run by pytest-asyncio", +] + +[tool.pytest-asyncio] +mode = "strict" [build-system] requires = ["hatchling", "uv-dynamic-versioning"] @@ -60,9 +65,7 @@ build-backend = "hatchling.build" source = "uv-dynamic-versioning" [tool.hatch.build.targets.sdist] -exclude = [ - "tests/", -] +exclude = ["tests/"] [tool.uv-dynamic-versioning] vcs = "git" @@ -70,14 +73,18 @@ style = "pep440" [dependency-groups] dev = [ - "datamodel-code-generator>=0.30.0", - "mypy>=1.15.0", - "pytest>=8.3.5", - "pytest-asyncio>=0.26.0", - "pytest-cov>=6.1.1", - "pytest-mock>=3.14.0", - "ruff>=0.11.6", - "uv-dynamic-versioning>=0.8.2", + "datamodel-code-generator>=0.30.0", + "mypy>=1.15.0", + "pytest>=8.3.5", + "pytest-asyncio>=0.26.0", + "pytest-cov>=6.1.1", + "pytest-mock>=3.14.0", + "respx>=0.20.2", + "ruff>=0.11.6", + "uv-dynamic-versioning>=0.8.2", + "types-protobuf", + "types-requests", + "pre-commit", ] [[tool.uv.index]] @@ -85,3 +92,17 @@ name = "testpypi" url = "https://test.pypi.org/simple/" publish-url = "https://test.pypi.org/legacy/" explicit = true + +[tool.pyright] +include = ["src"] +exclude = [ + "**/__pycache__", + "**/dist", + "**/build", + "**/node_modules", + "**/venv", + "**/.venv", + "src/a2a/grpc/", +] +reportMissingImports = "none" +reportMissingModuleSource = "none" diff --git a/scripts/generate_types.sh b/scripts/generate_types.sh new file mode 100644 index 000000000..0e53f70c5 --- /dev/null +++ b/scripts/generate_types.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status. +# Treat unset variables as an error. +set -euo pipefail + +# Check if an output file path was provided as an argument. +if [ -z "$1" ]; then + echo "Error: Output file path must be provided as the first argument." >&2 + exit 1 +fi + +REMOTE_URL="https://raw.githubusercontent.com/a2aproject/A2A/refs/heads/main/specification/json/a2a.json" +GENERATED_FILE="$1" + +echo "Running datamodel-codegen..." +echo " - Source URL: $REMOTE_URL" +echo " - Output File: $GENERATED_FILE" + +uv run datamodel-codegen \ + --url "$REMOTE_URL" \ + --input-file-type jsonschema \ + --output "$GENERATED_FILE" \ + --target-python-version 3.10 \ + --output-model-type pydantic_v2.BaseModel \ + --disable-timestamp \ + --use-schema-description \ + --use-union-operator \ + --use-field-description \ + --use-default \ + --use-default-kwarg \ + --use-one-literal-as-default \ + --class-name A2A \ + --use-standard-collections \ + --use-subclass-enum + +echo "Codegen finished successfully." diff --git a/scripts/grpc_gen_post_processor.py b/scripts/grpc_gen_post_processor.py index af2af78da..ce6b36121 100644 --- a/scripts/grpc_gen_post_processor.py +++ b/scripts/grpc_gen_post_processor.py @@ -1,5 +1,5 @@ -""" -Fix absolute imports in *_pb2_grpc.py files. +"""Fix absolute imports in *_pb2_grpc.py files. + Example: import a2a_pb2 as a2a__pb2 from . import a2a_pb2 as a2a__pb2 @@ -11,45 +11,45 @@ from pathlib import Path -def process_generated_code(src_folder: str = 'src/a2a/grpc'): +def process_generated_code(src_folder: str = 'src/a2a/grpc') -> None: """Post processor for the generated code.""" dir_path = Path(src_folder) print(dir_path) if not dir_path.is_dir(): - print('Source folder not found') - sys.exit(1) + print('Source folder not found') + sys.exit(1) grpc_pattern = '**/*_pb2_grpc.py' files = dir_path.glob(grpc_pattern) for file in files: - print(f'Processing {file}') - try: - with file.open('r', encoding='utf-8') as f: - src_content = f.read() - - # Change import a2a_pb2 as a2a__pb2 - import_pattern = r'^import (\w+_pb2) as (\w+__pb2)$' - # to from . import a2a_pb2 as a2a__pb2 - replacement_pattern = r'from . import \1 as \2' - - fixed_src_content = re.sub( - import_pattern, - replacement_pattern, - src_content, - flags=re.MULTILINE, - ) - - if fixed_src_content != src_content: - with file.open('w', encoding='utf-8') as f: - f.write(fixed_src_content) - print('Imports fixed') - else: - print('No changes needed') - - except Exception as e: - print(f'Error processing file {file}: {e}') - sys.exit(1) + print(f'Processing {file}') + try: + with file.open('r', encoding='utf-8') as f: + src_content = f.read() + + # Change import a2a_pb2 as a2a__pb2 + import_pattern = r'^import (\w+_pb2) as (\w+__pb2)$' + # to from . import a2a_pb2 as a2a__pb2 + replacement_pattern = r'from . import \1 as \2' + + fixed_src_content = re.sub( + import_pattern, + replacement_pattern, + src_content, + flags=re.MULTILINE, + ) + + if fixed_src_content != src_content: + with file.open('w', encoding='utf-8') as f: + f.write(fixed_src_content) + print('Imports fixed') + else: + print('No changes needed') + + except Exception as e: + print(f'Error processing file {file}: {e}') + sys.exit(1) if __name__ == '__main__': diff --git a/src/a2a/client/__init__.py b/src/a2a/client/__init__.py index e91c9eb7a..3d673b31e 100644 --- a/src/a2a/client/__init__.py +++ b/src/a2a/client/__init__.py @@ -1,5 +1,10 @@ """Client-side components for interacting with an A2A agent.""" +from a2a.client.auth import ( + AuthInterceptor, + CredentialService, + InMemoryContextCredentialStore, +) from a2a.client.client import A2ACardResolver, A2AClient from a2a.client.errors import ( A2AClientError, @@ -8,6 +13,7 @@ ) from a2a.client.grpc_client import A2AGrpcClient from a2a.client.helpers import create_text_message_object +from a2a.client.middleware import ClientCallContext, ClientCallInterceptor __all__ = [ @@ -17,5 +23,10 @@ 'A2AClientHTTPError', 'A2AClientJSONError', 'A2AGrpcClient', + 'AuthInterceptor', + 'ClientCallContext', + 'ClientCallInterceptor', + 'CredentialService', + 'InMemoryContextCredentialStore', 'create_text_message_object', ] diff --git a/src/a2a/client/auth/__init__.py b/src/a2a/client/auth/__init__.py new file mode 100644 index 000000000..8efe65fc0 --- /dev/null +++ b/src/a2a/client/auth/__init__.py @@ -0,0 +1,14 @@ +"""Client-side authentication components for the A2A Python SDK.""" + +from a2a.client.auth.credentials import ( + CredentialService, + InMemoryContextCredentialStore, +) +from a2a.client.auth.interceptor import AuthInterceptor + + +__all__ = [ + 'AuthInterceptor', + 'CredentialService', + 'InMemoryContextCredentialStore', +] diff --git a/src/a2a/client/auth/credentials.py b/src/a2a/client/auth/credentials.py new file mode 100644 index 000000000..11f323709 --- /dev/null +++ b/src/a2a/client/auth/credentials.py @@ -0,0 +1,55 @@ +from abc import ABC, abstractmethod + +from a2a.client.middleware import ClientCallContext + + +class CredentialService(ABC): + """An abstract service for retrieving credentials.""" + + @abstractmethod + async def get_credentials( + self, + security_scheme_name: str, + context: ClientCallContext | None, + ) -> str | None: + """ + Retrieves a credential (e.g., token) for a security scheme. + """ + + +class InMemoryContextCredentialStore(CredentialService): + """A simple in-memory store for session-keyed credentials. + + This class uses the 'sessionId' from the ClientCallContext state to + store and retrieve credentials... + """ + + def __init__(self) -> None: + self._store: dict[str, dict[str, str]] = {} + + async def get_credentials( + self, + security_scheme_name: str, + context: ClientCallContext | None, + ) -> str | None: + """Retrieves credentials from the in-memory store. + + Args: + security_scheme_name: The name of the security scheme. + context: The client call context. + + Returns: + The credential string, or None if not found. + """ + if not context or 'sessionId' not in context.state: + return None + session_id = context.state['sessionId'] + return self._store.get(session_id, {}).get(security_scheme_name) + + async def set_credentials( + self, session_id: str, security_scheme_name: str, credential: str + ) -> None: + """Method to populate the store.""" + if session_id not in self._store: + self._store[session_id] = {} + self._store[session_id][security_scheme_name] = credential diff --git a/src/a2a/client/auth/interceptor.py b/src/a2a/client/auth/interceptor.py new file mode 100644 index 000000000..a164f1355 --- /dev/null +++ b/src/a2a/client/auth/interceptor.py @@ -0,0 +1,93 @@ +import logging # noqa: I001 +from typing import Any + +from a2a.client.auth.credentials import CredentialService +from a2a.client.middleware import ClientCallContext, ClientCallInterceptor +from a2a.types import ( + AgentCard, + APIKeySecurityScheme, + HTTPAuthSecurityScheme, + In, + OAuth2SecurityScheme, + OpenIdConnectSecurityScheme, +) + +logger = logging.getLogger(__name__) + + +class AuthInterceptor(ClientCallInterceptor): + """An interceptor that automatically adds authentication details to requests. + + Based on the agent's security schemes. + """ + + def __init__(self, credential_service: CredentialService): + self._credential_service = credential_service + + async def intercept( + self, + method_name: str, + request_payload: dict[str, Any], + http_kwargs: dict[str, Any], + agent_card: AgentCard | None, + context: ClientCallContext | None, + ) -> tuple[dict[str, Any], dict[str, Any]]: + """Applies authentication headers to the request if credentials are available.""" + if ( + agent_card is None + or agent_card.security is None + or agent_card.securitySchemes is None + ): + return request_payload, http_kwargs + + for requirement in agent_card.security: + for scheme_name in requirement: + credential = await self._credential_service.get_credentials( + scheme_name, context + ) + if credential and scheme_name in agent_card.securitySchemes: + scheme_def_union = agent_card.securitySchemes.get( + scheme_name + ) + if not scheme_def_union: + continue + scheme_def = scheme_def_union.root + + headers = http_kwargs.get('headers', {}) + + match scheme_def: + # Case 1a: HTTP Bearer scheme with an if guard + case HTTPAuthSecurityScheme() if ( + scheme_def.scheme.lower() == 'bearer' + ): + headers['Authorization'] = f'Bearer {credential}' + logger.debug( + f"Added Bearer token for scheme '{scheme_name}' (type: {scheme_def.type})." + ) + http_kwargs['headers'] = headers + return request_payload, http_kwargs + + # Case 1b: OAuth2 and OIDC schemes, which are implicitly Bearer + case ( + OAuth2SecurityScheme() + | OpenIdConnectSecurityScheme() + ): + headers['Authorization'] = f'Bearer {credential}' + logger.debug( + f"Added Bearer token for scheme '{scheme_name}' (type: {scheme_def.type})." + ) + http_kwargs['headers'] = headers + return request_payload, http_kwargs + + # Case 2: API Key in Header + case APIKeySecurityScheme(in_=In.header): + headers[scheme_def.name] = credential + logger.debug( + f"Added API Key Header for scheme '{scheme_name}'." + ) + http_kwargs['headers'] = headers + return request_payload, http_kwargs + + # Note: Other cases like API keys in query/cookie are not handled and will be skipped. + + return request_payload, http_kwargs diff --git a/src/a2a/client/client.py b/src/a2a/client/client.py index 5bcc36f1d..e29ef8a7a 100644 --- a/src/a2a/client/client.py +++ b/src/a2a/client/client.py @@ -11,6 +11,7 @@ from pydantic import ValidationError from a2a.client.errors import A2AClientHTTPError, A2AClientJSONError +from a2a.client.middleware import ClientCallContext, ClientCallInterceptor from a2a.types import ( AgentCard, CancelTaskRequest, @@ -129,6 +130,7 @@ def __init__( httpx_client: httpx.AsyncClient, agent_card: AgentCard | None = None, url: str | None = None, + interceptors: list[ClientCallInterceptor] | None = None, ): """Initializes the A2AClient. @@ -138,6 +140,7 @@ def __init__( httpx_client: An async HTTP client instance (e.g., httpx.AsyncClient). agent_card: The agent card object. If provided, `url` is taken from `agent_card.url`. url: The direct URL to the agent's A2A RPC endpoint. Required if `agent_card` is None. + interceptors: An optional list of client call interceptors to apply to requests. Raises: ValueError: If neither `agent_card` nor `url` is provided. @@ -150,6 +153,32 @@ def __init__( raise ValueError('Must provide either agent_card or url') self.httpx_client = httpx_client + self.agent_card = agent_card + self.interceptors = interceptors or [] + + async def _apply_interceptors( + self, + method_name: str, + request_payload: dict[str, Any], + http_kwargs: dict[str, Any] | None, + context: ClientCallContext | None, + ) -> tuple[dict[str, Any], dict[str, Any]]: + """Applies all registered interceptors to the request.""" + final_http_kwargs = http_kwargs or {} + final_request_payload = request_payload + + for interceptor in self.interceptors: + ( + final_request_payload, + final_http_kwargs, + ) = await interceptor.intercept( + method_name, + final_request_payload, + final_http_kwargs, + self.agent_card, + context, + ) + return final_request_payload, final_http_kwargs @staticmethod async def get_client_from_agent_card_url( @@ -191,6 +220,7 @@ async def send_message( request: SendMessageRequest, *, http_kwargs: dict[str, Any] | None = None, + context: ClientCallContext | None = None, ) -> SendMessageResponse: """Sends a non-streaming message request to the agent. @@ -198,6 +228,7 @@ async def send_message( request: The `SendMessageRequest` object containing the message and configuration. http_kwargs: Optional dictionary of keyword arguments to pass to the underlying httpx.post request. + context: The client call context. Returns: A `SendMessageResponse` object containing the agent's response (Task or Message) or an error. @@ -209,18 +240,22 @@ async def send_message( if not request.id: request.id = str(uuid4()) - return SendMessageResponse( - **await self._send_request( - request.model_dump(mode='json', exclude_none=True), - http_kwargs, - ) + # Apply interceptors before sending + payload, modified_kwargs = await self._apply_interceptors( + 'message/send', + request.model_dump(mode='json', exclude_none=True), + http_kwargs, + context, ) + response_data = await self._send_request(payload, modified_kwargs) + return SendMessageResponse.model_validate(response_data) async def send_message_streaming( self, request: SendStreamingMessageRequest, *, http_kwargs: dict[str, Any] | None = None, + context: ClientCallContext | None = None, ) -> AsyncGenerator[SendStreamingMessageResponse]: """Sends a streaming message request to the agent and yields responses as they arrive. @@ -230,6 +265,7 @@ async def send_message_streaming( request: The `SendStreamingMessageRequest` object containing the message and configuration. http_kwargs: Optional dictionary of keyword arguments to pass to the underlying httpx.post request. A default `timeout=None` is set but can be overridden. + context: The client call context. Yields: `SendStreamingMessageResponse` objects as they are received in the SSE stream. @@ -242,22 +278,28 @@ async def send_message_streaming( if not request.id: request.id = str(uuid4()) - # Default to no timeout for streaming, can be overridden by http_kwargs - http_kwargs_with_timeout: dict[str, Any] = { - 'timeout': None, - **(http_kwargs or {}), - } + # Apply interceptors before sending + payload, modified_kwargs = await self._apply_interceptors( + 'message/stream', + request.model_dump(mode='json', exclude_none=True), + http_kwargs, + context, + ) + + modified_kwargs.setdefault('timeout', None) async with aconnect_sse( self.httpx_client, 'POST', self.url, - json=request.model_dump(mode='json', exclude_none=True), - **http_kwargs_with_timeout, + json=payload, + **modified_kwargs, ) as event_source: try: async for sse in event_source.aiter_sse(): - yield SendStreamingMessageResponse(**json.loads(sse.data)) + yield SendStreamingMessageResponse.model_validate( + json.loads(sse.data) + ) except SSEError as e: raise A2AClientHTTPError( 400, @@ -309,6 +351,7 @@ async def get_task( request: GetTaskRequest, *, http_kwargs: dict[str, Any] | None = None, + context: ClientCallContext | None = None, ) -> GetTaskResponse: """Retrieves the current state and history of a specific task. @@ -316,6 +359,7 @@ async def get_task( request: The `GetTaskRequest` object specifying the task ID and history length. http_kwargs: Optional dictionary of keyword arguments to pass to the underlying httpx.post request. + context: The client call context. Returns: A `GetTaskResponse` object containing the Task or an error. @@ -327,18 +371,22 @@ async def get_task( if not request.id: request.id = str(uuid4()) - return GetTaskResponse( - **await self._send_request( - request.model_dump(mode='json', exclude_none=True), - http_kwargs, - ) + # Apply interceptors before sending + payload, modified_kwargs = await self._apply_interceptors( + 'tasks/get', + request.model_dump(mode='json', exclude_none=True), + http_kwargs, + context, ) + response_data = await self._send_request(payload, modified_kwargs) + return GetTaskResponse.model_validate(response_data) async def cancel_task( self, request: CancelTaskRequest, *, http_kwargs: dict[str, Any] | None = None, + context: ClientCallContext | None = None, ) -> CancelTaskResponse: """Requests the agent to cancel a specific task. @@ -346,6 +394,7 @@ async def cancel_task( request: The `CancelTaskRequest` object specifying the task ID. http_kwargs: Optional dictionary of keyword arguments to pass to the underlying httpx.post request. + context: The client call context. Returns: A `CancelTaskResponse` object containing the updated Task with canceled status or an error. @@ -357,18 +406,22 @@ async def cancel_task( if not request.id: request.id = str(uuid4()) - return CancelTaskResponse( - **await self._send_request( - request.model_dump(mode='json', exclude_none=True), - http_kwargs, - ) + # Apply interceptors before sending + payload, modified_kwargs = await self._apply_interceptors( + 'tasks/cancel', + request.model_dump(mode='json', exclude_none=True), + http_kwargs, + context, ) + response_data = await self._send_request(payload, modified_kwargs) + return CancelTaskResponse.model_validate(response_data) async def set_task_callback( self, request: SetTaskPushNotificationConfigRequest, *, http_kwargs: dict[str, Any] | None = None, + context: ClientCallContext | None = None, ) -> SetTaskPushNotificationConfigResponse: """Sets or updates the push notification configuration for a specific task. @@ -376,6 +429,7 @@ async def set_task_callback( request: The `SetTaskPushNotificationConfigRequest` object specifying the task ID and configuration. http_kwargs: Optional dictionary of keyword arguments to pass to the underlying httpx.post request. + context: The client call context. Returns: A `SetTaskPushNotificationConfigResponse` object containing the confirmation or an error. @@ -387,11 +441,16 @@ async def set_task_callback( if not request.id: request.id = str(uuid4()) - return SetTaskPushNotificationConfigResponse( - **await self._send_request( - request.model_dump(mode='json', exclude_none=True), - http_kwargs, - ) + # Apply interceptors before sending + payload, modified_kwargs = await self._apply_interceptors( + 'tasks/pushNotificationConfig/set', + request.model_dump(mode='json', exclude_none=True), + http_kwargs, + context, + ) + response_data = await self._send_request(payload, modified_kwargs) + return SetTaskPushNotificationConfigResponse.model_validate( + response_data ) async def get_task_callback( @@ -399,6 +458,7 @@ async def get_task_callback( request: GetTaskPushNotificationConfigRequest, *, http_kwargs: dict[str, Any] | None = None, + context: ClientCallContext | None = None, ) -> GetTaskPushNotificationConfigResponse: """Retrieves the push notification configuration for a specific task. @@ -406,6 +466,7 @@ async def get_task_callback( request: The `GetTaskPushNotificationConfigRequest` object specifying the task ID. http_kwargs: Optional dictionary of keyword arguments to pass to the underlying httpx.post request. + context: The client call context. Returns: A `GetTaskPushNotificationConfigResponse` object containing the configuration or an error. @@ -417,9 +478,14 @@ async def get_task_callback( if not request.id: request.id = str(uuid4()) - return GetTaskPushNotificationConfigResponse( - **await self._send_request( - request.model_dump(mode='json', exclude_none=True), - http_kwargs, - ) + # Apply interceptors before sending + payload, modified_kwargs = await self._apply_interceptors( + 'tasks/pushNotificationConfig/get', + request.model_dump(mode='json', exclude_none=True), + http_kwargs, + context, + ) + response_data = await self._send_request(payload, modified_kwargs) + return GetTaskPushNotificationConfigResponse.model_validate( + response_data ) diff --git a/src/a2a/client/grpc_client.py b/src/a2a/client/grpc_client.py index d9f14b7f7..5fc7cc99c 100644 --- a/src/a2a/client/grpc_client.py +++ b/src/a2a/client/grpc_client.py @@ -98,7 +98,7 @@ async def send_message_streaming( ) while True: response = await stream.read() - if response == grpc.aio.EOF: + if response == grpc.aio.EOF: # pyright: ignore [reportAttributeAccessIssue] break if response.HasField('msg'): yield proto_utils.FromProto.message(response.msg) @@ -159,8 +159,8 @@ async def set_task_callback( Returns: A `TaskPushNotificationConfig` object containing the config. """ - config = await self.stub.CreateTaskPushNotification( - a2a_pb2.CreateTaskPushNotificationRequest( + config = await self.stub.CreateTaskPushNotificationConfig( + a2a_pb2.CreateTaskPushNotificationConfigRequest( parent='', config_id='', config=proto_utils.ToProto.task_push_notification_config( @@ -182,8 +182,8 @@ async def get_task_callback( Returns: A `TaskPushNotificationConfig` object containing the configuration. """ - config = await self.stub.GetTaskPushNotification( - a2a_pb2.GetTaskPushNotificationRequest( + config = await self.stub.GetTaskPushNotificationConfig( + a2a_pb2.GetTaskPushNotificationConfigRequest( name=f'tasks/{request.id}/pushNotification/undefined', ) ) diff --git a/src/a2a/client/middleware.py b/src/a2a/client/middleware.py new file mode 100644 index 000000000..73ada982f --- /dev/null +++ b/src/a2a/client/middleware.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from collections.abc import MutableMapping # noqa: TC003 +from typing import TYPE_CHECKING, Any + +from pydantic import BaseModel, Field + + +if TYPE_CHECKING: + from a2a.types import AgentCard + + +class ClientCallContext(BaseModel): + """A context passed with each client call, allowing for call-specific. + + configuration and data passing. Such as authentication details or + request deadlines. + """ + + state: MutableMapping[str, Any] = Field(default_factory=dict) + + +class ClientCallInterceptor(ABC): + """An abstract base class for client-side call interceptors. + + Interceptors can inspect and modify requests before they are sent, + which is ideal for concerns like authentication, logging, or tracing. + """ + + @abstractmethod + async def intercept( + self, + method_name: str, + request_payload: dict[str, Any], + http_kwargs: dict[str, Any], + agent_card: AgentCard | None, + context: ClientCallContext | None, + ) -> tuple[dict[str, Any], dict[str, Any]]: + """ + Intercepts a client call before the request is sent. + + Args: + method_name: The name of the RPC method (e.g., 'message/send'). + request_payload: The JSON RPC request payload dictionary. + http_kwargs: The keyword arguments for the httpx request. + agent_card: The AgentCard associated with the client. + context: The ClientCallContext for this specific call. + + Returns: + A tuple containing the (potentially modified) request_payload + and http_kwargs. + """ diff --git a/src/a2a/grpc/a2a_pb2.py b/src/a2a/grpc/a2a_pb2.py index 629b8472b..710307149 100644 --- a/src/a2a/grpc/a2a_pb2.py +++ b/src/a2a/grpc/a2a_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: a2a.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 5.29.3 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -11,9 +11,9 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, + 5, + 29, + 3, '', 'a2a.proto' ) @@ -25,11 +25,12 @@ from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 from google.api import client_pb2 as google_dot_api_dot_client__pb2 from google.api import field_behavior_pb2 as google_dot_api_dot_field__behavior__pb2 +from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ta2a.proto\x12\x06\x61\x32\x61.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xde\x01\n\x18SendMessageConfiguration\x12\x32\n\x15\x61\x63\x63\x65pted_output_modes\x18\x01 \x03(\tR\x13\x61\x63\x63\x65ptedOutputModes\x12K\n\x11push_notification\x18\x02 \x01(\x0b\x32\x1e.a2a.v1.PushNotificationConfigR\x10pushNotification\x12%\n\x0ehistory_length\x18\x03 \x01(\x05R\rhistoryLength\x12\x1a\n\x08\x62locking\x18\x04 \x01(\x08R\x08\x62locking\"\xf1\x01\n\x04Task\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1d\n\ncontext_id\x18\x02 \x01(\tR\tcontextId\x12*\n\x06status\x18\x03 \x01(\x0b\x32\x12.a2a.v1.TaskStatusR\x06status\x12.\n\tartifacts\x18\x04 \x03(\x0b\x32\x10.a2a.v1.ArtifactR\tartifacts\x12)\n\x07history\x18\x05 \x03(\x0b\x32\x0f.a2a.v1.MessageR\x07history\x12\x33\n\x08metadata\x18\x06 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\"\x98\x01\n\nTaskStatus\x12\'\n\x05state\x18\x01 \x01(\x0e\x32\x11.a2a.v1.TaskStateR\x05state\x12\'\n\x06update\x18\x02 \x01(\x0b\x32\x0f.a2a.v1.MessageR\x06update\x12\x38\n\ttimestamp\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\ttimestamp\"t\n\x04Part\x12\x14\n\x04text\x18\x01 \x01(\tH\x00R\x04text\x12&\n\x04\x66ile\x18\x02 \x01(\x0b\x32\x10.a2a.v1.FilePartH\x00R\x04\x66ile\x12&\n\x04\x64\x61ta\x18\x03 \x01(\x0b\x32\x10.a2a.v1.DataPartH\x00R\x04\x64\x61taB\x06\n\x04part\"\x7f\n\x08\x46ilePart\x12$\n\rfile_with_uri\x18\x01 \x01(\tH\x00R\x0b\x66ileWithUri\x12(\n\x0f\x66ile_with_bytes\x18\x02 \x01(\x0cH\x00R\rfileWithBytes\x12\x1b\n\tmime_type\x18\x03 \x01(\tR\x08mimeTypeB\x06\n\x04\x66ile\"7\n\x08\x44\x61taPart\x12+\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32\x17.google.protobuf.StructR\x04\x64\x61ta\"\xff\x01\n\x07Message\x12\x1d\n\nmessage_id\x18\x01 \x01(\tR\tmessageId\x12\x1d\n\ncontext_id\x18\x02 \x01(\tR\tcontextId\x12\x17\n\x07task_id\x18\x03 \x01(\tR\x06taskId\x12 \n\x04role\x18\x04 \x01(\x0e\x32\x0c.a2a.v1.RoleR\x04role\x12&\n\x07\x63ontent\x18\x05 \x03(\x0b\x32\x0c.a2a.v1.PartR\x07\x63ontent\x12\x33\n\x08metadata\x18\x06 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\x12\x1e\n\nextensions\x18\x07 \x03(\tR\nextensions\"\xda\x01\n\x08\x41rtifact\x12\x1f\n\x0b\x61rtifact_id\x18\x01 \x01(\tR\nartifactId\x12\x12\n\x04name\x18\x03 \x01(\tR\x04name\x12 \n\x0b\x64\x65scription\x18\x04 \x01(\tR\x0b\x64\x65scription\x12\"\n\x05parts\x18\x05 \x03(\x0b\x32\x0c.a2a.v1.PartR\x05parts\x12\x33\n\x08metadata\x18\x06 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\x12\x1e\n\nextensions\x18\x07 \x03(\tR\nextensions\"\xc6\x01\n\x15TaskStatusUpdateEvent\x12\x17\n\x07task_id\x18\x01 \x01(\tR\x06taskId\x12\x1d\n\ncontext_id\x18\x02 \x01(\tR\tcontextId\x12*\n\x06status\x18\x03 \x01(\x0b\x32\x12.a2a.v1.TaskStatusR\x06status\x12\x14\n\x05\x66inal\x18\x04 \x01(\x08R\x05\x66inal\x12\x33\n\x08metadata\x18\x05 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\"\xeb\x01\n\x17TaskArtifactUpdateEvent\x12\x17\n\x07task_id\x18\x01 \x01(\tR\x06taskId\x12\x1d\n\ncontext_id\x18\x02 \x01(\tR\tcontextId\x12,\n\x08\x61rtifact\x18\x03 \x01(\x0b\x32\x10.a2a.v1.ArtifactR\x08\x61rtifact\x12\x16\n\x06\x61ppend\x18\x04 \x01(\x08R\x06\x61ppend\x12\x1d\n\nlast_chunk\x18\x05 \x01(\x08R\tlastChunk\x12\x33\n\x08metadata\x18\x06 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\"\x94\x01\n\x16PushNotificationConfig\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x10\n\x03url\x18\x02 \x01(\tR\x03url\x12\x14\n\x05token\x18\x03 \x01(\tR\x05token\x12\x42\n\x0e\x61uthentication\x18\x04 \x01(\x0b\x32\x1a.a2a.v1.AuthenticationInfoR\x0e\x61uthentication\"P\n\x12\x41uthenticationInfo\x12\x18\n\x07schemes\x18\x01 \x03(\tR\x07schemes\x12 \n\x0b\x63redentials\x18\x02 \x01(\tR\x0b\x63redentials\"\xc8\x05\n\tAgentCard\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12 \n\x0b\x64\x65scription\x18\x02 \x01(\tR\x0b\x64\x65scription\x12\x10\n\x03url\x18\x03 \x01(\tR\x03url\x12\x31\n\x08provider\x18\x04 \x01(\x0b\x32\x15.a2a.v1.AgentProviderR\x08provider\x12\x18\n\x07version\x18\x05 \x01(\tR\x07version\x12+\n\x11\x64ocumentation_url\x18\x06 \x01(\tR\x10\x64ocumentationUrl\x12=\n\x0c\x63\x61pabilities\x18\x07 \x01(\x0b\x32\x19.a2a.v1.AgentCapabilitiesR\x0c\x63\x61pabilities\x12Q\n\x10security_schemes\x18\x08 \x03(\x0b\x32&.a2a.v1.AgentCard.SecuritySchemesEntryR\x0fsecuritySchemes\x12,\n\x08security\x18\t \x03(\x0b\x32\x10.a2a.v1.SecurityR\x08security\x12.\n\x13\x64\x65\x66\x61ult_input_modes\x18\n \x03(\tR\x11\x64\x65\x66\x61ultInputModes\x12\x30\n\x14\x64\x65\x66\x61ult_output_modes\x18\x0b \x03(\tR\x12\x64\x65\x66\x61ultOutputModes\x12*\n\x06skills\x18\x0c \x03(\x0b\x32\x12.a2a.v1.AgentSkillR\x06skills\x12O\n$supports_authenticated_extended_card\x18\r \x01(\x08R!supportsAuthenticatedExtendedCard\x1aZ\n\x14SecuritySchemesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12,\n\x05value\x18\x02 \x01(\x0b\x32\x16.a2a.v1.SecuritySchemeR\x05value:\x02\x38\x01\"E\n\rAgentProvider\x12\x10\n\x03url\x18\x01 \x01(\tR\x03url\x12\"\n\x0corganization\x18\x02 \x01(\tR\x0corganization\"\x98\x01\n\x11\x41gentCapabilities\x12\x1c\n\tstreaming\x18\x01 \x01(\x08R\tstreaming\x12-\n\x12push_notifications\x18\x02 \x01(\x08R\x11pushNotifications\x12\x36\n\nextensions\x18\x03 \x03(\x0b\x32\x16.a2a.v1.AgentExtensionR\nextensions\"\x91\x01\n\x0e\x41gentExtension\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12 \n\x0b\x64\x65scription\x18\x02 \x01(\tR\x0b\x64\x65scription\x12\x1a\n\x08required\x18\x03 \x01(\x08R\x08required\x12/\n\x06params\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructR\x06params\"\xc6\x01\n\nAgentSkill\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12 \n\x0b\x64\x65scription\x18\x03 \x01(\tR\x0b\x64\x65scription\x12\x12\n\x04tags\x18\x04 \x03(\tR\x04tags\x12\x1a\n\x08\x65xamples\x18\x05 \x03(\tR\x08\x65xamples\x12\x1f\n\x0binput_modes\x18\x06 \x03(\tR\ninputModes\x12!\n\x0coutput_modes\x18\x07 \x03(\tR\x0boutputModes\"\x8a\x01\n\x1aTaskPushNotificationConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12X\n\x18push_notification_config\x18\x02 \x01(\x0b\x32\x1e.a2a.v1.PushNotificationConfigR\x16pushNotificationConfig\" \n\nStringList\x12\x12\n\x04list\x18\x01 \x03(\tR\x04list\"\x93\x01\n\x08Security\x12\x37\n\x07schemes\x18\x01 \x03(\x0b\x32\x1d.a2a.v1.Security.SchemesEntryR\x07schemes\x1aN\n\x0cSchemesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12(\n\x05value\x18\x02 \x01(\x0b\x32\x12.a2a.v1.StringListR\x05value:\x02\x38\x01\"\x91\x03\n\x0eSecurityScheme\x12U\n\x17\x61pi_key_security_scheme\x18\x01 \x01(\x0b\x32\x1c.a2a.v1.APIKeySecuritySchemeH\x00R\x14\x61piKeySecurityScheme\x12[\n\x19http_auth_security_scheme\x18\x02 \x01(\x0b\x32\x1e.a2a.v1.HTTPAuthSecuritySchemeH\x00R\x16httpAuthSecurityScheme\x12T\n\x16oauth2_security_scheme\x18\x03 \x01(\x0b\x32\x1c.a2a.v1.OAuth2SecuritySchemeH\x00R\x14oauth2SecurityScheme\x12k\n\x1fopen_id_connect_security_scheme\x18\x04 \x01(\x0b\x32#.a2a.v1.OpenIdConnectSecuritySchemeH\x00R\x1bopenIdConnectSecuritySchemeB\x08\n\x06scheme\"h\n\x14\x41PIKeySecurityScheme\x12 \n\x0b\x64\x65scription\x18\x01 \x01(\tR\x0b\x64\x65scription\x12\x1a\n\x08location\x18\x02 \x01(\tR\x08location\x12\x12\n\x04name\x18\x03 \x01(\tR\x04name\"w\n\x16HTTPAuthSecurityScheme\x12 \n\x0b\x64\x65scription\x18\x01 \x01(\tR\x0b\x64\x65scription\x12\x16\n\x06scheme\x18\x02 \x01(\tR\x06scheme\x12#\n\rbearer_format\x18\x03 \x01(\tR\x0c\x62\x65\x61rerFormat\"b\n\x14OAuth2SecurityScheme\x12 \n\x0b\x64\x65scription\x18\x01 \x01(\tR\x0b\x64\x65scription\x12(\n\x05\x66lows\x18\x02 \x01(\x0b\x32\x12.a2a.v1.OAuthFlowsR\x05\x66lows\"n\n\x1bOpenIdConnectSecurityScheme\x12 \n\x0b\x64\x65scription\x18\x01 \x01(\tR\x0b\x64\x65scription\x12-\n\x13open_id_connect_url\x18\x02 \x01(\tR\x10openIdConnectUrl\"\xb0\x02\n\nOAuthFlows\x12S\n\x12\x61uthorization_code\x18\x01 \x01(\x0b\x32\".a2a.v1.AuthorizationCodeOAuthFlowH\x00R\x11\x61uthorizationCode\x12S\n\x12\x63lient_credentials\x18\x02 \x01(\x0b\x32\".a2a.v1.ClientCredentialsOAuthFlowH\x00R\x11\x63lientCredentials\x12\x37\n\x08implicit\x18\x03 \x01(\x0b\x32\x19.a2a.v1.ImplicitOAuthFlowH\x00R\x08implicit\x12\x37\n\x08password\x18\x04 \x01(\x0b\x32\x19.a2a.v1.PasswordOAuthFlowH\x00R\x08passwordB\x06\n\x04\x66low\"\x8a\x02\n\x1a\x41uthorizationCodeOAuthFlow\x12+\n\x11\x61uthorization_url\x18\x01 \x01(\tR\x10\x61uthorizationUrl\x12\x1b\n\ttoken_url\x18\x02 \x01(\tR\x08tokenUrl\x12\x1f\n\x0brefresh_url\x18\x03 \x01(\tR\nrefreshUrl\x12\x46\n\x06scopes\x18\x04 \x03(\x0b\x32..a2a.v1.AuthorizationCodeOAuthFlow.ScopesEntryR\x06scopes\x1a\x39\n\x0bScopesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xdd\x01\n\x1a\x43lientCredentialsOAuthFlow\x12\x1b\n\ttoken_url\x18\x01 \x01(\tR\x08tokenUrl\x12\x1f\n\x0brefresh_url\x18\x02 \x01(\tR\nrefreshUrl\x12\x46\n\x06scopes\x18\x03 \x03(\x0b\x32..a2a.v1.ClientCredentialsOAuthFlow.ScopesEntryR\x06scopes\x1a\x39\n\x0bScopesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xdb\x01\n\x11ImplicitOAuthFlow\x12+\n\x11\x61uthorization_url\x18\x01 \x01(\tR\x10\x61uthorizationUrl\x12\x1f\n\x0brefresh_url\x18\x02 \x01(\tR\nrefreshUrl\x12=\n\x06scopes\x18\x03 \x03(\x0b\x32%.a2a.v1.ImplicitOAuthFlow.ScopesEntryR\x06scopes\x1a\x39\n\x0bScopesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xcb\x01\n\x11PasswordOAuthFlow\x12\x1b\n\ttoken_url\x18\x01 \x01(\tR\x08tokenUrl\x12\x1f\n\x0brefresh_url\x18\x02 \x01(\tR\nrefreshUrl\x12=\n\x06scopes\x18\x03 \x03(\x0b\x32%.a2a.v1.PasswordOAuthFlow.ScopesEntryR\x06scopes\x1a\x39\n\x0bScopesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xc1\x01\n\x12SendMessageRequest\x12.\n\x07request\x18\x01 \x01(\x0b\x32\x0f.a2a.v1.MessageB\x03\xe0\x41\x02R\x07request\x12\x46\n\rconfiguration\x18\x02 \x01(\x0b\x32 .a2a.v1.SendMessageConfigurationR\rconfiguration\x12\x33\n\x08metadata\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\"P\n\x0eGetTaskRequest\x12\x17\n\x04name\x18\x01 \x01(\tB\x03\xe0\x41\x02R\x04name\x12%\n\x0ehistory_length\x18\x02 \x01(\x05R\rhistoryLength\"\'\n\x11\x43\x61ncelTaskRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\"4\n\x1eGetTaskPushNotificationRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\"\xa3\x01\n!CreateTaskPushNotificationRequest\x12\x1b\n\x06parent\x18\x01 \x01(\tB\x03\xe0\x41\x02R\x06parent\x12 \n\tconfig_id\x18\x02 \x01(\tB\x03\xe0\x41\x02R\x08\x63onfigId\x12?\n\x06\x63onfig\x18\x03 \x01(\x0b\x32\".a2a.v1.TaskPushNotificationConfigB\x03\xe0\x41\x02R\x06\x63onfig\"-\n\x17TaskSubscriptionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\"u\n\x1fListTaskPushNotificationRequest\x12\x16\n\x06parent\x18\x01 \x01(\tR\x06parent\x12\x1b\n\tpage_size\x18\x02 \x01(\x05R\x08pageSize\x12\x1d\n\npage_token\x18\x03 \x01(\tR\tpageToken\"\x15\n\x13GetAgentCardRequest\"i\n\x13SendMessageResponse\x12\"\n\x04task\x18\x01 \x01(\x0b\x32\x0c.a2a.v1.TaskH\x00R\x04task\x12#\n\x03msg\x18\x02 \x01(\x0b\x32\x0f.a2a.v1.MessageH\x00R\x03msgB\t\n\x07payload\"\xf6\x01\n\x0eStreamResponse\x12\"\n\x04task\x18\x01 \x01(\x0b\x32\x0c.a2a.v1.TaskH\x00R\x04task\x12#\n\x03msg\x18\x02 \x01(\x0b\x32\x0f.a2a.v1.MessageH\x00R\x03msg\x12\x44\n\rstatus_update\x18\x03 \x01(\x0b\x32\x1d.a2a.v1.TaskStatusUpdateEventH\x00R\x0cstatusUpdate\x12J\n\x0f\x61rtifact_update\x18\x04 \x01(\x0b\x32\x1f.a2a.v1.TaskArtifactUpdateEventH\x00R\x0e\x61rtifactUpdateB\t\n\x07payload\"\x88\x01\n ListTaskPushNotificationResponse\x12<\n\x07\x63onfigs\x18\x01 \x03(\x0b\x32\".a2a.v1.TaskPushNotificationConfigR\x07\x63onfigs\x12&\n\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken*\xfa\x01\n\tTaskState\x12\x1a\n\x16TASK_STATE_UNSPECIFIED\x10\x00\x12\x18\n\x14TASK_STATE_SUBMITTED\x10\x01\x12\x16\n\x12TASK_STATE_WORKING\x10\x02\x12\x18\n\x14TASK_STATE_COMPLETED\x10\x03\x12\x15\n\x11TASK_STATE_FAILED\x10\x04\x12\x18\n\x14TASK_STATE_CANCELLED\x10\x05\x12\x1d\n\x19TASK_STATE_INPUT_REQUIRED\x10\x06\x12\x17\n\x13TASK_STATE_REJECTED\x10\x07\x12\x1c\n\x18TASK_STATE_AUTH_REQUIRED\x10\x08*;\n\x04Role\x12\x14\n\x10ROLE_UNSPECIFIED\x10\x00\x12\r\n\tROLE_USER\x10\x01\x12\x0e\n\nROLE_AGENT\x10\x02\x32\xd3\x08\n\nA2AService\x12\x63\n\x0bSendMessage\x12\x1a.a2a.v1.SendMessageRequest\x1a\x1b.a2a.v1.SendMessageResponse\"\x1b\x82\xd3\xe4\x93\x02\x15\"\x10/v1/message:send:\x01*\x12k\n\x14SendStreamingMessage\x12\x1a.a2a.v1.SendMessageRequest\x1a\x16.a2a.v1.StreamResponse\"\x1d\x82\xd3\xe4\x93\x02\x17\"\x12/v1/message:stream:\x01*0\x01\x12R\n\x07GetTask\x12\x16.a2a.v1.GetTaskRequest\x1a\x0c.a2a.v1.Task\"!\xda\x41\x04name\x82\xd3\xe4\x93\x02\x14\x12\x12/v1/{name=tasks/*}\x12[\n\nCancelTask\x12\x19.a2a.v1.CancelTaskRequest\x1a\x0c.a2a.v1.Task\"$\x82\xd3\xe4\x93\x02\x1e\"\x19/v1/{name=tasks/*}:cancel:\x01*\x12s\n\x10TaskSubscription\x12\x1f.a2a.v1.TaskSubscriptionRequest\x1a\x16.a2a.v1.StreamResponse\"$\x82\xd3\xe4\x93\x02\x1e\x12\x1c/v1/{name=tasks/*}:subscribe0\x01\x12\xb2\x01\n\x1a\x43reateTaskPushNotification\x12).a2a.v1.CreateTaskPushNotificationRequest\x1a\".a2a.v1.TaskPushNotificationConfig\"E\xda\x41\rparent,config\x82\xd3\xe4\x93\x02/\"%/v1/{parent=task/*/pushNotifications}:\x06\x63onfig\x12\x9c\x01\n\x17GetTaskPushNotification\x12&.a2a.v1.GetTaskPushNotificationRequest\x1a\".a2a.v1.TaskPushNotificationConfig\"5\xda\x41\x04name\x82\xd3\xe4\x93\x02(\x12&/v1/{name=tasks/*/pushNotifications/*}\x12\xa6\x01\n\x18ListTaskPushNotification\x12\'.a2a.v1.ListTaskPushNotificationRequest\x1a(.a2a.v1.ListTaskPushNotificationResponse\"7\xda\x41\x06parent\x82\xd3\xe4\x93\x02(\x12&/v1/{parent=tasks/*}/pushNotifications\x12P\n\x0cGetAgentCard\x12\x1b.a2a.v1.GetAgentCardRequest\x1a\x11.a2a.v1.AgentCard\"\x10\x82\xd3\xe4\x93\x02\n\x12\x08/v1/cardBi\n\ncom.a2a.v1B\x08\x41\x32\x61ProtoP\x01Z\x18google.golang.org/a2a/v1\xa2\x02\x03\x41XX\xaa\x02\x06\x41\x32\x61.V1\xca\x02\x06\x41\x32\x61\\V1\xe2\x02\x12\x41\x32\x61\\V1\\GPBMetadata\xea\x02\x07\x41\x32\x61::V1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ta2a.proto\x12\x06\x61\x32\x61.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xde\x01\n\x18SendMessageConfiguration\x12\x32\n\x15\x61\x63\x63\x65pted_output_modes\x18\x01 \x03(\tR\x13\x61\x63\x63\x65ptedOutputModes\x12K\n\x11push_notification\x18\x02 \x01(\x0b\x32\x1e.a2a.v1.PushNotificationConfigR\x10pushNotification\x12%\n\x0ehistory_length\x18\x03 \x01(\x05R\rhistoryLength\x12\x1a\n\x08\x62locking\x18\x04 \x01(\x08R\x08\x62locking\"\xf1\x01\n\x04Task\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1d\n\ncontext_id\x18\x02 \x01(\tR\tcontextId\x12*\n\x06status\x18\x03 \x01(\x0b\x32\x12.a2a.v1.TaskStatusR\x06status\x12.\n\tartifacts\x18\x04 \x03(\x0b\x32\x10.a2a.v1.ArtifactR\tartifacts\x12)\n\x07history\x18\x05 \x03(\x0b\x32\x0f.a2a.v1.MessageR\x07history\x12\x33\n\x08metadata\x18\x06 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\"\x98\x01\n\nTaskStatus\x12\'\n\x05state\x18\x01 \x01(\x0e\x32\x11.a2a.v1.TaskStateR\x05state\x12\'\n\x06update\x18\x02 \x01(\x0b\x32\x0f.a2a.v1.MessageR\x06update\x12\x38\n\ttimestamp\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\ttimestamp\"t\n\x04Part\x12\x14\n\x04text\x18\x01 \x01(\tH\x00R\x04text\x12&\n\x04\x66ile\x18\x02 \x01(\x0b\x32\x10.a2a.v1.FilePartH\x00R\x04\x66ile\x12&\n\x04\x64\x61ta\x18\x03 \x01(\x0b\x32\x10.a2a.v1.DataPartH\x00R\x04\x64\x61taB\x06\n\x04part\"\x7f\n\x08\x46ilePart\x12$\n\rfile_with_uri\x18\x01 \x01(\tH\x00R\x0b\x66ileWithUri\x12(\n\x0f\x66ile_with_bytes\x18\x02 \x01(\x0cH\x00R\rfileWithBytes\x12\x1b\n\tmime_type\x18\x03 \x01(\tR\x08mimeTypeB\x06\n\x04\x66ile\"7\n\x08\x44\x61taPart\x12+\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32\x17.google.protobuf.StructR\x04\x64\x61ta\"\xff\x01\n\x07Message\x12\x1d\n\nmessage_id\x18\x01 \x01(\tR\tmessageId\x12\x1d\n\ncontext_id\x18\x02 \x01(\tR\tcontextId\x12\x17\n\x07task_id\x18\x03 \x01(\tR\x06taskId\x12 \n\x04role\x18\x04 \x01(\x0e\x32\x0c.a2a.v1.RoleR\x04role\x12&\n\x07\x63ontent\x18\x05 \x03(\x0b\x32\x0c.a2a.v1.PartR\x07\x63ontent\x12\x33\n\x08metadata\x18\x06 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\x12\x1e\n\nextensions\x18\x07 \x03(\tR\nextensions\"\xda\x01\n\x08\x41rtifact\x12\x1f\n\x0b\x61rtifact_id\x18\x01 \x01(\tR\nartifactId\x12\x12\n\x04name\x18\x03 \x01(\tR\x04name\x12 \n\x0b\x64\x65scription\x18\x04 \x01(\tR\x0b\x64\x65scription\x12\"\n\x05parts\x18\x05 \x03(\x0b\x32\x0c.a2a.v1.PartR\x05parts\x12\x33\n\x08metadata\x18\x06 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\x12\x1e\n\nextensions\x18\x07 \x03(\tR\nextensions\"\xc6\x01\n\x15TaskStatusUpdateEvent\x12\x17\n\x07task_id\x18\x01 \x01(\tR\x06taskId\x12\x1d\n\ncontext_id\x18\x02 \x01(\tR\tcontextId\x12*\n\x06status\x18\x03 \x01(\x0b\x32\x12.a2a.v1.TaskStatusR\x06status\x12\x14\n\x05\x66inal\x18\x04 \x01(\x08R\x05\x66inal\x12\x33\n\x08metadata\x18\x05 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\"\xeb\x01\n\x17TaskArtifactUpdateEvent\x12\x17\n\x07task_id\x18\x01 \x01(\tR\x06taskId\x12\x1d\n\ncontext_id\x18\x02 \x01(\tR\tcontextId\x12,\n\x08\x61rtifact\x18\x03 \x01(\x0b\x32\x10.a2a.v1.ArtifactR\x08\x61rtifact\x12\x16\n\x06\x61ppend\x18\x04 \x01(\x08R\x06\x61ppend\x12\x1d\n\nlast_chunk\x18\x05 \x01(\x08R\tlastChunk\x12\x33\n\x08metadata\x18\x06 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\"\x94\x01\n\x16PushNotificationConfig\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x10\n\x03url\x18\x02 \x01(\tR\x03url\x12\x14\n\x05token\x18\x03 \x01(\tR\x05token\x12\x42\n\x0e\x61uthentication\x18\x04 \x01(\x0b\x32\x1a.a2a.v1.AuthenticationInfoR\x0e\x61uthentication\"P\n\x12\x41uthenticationInfo\x12\x18\n\x07schemes\x18\x01 \x03(\tR\x07schemes\x12 \n\x0b\x63redentials\x18\x02 \x01(\tR\x0b\x63redentials\"@\n\x0e\x41gentInterface\x12\x10\n\x03url\x18\x01 \x01(\tR\x03url\x12\x1c\n\ttransport\x18\x02 \x01(\tR\ttransport\"\xf1\x06\n\tAgentCard\x12)\n\x10protocol_version\x18\x10 \x01(\tR\x0fprotocolVersion\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12 \n\x0b\x64\x65scription\x18\x02 \x01(\tR\x0b\x64\x65scription\x12\x10\n\x03url\x18\x03 \x01(\tR\x03url\x12/\n\x13preferred_transport\x18\x0e \x01(\tR\x12preferredTransport\x12K\n\x15\x61\x64\x64itional_interfaces\x18\x0f \x03(\x0b\x32\x16.a2a.v1.AgentInterfaceR\x14\x61\x64\x64itionalInterfaces\x12\x31\n\x08provider\x18\x04 \x01(\x0b\x32\x15.a2a.v1.AgentProviderR\x08provider\x12\x18\n\x07version\x18\x05 \x01(\tR\x07version\x12+\n\x11\x64ocumentation_url\x18\x06 \x01(\tR\x10\x64ocumentationUrl\x12=\n\x0c\x63\x61pabilities\x18\x07 \x01(\x0b\x32\x19.a2a.v1.AgentCapabilitiesR\x0c\x63\x61pabilities\x12Q\n\x10security_schemes\x18\x08 \x03(\x0b\x32&.a2a.v1.AgentCard.SecuritySchemesEntryR\x0fsecuritySchemes\x12,\n\x08security\x18\t \x03(\x0b\x32\x10.a2a.v1.SecurityR\x08security\x12.\n\x13\x64\x65\x66\x61ult_input_modes\x18\n \x03(\tR\x11\x64\x65\x66\x61ultInputModes\x12\x30\n\x14\x64\x65\x66\x61ult_output_modes\x18\x0b \x03(\tR\x12\x64\x65\x66\x61ultOutputModes\x12*\n\x06skills\x18\x0c \x03(\x0b\x32\x12.a2a.v1.AgentSkillR\x06skills\x12O\n$supports_authenticated_extended_card\x18\r \x01(\x08R!supportsAuthenticatedExtendedCard\x1aZ\n\x14SecuritySchemesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12,\n\x05value\x18\x02 \x01(\x0b\x32\x16.a2a.v1.SecuritySchemeR\x05value:\x02\x38\x01\"E\n\rAgentProvider\x12\x10\n\x03url\x18\x01 \x01(\tR\x03url\x12\"\n\x0corganization\x18\x02 \x01(\tR\x0corganization\"\x98\x01\n\x11\x41gentCapabilities\x12\x1c\n\tstreaming\x18\x01 \x01(\x08R\tstreaming\x12-\n\x12push_notifications\x18\x02 \x01(\x08R\x11pushNotifications\x12\x36\n\nextensions\x18\x03 \x03(\x0b\x32\x16.a2a.v1.AgentExtensionR\nextensions\"\x91\x01\n\x0e\x41gentExtension\x12\x10\n\x03uri\x18\x01 \x01(\tR\x03uri\x12 \n\x0b\x64\x65scription\x18\x02 \x01(\tR\x0b\x64\x65scription\x12\x1a\n\x08required\x18\x03 \x01(\x08R\x08required\x12/\n\x06params\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructR\x06params\"\xc6\x01\n\nAgentSkill\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12 \n\x0b\x64\x65scription\x18\x03 \x01(\tR\x0b\x64\x65scription\x12\x12\n\x04tags\x18\x04 \x03(\tR\x04tags\x12\x1a\n\x08\x65xamples\x18\x05 \x03(\tR\x08\x65xamples\x12\x1f\n\x0binput_modes\x18\x06 \x03(\tR\ninputModes\x12!\n\x0coutput_modes\x18\x07 \x03(\tR\x0boutputModes\"\x8a\x01\n\x1aTaskPushNotificationConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12X\n\x18push_notification_config\x18\x02 \x01(\x0b\x32\x1e.a2a.v1.PushNotificationConfigR\x16pushNotificationConfig\" \n\nStringList\x12\x12\n\x04list\x18\x01 \x03(\tR\x04list\"\x93\x01\n\x08Security\x12\x37\n\x07schemes\x18\x01 \x03(\x0b\x32\x1d.a2a.v1.Security.SchemesEntryR\x07schemes\x1aN\n\x0cSchemesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12(\n\x05value\x18\x02 \x01(\x0b\x32\x12.a2a.v1.StringListR\x05value:\x02\x38\x01\"\x91\x03\n\x0eSecurityScheme\x12U\n\x17\x61pi_key_security_scheme\x18\x01 \x01(\x0b\x32\x1c.a2a.v1.APIKeySecuritySchemeH\x00R\x14\x61piKeySecurityScheme\x12[\n\x19http_auth_security_scheme\x18\x02 \x01(\x0b\x32\x1e.a2a.v1.HTTPAuthSecuritySchemeH\x00R\x16httpAuthSecurityScheme\x12T\n\x16oauth2_security_scheme\x18\x03 \x01(\x0b\x32\x1c.a2a.v1.OAuth2SecuritySchemeH\x00R\x14oauth2SecurityScheme\x12k\n\x1fopen_id_connect_security_scheme\x18\x04 \x01(\x0b\x32#.a2a.v1.OpenIdConnectSecuritySchemeH\x00R\x1bopenIdConnectSecuritySchemeB\x08\n\x06scheme\"h\n\x14\x41PIKeySecurityScheme\x12 \n\x0b\x64\x65scription\x18\x01 \x01(\tR\x0b\x64\x65scription\x12\x1a\n\x08location\x18\x02 \x01(\tR\x08location\x12\x12\n\x04name\x18\x03 \x01(\tR\x04name\"w\n\x16HTTPAuthSecurityScheme\x12 \n\x0b\x64\x65scription\x18\x01 \x01(\tR\x0b\x64\x65scription\x12\x16\n\x06scheme\x18\x02 \x01(\tR\x06scheme\x12#\n\rbearer_format\x18\x03 \x01(\tR\x0c\x62\x65\x61rerFormat\"b\n\x14OAuth2SecurityScheme\x12 \n\x0b\x64\x65scription\x18\x01 \x01(\tR\x0b\x64\x65scription\x12(\n\x05\x66lows\x18\x02 \x01(\x0b\x32\x12.a2a.v1.OAuthFlowsR\x05\x66lows\"n\n\x1bOpenIdConnectSecurityScheme\x12 \n\x0b\x64\x65scription\x18\x01 \x01(\tR\x0b\x64\x65scription\x12-\n\x13open_id_connect_url\x18\x02 \x01(\tR\x10openIdConnectUrl\"\xb0\x02\n\nOAuthFlows\x12S\n\x12\x61uthorization_code\x18\x01 \x01(\x0b\x32\".a2a.v1.AuthorizationCodeOAuthFlowH\x00R\x11\x61uthorizationCode\x12S\n\x12\x63lient_credentials\x18\x02 \x01(\x0b\x32\".a2a.v1.ClientCredentialsOAuthFlowH\x00R\x11\x63lientCredentials\x12\x37\n\x08implicit\x18\x03 \x01(\x0b\x32\x19.a2a.v1.ImplicitOAuthFlowH\x00R\x08implicit\x12\x37\n\x08password\x18\x04 \x01(\x0b\x32\x19.a2a.v1.PasswordOAuthFlowH\x00R\x08passwordB\x06\n\x04\x66low\"\x8a\x02\n\x1a\x41uthorizationCodeOAuthFlow\x12+\n\x11\x61uthorization_url\x18\x01 \x01(\tR\x10\x61uthorizationUrl\x12\x1b\n\ttoken_url\x18\x02 \x01(\tR\x08tokenUrl\x12\x1f\n\x0brefresh_url\x18\x03 \x01(\tR\nrefreshUrl\x12\x46\n\x06scopes\x18\x04 \x03(\x0b\x32..a2a.v1.AuthorizationCodeOAuthFlow.ScopesEntryR\x06scopes\x1a\x39\n\x0bScopesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xdd\x01\n\x1a\x43lientCredentialsOAuthFlow\x12\x1b\n\ttoken_url\x18\x01 \x01(\tR\x08tokenUrl\x12\x1f\n\x0brefresh_url\x18\x02 \x01(\tR\nrefreshUrl\x12\x46\n\x06scopes\x18\x03 \x03(\x0b\x32..a2a.v1.ClientCredentialsOAuthFlow.ScopesEntryR\x06scopes\x1a\x39\n\x0bScopesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xdb\x01\n\x11ImplicitOAuthFlow\x12+\n\x11\x61uthorization_url\x18\x01 \x01(\tR\x10\x61uthorizationUrl\x12\x1f\n\x0brefresh_url\x18\x02 \x01(\tR\nrefreshUrl\x12=\n\x06scopes\x18\x03 \x03(\x0b\x32%.a2a.v1.ImplicitOAuthFlow.ScopesEntryR\x06scopes\x1a\x39\n\x0bScopesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xcb\x01\n\x11PasswordOAuthFlow\x12\x1b\n\ttoken_url\x18\x01 \x01(\tR\x08tokenUrl\x12\x1f\n\x0brefresh_url\x18\x02 \x01(\tR\nrefreshUrl\x12=\n\x06scopes\x18\x03 \x03(\x0b\x32%.a2a.v1.PasswordOAuthFlow.ScopesEntryR\x06scopes\x1a\x39\n\x0bScopesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xc1\x01\n\x12SendMessageRequest\x12.\n\x07request\x18\x01 \x01(\x0b\x32\x0f.a2a.v1.MessageB\x03\xe0\x41\x02R\x07request\x12\x46\n\rconfiguration\x18\x02 \x01(\x0b\x32 .a2a.v1.SendMessageConfigurationR\rconfiguration\x12\x33\n\x08metadata\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\"P\n\x0eGetTaskRequest\x12\x17\n\x04name\x18\x01 \x01(\tB\x03\xe0\x41\x02R\x04name\x12%\n\x0ehistory_length\x18\x02 \x01(\x05R\rhistoryLength\"\'\n\x11\x43\x61ncelTaskRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\":\n$GetTaskPushNotificationConfigRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\"=\n\'DeleteTaskPushNotificationConfigRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\"\xa9\x01\n\'CreateTaskPushNotificationConfigRequest\x12\x1b\n\x06parent\x18\x01 \x01(\tB\x03\xe0\x41\x02R\x06parent\x12 \n\tconfig_id\x18\x02 \x01(\tB\x03\xe0\x41\x02R\x08\x63onfigId\x12?\n\x06\x63onfig\x18\x03 \x01(\x0b\x32\".a2a.v1.TaskPushNotificationConfigB\x03\xe0\x41\x02R\x06\x63onfig\"-\n\x17TaskSubscriptionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\"{\n%ListTaskPushNotificationConfigRequest\x12\x16\n\x06parent\x18\x01 \x01(\tR\x06parent\x12\x1b\n\tpage_size\x18\x02 \x01(\x05R\x08pageSize\x12\x1d\n\npage_token\x18\x03 \x01(\tR\tpageToken\"\x15\n\x13GetAgentCardRequest\"i\n\x13SendMessageResponse\x12\"\n\x04task\x18\x01 \x01(\x0b\x32\x0c.a2a.v1.TaskH\x00R\x04task\x12#\n\x03msg\x18\x02 \x01(\x0b\x32\x0f.a2a.v1.MessageH\x00R\x03msgB\t\n\x07payload\"\xf6\x01\n\x0eStreamResponse\x12\"\n\x04task\x18\x01 \x01(\x0b\x32\x0c.a2a.v1.TaskH\x00R\x04task\x12#\n\x03msg\x18\x02 \x01(\x0b\x32\x0f.a2a.v1.MessageH\x00R\x03msg\x12\x44\n\rstatus_update\x18\x03 \x01(\x0b\x32\x1d.a2a.v1.TaskStatusUpdateEventH\x00R\x0cstatusUpdate\x12J\n\x0f\x61rtifact_update\x18\x04 \x01(\x0b\x32\x1f.a2a.v1.TaskArtifactUpdateEventH\x00R\x0e\x61rtifactUpdateB\t\n\x07payload\"\x8e\x01\n&ListTaskPushNotificationConfigResponse\x12<\n\x07\x63onfigs\x18\x01 \x03(\x0b\x32\".a2a.v1.TaskPushNotificationConfigR\x07\x63onfigs\x12&\n\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken*\xfa\x01\n\tTaskState\x12\x1a\n\x16TASK_STATE_UNSPECIFIED\x10\x00\x12\x18\n\x14TASK_STATE_SUBMITTED\x10\x01\x12\x16\n\x12TASK_STATE_WORKING\x10\x02\x12\x18\n\x14TASK_STATE_COMPLETED\x10\x03\x12\x15\n\x11TASK_STATE_FAILED\x10\x04\x12\x18\n\x14TASK_STATE_CANCELLED\x10\x05\x12\x1d\n\x19TASK_STATE_INPUT_REQUIRED\x10\x06\x12\x17\n\x13TASK_STATE_REJECTED\x10\x07\x12\x1c\n\x18TASK_STATE_AUTH_REQUIRED\x10\x08*;\n\x04Role\x12\x14\n\x10ROLE_UNSPECIFIED\x10\x00\x12\r\n\tROLE_USER\x10\x01\x12\x0e\n\nROLE_AGENT\x10\x02\x32\xba\n\n\nA2AService\x12\x63\n\x0bSendMessage\x12\x1a.a2a.v1.SendMessageRequest\x1a\x1b.a2a.v1.SendMessageResponse\"\x1b\x82\xd3\xe4\x93\x02\x15\"\x10/v1/message:send:\x01*\x12k\n\x14SendStreamingMessage\x12\x1a.a2a.v1.SendMessageRequest\x1a\x16.a2a.v1.StreamResponse\"\x1d\x82\xd3\xe4\x93\x02\x17\"\x12/v1/message:stream:\x01*0\x01\x12R\n\x07GetTask\x12\x16.a2a.v1.GetTaskRequest\x1a\x0c.a2a.v1.Task\"!\xda\x41\x04name\x82\xd3\xe4\x93\x02\x14\x12\x12/v1/{name=tasks/*}\x12[\n\nCancelTask\x12\x19.a2a.v1.CancelTaskRequest\x1a\x0c.a2a.v1.Task\"$\x82\xd3\xe4\x93\x02\x1e\"\x19/v1/{name=tasks/*}:cancel:\x01*\x12s\n\x10TaskSubscription\x12\x1f.a2a.v1.TaskSubscriptionRequest\x1a\x16.a2a.v1.StreamResponse\"$\x82\xd3\xe4\x93\x02\x1e\x12\x1c/v1/{name=tasks/*}:subscribe0\x01\x12\xc4\x01\n CreateTaskPushNotificationConfig\x12/.a2a.v1.CreateTaskPushNotificationConfigRequest\x1a\".a2a.v1.TaskPushNotificationConfig\"K\xda\x41\rparent,config\x82\xd3\xe4\x93\x02\x35\"+/v1/{parent=task/*/pushNotificationConfigs}:\x06\x63onfig\x12\xae\x01\n\x1dGetTaskPushNotificationConfig\x12,.a2a.v1.GetTaskPushNotificationConfigRequest\x1a\".a2a.v1.TaskPushNotificationConfig\";\xda\x41\x04name\x82\xd3\xe4\x93\x02.\x12,/v1/{name=tasks/*/pushNotificationConfigs/*}\x12\xbe\x01\n\x1eListTaskPushNotificationConfig\x12-.a2a.v1.ListTaskPushNotificationConfigRequest\x1a..a2a.v1.ListTaskPushNotificationConfigResponse\"=\xda\x41\x06parent\x82\xd3\xe4\x93\x02.\x12,/v1/{parent=tasks/*}/pushNotificationConfigs\x12P\n\x0cGetAgentCard\x12\x1b.a2a.v1.GetAgentCardRequest\x1a\x11.a2a.v1.AgentCard\"\x10\x82\xd3\xe4\x93\x02\n\x12\x08/v1/card\x12\xa8\x01\n DeleteTaskPushNotificationConfig\x12/.a2a.v1.DeleteTaskPushNotificationConfigRequest\x1a\x16.google.protobuf.Empty\";\xda\x41\x04name\x82\xd3\xe4\x93\x02.*,/v1/{name=tasks/*/pushNotificationConfigs/*}Bi\n\ncom.a2a.v1B\x08\x41\x32\x61ProtoP\x01Z\x18google.golang.org/a2a/v1\xa2\x02\x03\x41XX\xaa\x02\x06\x41\x32\x61.V1\xca\x02\x06\x41\x32\x61\\V1\xe2\x02\x12\x41\x32\x61\\V1\\GPBMetadata\xea\x02\x07\x41\x32\x61::V1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -53,12 +54,12 @@ _globals['_SENDMESSAGEREQUEST'].fields_by_name['request']._serialized_options = b'\340A\002' _globals['_GETTASKREQUEST'].fields_by_name['name']._loaded_options = None _globals['_GETTASKREQUEST'].fields_by_name['name']._serialized_options = b'\340A\002' - _globals['_CREATETASKPUSHNOTIFICATIONREQUEST'].fields_by_name['parent']._loaded_options = None - _globals['_CREATETASKPUSHNOTIFICATIONREQUEST'].fields_by_name['parent']._serialized_options = b'\340A\002' - _globals['_CREATETASKPUSHNOTIFICATIONREQUEST'].fields_by_name['config_id']._loaded_options = None - _globals['_CREATETASKPUSHNOTIFICATIONREQUEST'].fields_by_name['config_id']._serialized_options = b'\340A\002' - _globals['_CREATETASKPUSHNOTIFICATIONREQUEST'].fields_by_name['config']._loaded_options = None - _globals['_CREATETASKPUSHNOTIFICATIONREQUEST'].fields_by_name['config']._serialized_options = b'\340A\002' + _globals['_CREATETASKPUSHNOTIFICATIONCONFIGREQUEST'].fields_by_name['parent']._loaded_options = None + _globals['_CREATETASKPUSHNOTIFICATIONCONFIGREQUEST'].fields_by_name['parent']._serialized_options = b'\340A\002' + _globals['_CREATETASKPUSHNOTIFICATIONCONFIGREQUEST'].fields_by_name['config_id']._loaded_options = None + _globals['_CREATETASKPUSHNOTIFICATIONCONFIGREQUEST'].fields_by_name['config_id']._serialized_options = b'\340A\002' + _globals['_CREATETASKPUSHNOTIFICATIONCONFIGREQUEST'].fields_by_name['config']._loaded_options = None + _globals['_CREATETASKPUSHNOTIFICATIONCONFIGREQUEST'].fields_by_name['config']._serialized_options = b'\340A\002' _globals['_A2ASERVICE'].methods_by_name['SendMessage']._loaded_options = None _globals['_A2ASERVICE'].methods_by_name['SendMessage']._serialized_options = b'\202\323\344\223\002\025\"\020/v1/message:send:\001*' _globals['_A2ASERVICE'].methods_by_name['SendStreamingMessage']._loaded_options = None @@ -69,112 +70,118 @@ _globals['_A2ASERVICE'].methods_by_name['CancelTask']._serialized_options = b'\202\323\344\223\002\036\"\031/v1/{name=tasks/*}:cancel:\001*' _globals['_A2ASERVICE'].methods_by_name['TaskSubscription']._loaded_options = None _globals['_A2ASERVICE'].methods_by_name['TaskSubscription']._serialized_options = b'\202\323\344\223\002\036\022\034/v1/{name=tasks/*}:subscribe' - _globals['_A2ASERVICE'].methods_by_name['CreateTaskPushNotification']._loaded_options = None - _globals['_A2ASERVICE'].methods_by_name['CreateTaskPushNotification']._serialized_options = b'\332A\rparent,config\202\323\344\223\002/\"%/v1/{parent=task/*/pushNotifications}:\006config' - _globals['_A2ASERVICE'].methods_by_name['GetTaskPushNotification']._loaded_options = None - _globals['_A2ASERVICE'].methods_by_name['GetTaskPushNotification']._serialized_options = b'\332A\004name\202\323\344\223\002(\022&/v1/{name=tasks/*/pushNotifications/*}' - _globals['_A2ASERVICE'].methods_by_name['ListTaskPushNotification']._loaded_options = None - _globals['_A2ASERVICE'].methods_by_name['ListTaskPushNotification']._serialized_options = b'\332A\006parent\202\323\344\223\002(\022&/v1/{parent=tasks/*}/pushNotifications' + _globals['_A2ASERVICE'].methods_by_name['CreateTaskPushNotificationConfig']._loaded_options = None + _globals['_A2ASERVICE'].methods_by_name['CreateTaskPushNotificationConfig']._serialized_options = b'\332A\rparent,config\202\323\344\223\0025\"+/v1/{parent=task/*/pushNotificationConfigs}:\006config' + _globals['_A2ASERVICE'].methods_by_name['GetTaskPushNotificationConfig']._loaded_options = None + _globals['_A2ASERVICE'].methods_by_name['GetTaskPushNotificationConfig']._serialized_options = b'\332A\004name\202\323\344\223\002.\022,/v1/{name=tasks/*/pushNotificationConfigs/*}' + _globals['_A2ASERVICE'].methods_by_name['ListTaskPushNotificationConfig']._loaded_options = None + _globals['_A2ASERVICE'].methods_by_name['ListTaskPushNotificationConfig']._serialized_options = b'\332A\006parent\202\323\344\223\002.\022,/v1/{parent=tasks/*}/pushNotificationConfigs' _globals['_A2ASERVICE'].methods_by_name['GetAgentCard']._loaded_options = None _globals['_A2ASERVICE'].methods_by_name['GetAgentCard']._serialized_options = b'\202\323\344\223\002\n\022\010/v1/card' - _globals['_TASKSTATE']._serialized_start=7161 - _globals['_TASKSTATE']._serialized_end=7411 - _globals['_ROLE']._serialized_start=7413 - _globals['_ROLE']._serialized_end=7472 - _globals['_SENDMESSAGECONFIGURATION']._serialized_start=173 - _globals['_SENDMESSAGECONFIGURATION']._serialized_end=395 - _globals['_TASK']._serialized_start=398 - _globals['_TASK']._serialized_end=639 - _globals['_TASKSTATUS']._serialized_start=642 - _globals['_TASKSTATUS']._serialized_end=794 - _globals['_PART']._serialized_start=796 - _globals['_PART']._serialized_end=912 - _globals['_FILEPART']._serialized_start=914 - _globals['_FILEPART']._serialized_end=1041 - _globals['_DATAPART']._serialized_start=1043 - _globals['_DATAPART']._serialized_end=1098 - _globals['_MESSAGE']._serialized_start=1101 - _globals['_MESSAGE']._serialized_end=1356 - _globals['_ARTIFACT']._serialized_start=1359 - _globals['_ARTIFACT']._serialized_end=1577 - _globals['_TASKSTATUSUPDATEEVENT']._serialized_start=1580 - _globals['_TASKSTATUSUPDATEEVENT']._serialized_end=1778 - _globals['_TASKARTIFACTUPDATEEVENT']._serialized_start=1781 - _globals['_TASKARTIFACTUPDATEEVENT']._serialized_end=2016 - _globals['_PUSHNOTIFICATIONCONFIG']._serialized_start=2019 - _globals['_PUSHNOTIFICATIONCONFIG']._serialized_end=2167 - _globals['_AUTHENTICATIONINFO']._serialized_start=2169 - _globals['_AUTHENTICATIONINFO']._serialized_end=2249 - _globals['_AGENTCARD']._serialized_start=2252 - _globals['_AGENTCARD']._serialized_end=2964 - _globals['_AGENTCARD_SECURITYSCHEMESENTRY']._serialized_start=2874 - _globals['_AGENTCARD_SECURITYSCHEMESENTRY']._serialized_end=2964 - _globals['_AGENTPROVIDER']._serialized_start=2966 - _globals['_AGENTPROVIDER']._serialized_end=3035 - _globals['_AGENTCAPABILITIES']._serialized_start=3038 - _globals['_AGENTCAPABILITIES']._serialized_end=3190 - _globals['_AGENTEXTENSION']._serialized_start=3193 - _globals['_AGENTEXTENSION']._serialized_end=3338 - _globals['_AGENTSKILL']._serialized_start=3341 - _globals['_AGENTSKILL']._serialized_end=3539 - _globals['_TASKPUSHNOTIFICATIONCONFIG']._serialized_start=3542 - _globals['_TASKPUSHNOTIFICATIONCONFIG']._serialized_end=3680 - _globals['_STRINGLIST']._serialized_start=3682 - _globals['_STRINGLIST']._serialized_end=3714 - _globals['_SECURITY']._serialized_start=3717 - _globals['_SECURITY']._serialized_end=3864 - _globals['_SECURITY_SCHEMESENTRY']._serialized_start=3786 - _globals['_SECURITY_SCHEMESENTRY']._serialized_end=3864 - _globals['_SECURITYSCHEME']._serialized_start=3867 - _globals['_SECURITYSCHEME']._serialized_end=4268 - _globals['_APIKEYSECURITYSCHEME']._serialized_start=4270 - _globals['_APIKEYSECURITYSCHEME']._serialized_end=4374 - _globals['_HTTPAUTHSECURITYSCHEME']._serialized_start=4376 - _globals['_HTTPAUTHSECURITYSCHEME']._serialized_end=4495 - _globals['_OAUTH2SECURITYSCHEME']._serialized_start=4497 - _globals['_OAUTH2SECURITYSCHEME']._serialized_end=4595 - _globals['_OPENIDCONNECTSECURITYSCHEME']._serialized_start=4597 - _globals['_OPENIDCONNECTSECURITYSCHEME']._serialized_end=4707 - _globals['_OAUTHFLOWS']._serialized_start=4710 - _globals['_OAUTHFLOWS']._serialized_end=5014 - _globals['_AUTHORIZATIONCODEOAUTHFLOW']._serialized_start=5017 - _globals['_AUTHORIZATIONCODEOAUTHFLOW']._serialized_end=5283 - _globals['_AUTHORIZATIONCODEOAUTHFLOW_SCOPESENTRY']._serialized_start=5226 - _globals['_AUTHORIZATIONCODEOAUTHFLOW_SCOPESENTRY']._serialized_end=5283 - _globals['_CLIENTCREDENTIALSOAUTHFLOW']._serialized_start=5286 - _globals['_CLIENTCREDENTIALSOAUTHFLOW']._serialized_end=5507 - _globals['_CLIENTCREDENTIALSOAUTHFLOW_SCOPESENTRY']._serialized_start=5226 - _globals['_CLIENTCREDENTIALSOAUTHFLOW_SCOPESENTRY']._serialized_end=5283 - _globals['_IMPLICITOAUTHFLOW']._serialized_start=5510 - _globals['_IMPLICITOAUTHFLOW']._serialized_end=5729 - _globals['_IMPLICITOAUTHFLOW_SCOPESENTRY']._serialized_start=5226 - _globals['_IMPLICITOAUTHFLOW_SCOPESENTRY']._serialized_end=5283 - _globals['_PASSWORDOAUTHFLOW']._serialized_start=5732 - _globals['_PASSWORDOAUTHFLOW']._serialized_end=5935 - _globals['_PASSWORDOAUTHFLOW_SCOPESENTRY']._serialized_start=5226 - _globals['_PASSWORDOAUTHFLOW_SCOPESENTRY']._serialized_end=5283 - _globals['_SENDMESSAGEREQUEST']._serialized_start=5938 - _globals['_SENDMESSAGEREQUEST']._serialized_end=6131 - _globals['_GETTASKREQUEST']._serialized_start=6133 - _globals['_GETTASKREQUEST']._serialized_end=6213 - _globals['_CANCELTASKREQUEST']._serialized_start=6215 - _globals['_CANCELTASKREQUEST']._serialized_end=6254 - _globals['_GETTASKPUSHNOTIFICATIONREQUEST']._serialized_start=6256 - _globals['_GETTASKPUSHNOTIFICATIONREQUEST']._serialized_end=6308 - _globals['_CREATETASKPUSHNOTIFICATIONREQUEST']._serialized_start=6311 - _globals['_CREATETASKPUSHNOTIFICATIONREQUEST']._serialized_end=6474 - _globals['_TASKSUBSCRIPTIONREQUEST']._serialized_start=6476 - _globals['_TASKSUBSCRIPTIONREQUEST']._serialized_end=6521 - _globals['_LISTTASKPUSHNOTIFICATIONREQUEST']._serialized_start=6523 - _globals['_LISTTASKPUSHNOTIFICATIONREQUEST']._serialized_end=6640 - _globals['_GETAGENTCARDREQUEST']._serialized_start=6642 - _globals['_GETAGENTCARDREQUEST']._serialized_end=6663 - _globals['_SENDMESSAGERESPONSE']._serialized_start=6665 - _globals['_SENDMESSAGERESPONSE']._serialized_end=6770 - _globals['_STREAMRESPONSE']._serialized_start=6773 - _globals['_STREAMRESPONSE']._serialized_end=7019 - _globals['_LISTTASKPUSHNOTIFICATIONRESPONSE']._serialized_start=7022 - _globals['_LISTTASKPUSHNOTIFICATIONRESPONSE']._serialized_end=7158 - _globals['_A2ASERVICE']._serialized_start=7475 - _globals['_A2ASERVICE']._serialized_end=8582 + _globals['_A2ASERVICE'].methods_by_name['DeleteTaskPushNotificationConfig']._loaded_options = None + _globals['_A2ASERVICE'].methods_by_name['DeleteTaskPushNotificationConfig']._serialized_options = b'\332A\004name\202\323\344\223\002.*,/v1/{name=tasks/*/pushNotificationConfigs/*}' + _globals['_TASKSTATE']._serialized_start=7512 + _globals['_TASKSTATE']._serialized_end=7762 + _globals['_ROLE']._serialized_start=7764 + _globals['_ROLE']._serialized_end=7823 + _globals['_SENDMESSAGECONFIGURATION']._serialized_start=202 + _globals['_SENDMESSAGECONFIGURATION']._serialized_end=424 + _globals['_TASK']._serialized_start=427 + _globals['_TASK']._serialized_end=668 + _globals['_TASKSTATUS']._serialized_start=671 + _globals['_TASKSTATUS']._serialized_end=823 + _globals['_PART']._serialized_start=825 + _globals['_PART']._serialized_end=941 + _globals['_FILEPART']._serialized_start=943 + _globals['_FILEPART']._serialized_end=1070 + _globals['_DATAPART']._serialized_start=1072 + _globals['_DATAPART']._serialized_end=1127 + _globals['_MESSAGE']._serialized_start=1130 + _globals['_MESSAGE']._serialized_end=1385 + _globals['_ARTIFACT']._serialized_start=1388 + _globals['_ARTIFACT']._serialized_end=1606 + _globals['_TASKSTATUSUPDATEEVENT']._serialized_start=1609 + _globals['_TASKSTATUSUPDATEEVENT']._serialized_end=1807 + _globals['_TASKARTIFACTUPDATEEVENT']._serialized_start=1810 + _globals['_TASKARTIFACTUPDATEEVENT']._serialized_end=2045 + _globals['_PUSHNOTIFICATIONCONFIG']._serialized_start=2048 + _globals['_PUSHNOTIFICATIONCONFIG']._serialized_end=2196 + _globals['_AUTHENTICATIONINFO']._serialized_start=2198 + _globals['_AUTHENTICATIONINFO']._serialized_end=2278 + _globals['_AGENTINTERFACE']._serialized_start=2280 + _globals['_AGENTINTERFACE']._serialized_end=2344 + _globals['_AGENTCARD']._serialized_start=2347 + _globals['_AGENTCARD']._serialized_end=3228 + _globals['_AGENTCARD_SECURITYSCHEMESENTRY']._serialized_start=3138 + _globals['_AGENTCARD_SECURITYSCHEMESENTRY']._serialized_end=3228 + _globals['_AGENTPROVIDER']._serialized_start=3230 + _globals['_AGENTPROVIDER']._serialized_end=3299 + _globals['_AGENTCAPABILITIES']._serialized_start=3302 + _globals['_AGENTCAPABILITIES']._serialized_end=3454 + _globals['_AGENTEXTENSION']._serialized_start=3457 + _globals['_AGENTEXTENSION']._serialized_end=3602 + _globals['_AGENTSKILL']._serialized_start=3605 + _globals['_AGENTSKILL']._serialized_end=3803 + _globals['_TASKPUSHNOTIFICATIONCONFIG']._serialized_start=3806 + _globals['_TASKPUSHNOTIFICATIONCONFIG']._serialized_end=3944 + _globals['_STRINGLIST']._serialized_start=3946 + _globals['_STRINGLIST']._serialized_end=3978 + _globals['_SECURITY']._serialized_start=3981 + _globals['_SECURITY']._serialized_end=4128 + _globals['_SECURITY_SCHEMESENTRY']._serialized_start=4050 + _globals['_SECURITY_SCHEMESENTRY']._serialized_end=4128 + _globals['_SECURITYSCHEME']._serialized_start=4131 + _globals['_SECURITYSCHEME']._serialized_end=4532 + _globals['_APIKEYSECURITYSCHEME']._serialized_start=4534 + _globals['_APIKEYSECURITYSCHEME']._serialized_end=4638 + _globals['_HTTPAUTHSECURITYSCHEME']._serialized_start=4640 + _globals['_HTTPAUTHSECURITYSCHEME']._serialized_end=4759 + _globals['_OAUTH2SECURITYSCHEME']._serialized_start=4761 + _globals['_OAUTH2SECURITYSCHEME']._serialized_end=4859 + _globals['_OPENIDCONNECTSECURITYSCHEME']._serialized_start=4861 + _globals['_OPENIDCONNECTSECURITYSCHEME']._serialized_end=4971 + _globals['_OAUTHFLOWS']._serialized_start=4974 + _globals['_OAUTHFLOWS']._serialized_end=5278 + _globals['_AUTHORIZATIONCODEOAUTHFLOW']._serialized_start=5281 + _globals['_AUTHORIZATIONCODEOAUTHFLOW']._serialized_end=5547 + _globals['_AUTHORIZATIONCODEOAUTHFLOW_SCOPESENTRY']._serialized_start=5490 + _globals['_AUTHORIZATIONCODEOAUTHFLOW_SCOPESENTRY']._serialized_end=5547 + _globals['_CLIENTCREDENTIALSOAUTHFLOW']._serialized_start=5550 + _globals['_CLIENTCREDENTIALSOAUTHFLOW']._serialized_end=5771 + _globals['_CLIENTCREDENTIALSOAUTHFLOW_SCOPESENTRY']._serialized_start=5490 + _globals['_CLIENTCREDENTIALSOAUTHFLOW_SCOPESENTRY']._serialized_end=5547 + _globals['_IMPLICITOAUTHFLOW']._serialized_start=5774 + _globals['_IMPLICITOAUTHFLOW']._serialized_end=5993 + _globals['_IMPLICITOAUTHFLOW_SCOPESENTRY']._serialized_start=5490 + _globals['_IMPLICITOAUTHFLOW_SCOPESENTRY']._serialized_end=5547 + _globals['_PASSWORDOAUTHFLOW']._serialized_start=5996 + _globals['_PASSWORDOAUTHFLOW']._serialized_end=6199 + _globals['_PASSWORDOAUTHFLOW_SCOPESENTRY']._serialized_start=5490 + _globals['_PASSWORDOAUTHFLOW_SCOPESENTRY']._serialized_end=5547 + _globals['_SENDMESSAGEREQUEST']._serialized_start=6202 + _globals['_SENDMESSAGEREQUEST']._serialized_end=6395 + _globals['_GETTASKREQUEST']._serialized_start=6397 + _globals['_GETTASKREQUEST']._serialized_end=6477 + _globals['_CANCELTASKREQUEST']._serialized_start=6479 + _globals['_CANCELTASKREQUEST']._serialized_end=6518 + _globals['_GETTASKPUSHNOTIFICATIONCONFIGREQUEST']._serialized_start=6520 + _globals['_GETTASKPUSHNOTIFICATIONCONFIGREQUEST']._serialized_end=6578 + _globals['_DELETETASKPUSHNOTIFICATIONCONFIGREQUEST']._serialized_start=6580 + _globals['_DELETETASKPUSHNOTIFICATIONCONFIGREQUEST']._serialized_end=6641 + _globals['_CREATETASKPUSHNOTIFICATIONCONFIGREQUEST']._serialized_start=6644 + _globals['_CREATETASKPUSHNOTIFICATIONCONFIGREQUEST']._serialized_end=6813 + _globals['_TASKSUBSCRIPTIONREQUEST']._serialized_start=6815 + _globals['_TASKSUBSCRIPTIONREQUEST']._serialized_end=6860 + _globals['_LISTTASKPUSHNOTIFICATIONCONFIGREQUEST']._serialized_start=6862 + _globals['_LISTTASKPUSHNOTIFICATIONCONFIGREQUEST']._serialized_end=6985 + _globals['_GETAGENTCARDREQUEST']._serialized_start=6987 + _globals['_GETAGENTCARDREQUEST']._serialized_end=7008 + _globals['_SENDMESSAGERESPONSE']._serialized_start=7010 + _globals['_SENDMESSAGERESPONSE']._serialized_end=7115 + _globals['_STREAMRESPONSE']._serialized_start=7118 + _globals['_STREAMRESPONSE']._serialized_end=7364 + _globals['_LISTTASKPUSHNOTIFICATIONCONFIGRESPONSE']._serialized_start=7367 + _globals['_LISTTASKPUSHNOTIFICATIONCONFIGRESPONSE']._serialized_end=7509 + _globals['_A2ASERVICE']._serialized_start=7826 + _globals['_A2ASERVICE']._serialized_end=9164 # @@protoc_insertion_point(module_scope) diff --git a/src/a2a/grpc/a2a_pb2.pyi b/src/a2a/grpc/a2a_pb2.pyi index 31f363155..70cf0cf48 100644 --- a/src/a2a/grpc/a2a_pb2.pyi +++ b/src/a2a/grpc/a2a_pb2.pyi @@ -3,6 +3,7 @@ import datetime from google.api import annotations_pb2 as _annotations_pb2 from google.api import client_pb2 as _client_pb2 from google.api import field_behavior_pb2 as _field_behavior_pb2 +from google.protobuf import empty_pb2 as _empty_pb2 from google.protobuf import struct_pb2 as _struct_pb2 from google.protobuf import timestamp_pb2 as _timestamp_pb2 from google.protobuf.internal import containers as _containers @@ -192,8 +193,16 @@ class AuthenticationInfo(_message.Message): credentials: str def __init__(self, schemes: _Optional[_Iterable[str]] = ..., credentials: _Optional[str] = ...) -> None: ... +class AgentInterface(_message.Message): + __slots__ = ("url", "transport") + URL_FIELD_NUMBER: _ClassVar[int] + TRANSPORT_FIELD_NUMBER: _ClassVar[int] + url: str + transport: str + def __init__(self, url: _Optional[str] = ..., transport: _Optional[str] = ...) -> None: ... + class AgentCard(_message.Message): - __slots__ = ("name", "description", "url", "provider", "version", "documentation_url", "capabilities", "security_schemes", "security", "default_input_modes", "default_output_modes", "skills", "supports_authenticated_extended_card") + __slots__ = ("protocol_version", "name", "description", "url", "preferred_transport", "additional_interfaces", "provider", "version", "documentation_url", "capabilities", "security_schemes", "security", "default_input_modes", "default_output_modes", "skills", "supports_authenticated_extended_card") class SecuritySchemesEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -201,9 +210,12 @@ class AgentCard(_message.Message): key: str value: SecurityScheme def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[SecurityScheme, _Mapping]] = ...) -> None: ... + PROTOCOL_VERSION_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] DESCRIPTION_FIELD_NUMBER: _ClassVar[int] URL_FIELD_NUMBER: _ClassVar[int] + PREFERRED_TRANSPORT_FIELD_NUMBER: _ClassVar[int] + ADDITIONAL_INTERFACES_FIELD_NUMBER: _ClassVar[int] PROVIDER_FIELD_NUMBER: _ClassVar[int] VERSION_FIELD_NUMBER: _ClassVar[int] DOCUMENTATION_URL_FIELD_NUMBER: _ClassVar[int] @@ -214,9 +226,12 @@ class AgentCard(_message.Message): DEFAULT_OUTPUT_MODES_FIELD_NUMBER: _ClassVar[int] SKILLS_FIELD_NUMBER: _ClassVar[int] SUPPORTS_AUTHENTICATED_EXTENDED_CARD_FIELD_NUMBER: _ClassVar[int] + protocol_version: str name: str description: str url: str + preferred_transport: str + additional_interfaces: _containers.RepeatedCompositeFieldContainer[AgentInterface] provider: AgentProvider version: str documentation_url: str @@ -227,7 +242,7 @@ class AgentCard(_message.Message): default_output_modes: _containers.RepeatedScalarFieldContainer[str] skills: _containers.RepeatedCompositeFieldContainer[AgentSkill] supports_authenticated_extended_card: bool - def __init__(self, name: _Optional[str] = ..., description: _Optional[str] = ..., url: _Optional[str] = ..., provider: _Optional[_Union[AgentProvider, _Mapping]] = ..., version: _Optional[str] = ..., documentation_url: _Optional[str] = ..., capabilities: _Optional[_Union[AgentCapabilities, _Mapping]] = ..., security_schemes: _Optional[_Mapping[str, SecurityScheme]] = ..., security: _Optional[_Iterable[_Union[Security, _Mapping]]] = ..., default_input_modes: _Optional[_Iterable[str]] = ..., default_output_modes: _Optional[_Iterable[str]] = ..., skills: _Optional[_Iterable[_Union[AgentSkill, _Mapping]]] = ..., supports_authenticated_extended_card: bool = ...) -> None: ... + def __init__(self, protocol_version: _Optional[str] = ..., name: _Optional[str] = ..., description: _Optional[str] = ..., url: _Optional[str] = ..., preferred_transport: _Optional[str] = ..., additional_interfaces: _Optional[_Iterable[_Union[AgentInterface, _Mapping]]] = ..., provider: _Optional[_Union[AgentProvider, _Mapping]] = ..., version: _Optional[str] = ..., documentation_url: _Optional[str] = ..., capabilities: _Optional[_Union[AgentCapabilities, _Mapping]] = ..., security_schemes: _Optional[_Mapping[str, SecurityScheme]] = ..., security: _Optional[_Iterable[_Union[Security, _Mapping]]] = ..., default_input_modes: _Optional[_Iterable[str]] = ..., default_output_modes: _Optional[_Iterable[str]] = ..., skills: _Optional[_Iterable[_Union[AgentSkill, _Mapping]]] = ..., supports_authenticated_extended_card: bool = ...) -> None: ... class AgentProvider(_message.Message): __slots__ = ("url", "organization") @@ -458,13 +473,19 @@ class CancelTaskRequest(_message.Message): name: str def __init__(self, name: _Optional[str] = ...) -> None: ... -class GetTaskPushNotificationRequest(_message.Message): +class GetTaskPushNotificationConfigRequest(_message.Message): + __slots__ = ("name",) + NAME_FIELD_NUMBER: _ClassVar[int] + name: str + def __init__(self, name: _Optional[str] = ...) -> None: ... + +class DeleteTaskPushNotificationConfigRequest(_message.Message): __slots__ = ("name",) NAME_FIELD_NUMBER: _ClassVar[int] name: str def __init__(self, name: _Optional[str] = ...) -> None: ... -class CreateTaskPushNotificationRequest(_message.Message): +class CreateTaskPushNotificationConfigRequest(_message.Message): __slots__ = ("parent", "config_id", "config") PARENT_FIELD_NUMBER: _ClassVar[int] CONFIG_ID_FIELD_NUMBER: _ClassVar[int] @@ -480,7 +501,7 @@ class TaskSubscriptionRequest(_message.Message): name: str def __init__(self, name: _Optional[str] = ...) -> None: ... -class ListTaskPushNotificationRequest(_message.Message): +class ListTaskPushNotificationConfigRequest(_message.Message): __slots__ = ("parent", "page_size", "page_token") PARENT_FIELD_NUMBER: _ClassVar[int] PAGE_SIZE_FIELD_NUMBER: _ClassVar[int] @@ -514,7 +535,7 @@ class StreamResponse(_message.Message): artifact_update: TaskArtifactUpdateEvent def __init__(self, task: _Optional[_Union[Task, _Mapping]] = ..., msg: _Optional[_Union[Message, _Mapping]] = ..., status_update: _Optional[_Union[TaskStatusUpdateEvent, _Mapping]] = ..., artifact_update: _Optional[_Union[TaskArtifactUpdateEvent, _Mapping]] = ...) -> None: ... -class ListTaskPushNotificationResponse(_message.Message): +class ListTaskPushNotificationConfigResponse(_message.Message): __slots__ = ("configs", "next_page_token") CONFIGS_FIELD_NUMBER: _ClassVar[int] NEXT_PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] diff --git a/src/a2a/grpc/a2a_pb2_grpc.py b/src/a2a/grpc/a2a_pb2_grpc.py index 5c3f64563..e314bde3a 100644 --- a/src/a2a/grpc/a2a_pb2_grpc.py +++ b/src/a2a/grpc/a2a_pb2_grpc.py @@ -3,18 +3,19 @@ import grpc from . import a2a_pb2 as a2a__pb2 +from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 class A2AServiceStub(object): """A2AService defines the gRPC version of the A2A protocol. This has a slightly different shape than the JSONRPC version to better conform to AIP-127, where appropriate. The nouns are AgentCard, Message, Task and - TaskPushNotification. + TaskPushNotificationConfig. - Messages are not a standard resource so there is no get/delete/update/list interface, only a send and stream custom methods. - Tasks have a get interface and custom cancel and subscribe methods. - - TaskPushNotification are a resource whose parent is a task. They have get, - list and create methods. + - TaskPushNotificationConfig are a resource whose parent is a task. + They have get, list and create methods. - AgentCard is a static resource with only a get method. fields are not present as they don't comply with AIP rules, and the optional history_length on the get task method is not present as it also @@ -52,38 +53,43 @@ def __init__(self, channel): request_serializer=a2a__pb2.TaskSubscriptionRequest.SerializeToString, response_deserializer=a2a__pb2.StreamResponse.FromString, _registered_method=True) - self.CreateTaskPushNotification = channel.unary_unary( - '/a2a.v1.A2AService/CreateTaskPushNotification', - request_serializer=a2a__pb2.CreateTaskPushNotificationRequest.SerializeToString, + self.CreateTaskPushNotificationConfig = channel.unary_unary( + '/a2a.v1.A2AService/CreateTaskPushNotificationConfig', + request_serializer=a2a__pb2.CreateTaskPushNotificationConfigRequest.SerializeToString, response_deserializer=a2a__pb2.TaskPushNotificationConfig.FromString, _registered_method=True) - self.GetTaskPushNotification = channel.unary_unary( - '/a2a.v1.A2AService/GetTaskPushNotification', - request_serializer=a2a__pb2.GetTaskPushNotificationRequest.SerializeToString, + self.GetTaskPushNotificationConfig = channel.unary_unary( + '/a2a.v1.A2AService/GetTaskPushNotificationConfig', + request_serializer=a2a__pb2.GetTaskPushNotificationConfigRequest.SerializeToString, response_deserializer=a2a__pb2.TaskPushNotificationConfig.FromString, _registered_method=True) - self.ListTaskPushNotification = channel.unary_unary( - '/a2a.v1.A2AService/ListTaskPushNotification', - request_serializer=a2a__pb2.ListTaskPushNotificationRequest.SerializeToString, - response_deserializer=a2a__pb2.ListTaskPushNotificationResponse.FromString, + self.ListTaskPushNotificationConfig = channel.unary_unary( + '/a2a.v1.A2AService/ListTaskPushNotificationConfig', + request_serializer=a2a__pb2.ListTaskPushNotificationConfigRequest.SerializeToString, + response_deserializer=a2a__pb2.ListTaskPushNotificationConfigResponse.FromString, _registered_method=True) self.GetAgentCard = channel.unary_unary( '/a2a.v1.A2AService/GetAgentCard', request_serializer=a2a__pb2.GetAgentCardRequest.SerializeToString, response_deserializer=a2a__pb2.AgentCard.FromString, _registered_method=True) + self.DeleteTaskPushNotificationConfig = channel.unary_unary( + '/a2a.v1.A2AService/DeleteTaskPushNotificationConfig', + request_serializer=a2a__pb2.DeleteTaskPushNotificationConfigRequest.SerializeToString, + response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, + _registered_method=True) class A2AServiceServicer(object): """A2AService defines the gRPC version of the A2A protocol. This has a slightly different shape than the JSONRPC version to better conform to AIP-127, where appropriate. The nouns are AgentCard, Message, Task and - TaskPushNotification. + TaskPushNotificationConfig. - Messages are not a standard resource so there is no get/delete/update/list interface, only a send and stream custom methods. - Tasks have a get interface and custom cancel and subscribe methods. - - TaskPushNotification are a resource whose parent is a task. They have get, - list and create methods. + - TaskPushNotificationConfig are a resource whose parent is a task. + They have get, list and create methods. - AgentCard is a static resource with only a get method. fields are not present as they don't comply with AIP rules, and the optional history_length on the get task method is not present as it also @@ -131,21 +137,21 @@ def TaskSubscription(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def CreateTaskPushNotification(self, request, context): + def CreateTaskPushNotificationConfig(self, request, context): """Set a push notification config for a task. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def GetTaskPushNotification(self, request, context): + def GetTaskPushNotificationConfig(self, request, context): """Get a push notification config for a task. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def ListTaskPushNotification(self, request, context): + def ListTaskPushNotificationConfig(self, request, context): """Get a list of push notifications configured for a task. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -159,6 +165,13 @@ def GetAgentCard(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def DeleteTaskPushNotificationConfig(self, request, context): + """Delete a push notification config for a task. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_A2AServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -187,26 +200,31 @@ def add_A2AServiceServicer_to_server(servicer, server): request_deserializer=a2a__pb2.TaskSubscriptionRequest.FromString, response_serializer=a2a__pb2.StreamResponse.SerializeToString, ), - 'CreateTaskPushNotification': grpc.unary_unary_rpc_method_handler( - servicer.CreateTaskPushNotification, - request_deserializer=a2a__pb2.CreateTaskPushNotificationRequest.FromString, + 'CreateTaskPushNotificationConfig': grpc.unary_unary_rpc_method_handler( + servicer.CreateTaskPushNotificationConfig, + request_deserializer=a2a__pb2.CreateTaskPushNotificationConfigRequest.FromString, response_serializer=a2a__pb2.TaskPushNotificationConfig.SerializeToString, ), - 'GetTaskPushNotification': grpc.unary_unary_rpc_method_handler( - servicer.GetTaskPushNotification, - request_deserializer=a2a__pb2.GetTaskPushNotificationRequest.FromString, + 'GetTaskPushNotificationConfig': grpc.unary_unary_rpc_method_handler( + servicer.GetTaskPushNotificationConfig, + request_deserializer=a2a__pb2.GetTaskPushNotificationConfigRequest.FromString, response_serializer=a2a__pb2.TaskPushNotificationConfig.SerializeToString, ), - 'ListTaskPushNotification': grpc.unary_unary_rpc_method_handler( - servicer.ListTaskPushNotification, - request_deserializer=a2a__pb2.ListTaskPushNotificationRequest.FromString, - response_serializer=a2a__pb2.ListTaskPushNotificationResponse.SerializeToString, + 'ListTaskPushNotificationConfig': grpc.unary_unary_rpc_method_handler( + servicer.ListTaskPushNotificationConfig, + request_deserializer=a2a__pb2.ListTaskPushNotificationConfigRequest.FromString, + response_serializer=a2a__pb2.ListTaskPushNotificationConfigResponse.SerializeToString, ), 'GetAgentCard': grpc.unary_unary_rpc_method_handler( servicer.GetAgentCard, request_deserializer=a2a__pb2.GetAgentCardRequest.FromString, response_serializer=a2a__pb2.AgentCard.SerializeToString, ), + 'DeleteTaskPushNotificationConfig': grpc.unary_unary_rpc_method_handler( + servicer.DeleteTaskPushNotificationConfig, + request_deserializer=a2a__pb2.DeleteTaskPushNotificationConfigRequest.FromString, + response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'a2a.v1.A2AService', rpc_method_handlers) @@ -219,12 +237,12 @@ class A2AService(object): """A2AService defines the gRPC version of the A2A protocol. This has a slightly different shape than the JSONRPC version to better conform to AIP-127, where appropriate. The nouns are AgentCard, Message, Task and - TaskPushNotification. + TaskPushNotificationConfig. - Messages are not a standard resource so there is no get/delete/update/list interface, only a send and stream custom methods. - Tasks have a get interface and custom cancel and subscribe methods. - - TaskPushNotification are a resource whose parent is a task. They have get, - list and create methods. + - TaskPushNotificationConfig are a resource whose parent is a task. + They have get, list and create methods. - AgentCard is a static resource with only a get method. fields are not present as they don't comply with AIP rules, and the optional history_length on the get task method is not present as it also @@ -367,7 +385,7 @@ def TaskSubscription(request, _registered_method=True) @staticmethod - def CreateTaskPushNotification(request, + def CreateTaskPushNotificationConfig(request, target, options=(), channel_credentials=None, @@ -380,8 +398,8 @@ def CreateTaskPushNotification(request, return grpc.experimental.unary_unary( request, target, - '/a2a.v1.A2AService/CreateTaskPushNotification', - a2a__pb2.CreateTaskPushNotificationRequest.SerializeToString, + '/a2a.v1.A2AService/CreateTaskPushNotificationConfig', + a2a__pb2.CreateTaskPushNotificationConfigRequest.SerializeToString, a2a__pb2.TaskPushNotificationConfig.FromString, options, channel_credentials, @@ -394,7 +412,7 @@ def CreateTaskPushNotification(request, _registered_method=True) @staticmethod - def GetTaskPushNotification(request, + def GetTaskPushNotificationConfig(request, target, options=(), channel_credentials=None, @@ -407,8 +425,8 @@ def GetTaskPushNotification(request, return grpc.experimental.unary_unary( request, target, - '/a2a.v1.A2AService/GetTaskPushNotification', - a2a__pb2.GetTaskPushNotificationRequest.SerializeToString, + '/a2a.v1.A2AService/GetTaskPushNotificationConfig', + a2a__pb2.GetTaskPushNotificationConfigRequest.SerializeToString, a2a__pb2.TaskPushNotificationConfig.FromString, options, channel_credentials, @@ -421,7 +439,7 @@ def GetTaskPushNotification(request, _registered_method=True) @staticmethod - def ListTaskPushNotification(request, + def ListTaskPushNotificationConfig(request, target, options=(), channel_credentials=None, @@ -434,9 +452,9 @@ def ListTaskPushNotification(request, return grpc.experimental.unary_unary( request, target, - '/a2a.v1.A2AService/ListTaskPushNotification', - a2a__pb2.ListTaskPushNotificationRequest.SerializeToString, - a2a__pb2.ListTaskPushNotificationResponse.FromString, + '/a2a.v1.A2AService/ListTaskPushNotificationConfig', + a2a__pb2.ListTaskPushNotificationConfigRequest.SerializeToString, + a2a__pb2.ListTaskPushNotificationConfigResponse.FromString, options, channel_credentials, insecure, @@ -473,3 +491,30 @@ def GetAgentCard(request, timeout, metadata, _registered_method=True) + + @staticmethod + def DeleteTaskPushNotificationConfig(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/a2a.v1.A2AService/DeleteTaskPushNotificationConfig', + a2a__pb2.DeleteTaskPushNotificationConfigRequest.SerializeToString, + google_dot_protobuf_dot_empty__pb2.Empty.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/src/a2a/server/apps/jsonrpc/fastapi_app.py b/src/a2a/server/apps/jsonrpc/fastapi_app.py index fa307be2a..e961e8683 100644 --- a/src/a2a/server/apps/jsonrpc/fastapi_app.py +++ b/src/a2a/server/apps/jsonrpc/fastapi_app.py @@ -49,25 +49,21 @@ def __init__( context_builder=context_builder, ) - def build( + def add_routes_to_app( self, + app: FastAPI, agent_card_url: str = '/.well-known/agent.json', rpc_url: str = '/', extended_agent_card_url: str = '/agent/authenticatedExtendedCard', - **kwargs: Any, - ) -> FastAPI: - """Builds and returns the FastAPI application instance. + ) -> None: + """Adds the routes to the FastAPI application. Args: + app: The FastAPI application to add the routes to. agent_card_url: The URL for the agent card endpoint. rpc_url: The URL for the A2A JSON-RPC endpoint. extended_agent_card_url: The URL for the authenticated extended agent card endpoint. - **kwargs: Additional keyword arguments to pass to the FastAPI constructor. - - Returns: - A configured FastAPI application instance. """ - app = FastAPI(**kwargs) @app.post(rpc_url) async def handle_a2a_request(request: Request) -> Response: @@ -85,4 +81,28 @@ async def get_extended_agent_card(request: Request) -> Response: request ) + def build( + self, + agent_card_url: str = '/.well-known/agent.json', + rpc_url: str = '/', + extended_agent_card_url: str = '/agent/authenticatedExtendedCard', + **kwargs: Any, + ) -> FastAPI: + """Builds and returns the FastAPI application instance. + + Args: + agent_card_url: The URL for the agent card endpoint. + rpc_url: The URL for the A2A JSON-RPC endpoint. + extended_agent_card_url: The URL for the authenticated extended agent card endpoint. + **kwargs: Additional keyword arguments to pass to the FastAPI constructor. + + Returns: + A configured FastAPI application instance. + """ + app = FastAPI(**kwargs) + + self.add_routes_to_app( + app, agent_card_url, rpc_url, extended_agent_card_url + ) + return app diff --git a/src/a2a/server/apps/jsonrpc/jsonrpc_app.py b/src/a2a/server/apps/jsonrpc/jsonrpc_app.py index ff6998cd6..0b78cc1c8 100644 --- a/src/a2a/server/apps/jsonrpc/jsonrpc_app.py +++ b/src/a2a/server/apps/jsonrpc/jsonrpc_app.py @@ -375,7 +375,10 @@ async def _handle_get_agent_card(self, request: Request) -> JSONResponse: # The public agent card is a direct serialization of the agent_card # provided at initialization. return JSONResponse( - self.agent_card.model_dump(mode='json', exclude_none=True) + self.agent_card.model_dump( + exclude_none=True, + by_alias=True, + ) ) async def _handle_get_authenticated_extended_agent_card( @@ -392,7 +395,8 @@ async def _handle_get_authenticated_extended_agent_card( if self.extended_agent_card: return JSONResponse( self.extended_agent_card.model_dump( - mode='json', exclude_none=True + exclude_none=True, + by_alias=True, ) ) # If supportsAuthenticatedExtendedCard is true, but no specific diff --git a/src/a2a/server/apps/jsonrpc/starlette_app.py b/src/a2a/server/apps/jsonrpc/starlette_app.py index 4aaff37ed..1826841b9 100644 --- a/src/a2a/server/apps/jsonrpc/starlette_app.py +++ b/src/a2a/server/apps/jsonrpc/starlette_app.py @@ -92,6 +92,28 @@ def routes( ) return app_routes + def add_routes_to_app( + self, + app: Starlette, + agent_card_url: str = '/.well-known/agent.json', + rpc_url: str = '/', + extended_agent_card_url: str = '/agent/authenticatedExtendedCard', + ) -> None: + """Adds the routes to the Starlette application. + + Args: + app: The Starlette application to add the routes to. + agent_card_url: The URL path for the agent card endpoint. + rpc_url: The URL path for the A2A JSON-RPC endpoint (POST requests). + extended_agent_card_url: The URL for the authenticated extended agent card endpoint. + """ + routes = self.routes( + agent_card_url=agent_card_url, + rpc_url=rpc_url, + extended_agent_card_url=extended_agent_card_url, + ) + app.routes.extend(routes) + def build( self, agent_card_url: str = '/.well-known/agent.json', @@ -110,14 +132,10 @@ def build( Returns: A configured Starlette application instance. """ - app_routes = self.routes( - agent_card_url=agent_card_url, - rpc_url=rpc_url, - extended_agent_card_url=extended_agent_card_url, + app = Starlette(**kwargs) + + self.add_routes_to_app( + app, agent_card_url, rpc_url, extended_agent_card_url ) - if 'routes' in kwargs: - kwargs['routes'].extend(app_routes) - else: - kwargs['routes'] = app_routes - return Starlette(**kwargs) + return app diff --git a/src/a2a/server/events/event_consumer.py b/src/a2a/server/events/event_consumer.py index c5794f45f..1fe5c3f36 100644 --- a/src/a2a/server/events/event_consumer.py +++ b/src/a2a/server/events/event_consumer.py @@ -112,7 +112,7 @@ async def consume_all(self) -> AsyncGenerator[Event]: TaskState.failed, TaskState.rejected, TaskState.unknown, - TaskState.input_required + TaskState.input_required, ) ) ) @@ -130,11 +130,20 @@ async def consume_all(self) -> AsyncGenerator[Event]: except TimeoutError: # continue polling until there is a final event continue + except asyncio.TimeoutError: # pyright: ignore [reportUnusedExcept] + # This class was made an alias of build-in TimeoutError after 3.11 + continue except QueueClosed: # Confirm that the queue is closed, e.g. we aren't on # python 3.12 and get a queue empty error on an open queue if self.queue.is_closed(): break + except Exception as e: + logger.error( + f'Stopping event consumption due to exception: {e}' + ) + self._exception = e + continue def agent_task_callback(self, agent_task: asyncio.Task[None]) -> None: """Callback to handle exceptions from the agent's execution task. diff --git a/src/a2a/server/request_handlers/default_request_handler.py b/src/a2a/server/request_handlers/default_request_handler.py index 2c81da967..ff86a069d 100644 --- a/src/a2a/server/request_handlers/default_request_handler.py +++ b/src/a2a/server/request_handlers/default_request_handler.py @@ -27,7 +27,9 @@ TaskStore, ) from a2a.types import ( + GetTaskPushNotificationConfigParams, InternalError, + InvalidParamsError, Message, MessageSendConfiguration, MessageSendParams, @@ -37,6 +39,7 @@ TaskNotFoundError, TaskPushNotificationConfig, TaskQueryParams, + TaskState, UnsupportedOperationError, ) from a2a.utils.errors import ServerError @@ -45,6 +48,13 @@ logger = logging.getLogger(__name__) +TERMINAL_TASK_STATES = { + TaskState.completed, + TaskState.canceled, + TaskState.failed, + TaskState.rejected, +} + @trace_class(kind=SpanKind.SERVER) class DefaultRequestHandler(RequestHandler): @@ -159,16 +169,17 @@ async def _run_event_stream( await self.agent_executor.execute(request, queue) await queue.close() - async def on_message_send( + async def _setup_message_execution( self, params: MessageSendParams, context: ServerCallContext | None = None, - ) -> Message | Task: - """Default handler for 'message/send' interface (non-streaming). + ) -> tuple[TaskManager, str, EventQueue, ResultAggregator, asyncio.Task]: + """Common setup logic for both streaming and non-streaming message handling. - Starts the agent execution for the message and waits for the final - result (Task or Message). + Returns: + A tuple of (task_manager, task_id, queue, result_aggregator, producer_task) """ + # Create task manager and validate existing task task_manager = TaskManager( task_id=params.message.taskId, context_id=params.message.contextId, @@ -176,7 +187,15 @@ async def on_message_send( initial_message=params.message, ) task: Task | None = await task_manager.get_task() + if task: + if task.status.state in TERMINAL_TASK_STATES: + raise ServerError( + error=InvalidParamsError( + message=f'Task {task.id} is in terminal state: {task.status.state}' + ) + ) + task = task_manager.update_with_message(params.message, task) if self.should_add_push_info(params): assert isinstance(self._push_notifier, PushNotifier) @@ -190,6 +209,8 @@ async def on_message_send( await self._push_notifier.set_info( task.id, params.configuration.pushNotificationConfig ) + + # Build request context request_context = await self._request_context_builder.build( params=params, task_id=task.id if task else None, @@ -206,13 +227,49 @@ async def on_message_send( result_aggregator = ResultAggregator(task_manager) # TODO: to manage the non-blocking flows. producer_task = asyncio.create_task( - self._run_event_stream( - request_context, - queue, - ) + self._run_event_stream(request_context, queue) ) await self._register_producer(task_id, producer_task) + return task_manager, task_id, queue, result_aggregator, producer_task + + def _validate_task_id_match(self, task_id: str, event_task_id: str) -> None: + """Validates that agent-generated task ID matches the expected task ID.""" + if task_id != event_task_id: + logger.error( + f'Agent generated task_id={event_task_id} does not match the RequestContext task_id={task_id}.' + ) + raise ServerError( + InternalError(message='Task ID mismatch in agent response') + ) + + async def _send_push_notification_if_needed( + self, task_id: str, result_aggregator: ResultAggregator + ) -> None: + """Sends push notification if configured and task is available.""" + if self._push_notifier and task_id: + latest_task = await result_aggregator.current_result + if isinstance(latest_task, Task): + await self._push_notifier.send_notification(latest_task) + + async def on_message_send( + self, + params: MessageSendParams, + context: ServerCallContext | None = None, + ) -> Message | Task: + """Default handler for 'message/send' interface (non-streaming). + + Starts the agent execution for the message and waits for the final + result (Task or Message). + """ + ( + task_manager, + task_id, + queue, + result_aggregator, + producer_task, + ) = await self._setup_message_execution(params, context) + consumer = EventConsumer(queue) producer_task.add_done_callback(consumer.agent_task_callback) @@ -225,14 +282,16 @@ async def on_message_send( if not result: raise ServerError(error=InternalError()) - if isinstance(result, Task) and task_id != result.id: - logger.error( - f'Agent generated task_id={result.id} does not match the RequestContext task_id={task_id}.' - ) - raise ServerError( - InternalError(message='Task ID mismatch in agent response') - ) + if isinstance(result, Task): + self._validate_task_id_match(task_id, result.id) + + await self._send_push_notification_if_needed( + task_id, result_aggregator + ) + except Exception as e: + logger.error(f'Agent execution failed. Error: {e}') + raise finally: if interrupted: # TODO: Track this disconnected cleanup task. @@ -254,79 +313,34 @@ async def on_message_send_stream( Starts the agent execution and yields events as they are produced by the agent. """ - task_manager = TaskManager( - task_id=params.message.taskId, - context_id=params.message.contextId, - task_store=self.task_store, - initial_message=params.message, - ) - task: Task | None = await task_manager.get_task() - - if task: - task = task_manager.update_with_message(params.message, task) - - if self.should_add_push_info(params): - assert isinstance(self._push_notifier, PushNotifier) - assert isinstance( - params.configuration, MessageSendConfiguration - ) - assert isinstance( - params.configuration.pushNotificationConfig, - PushNotificationConfig, - ) - await self._push_notifier.set_info( - task.id, params.configuration.pushNotificationConfig - ) - else: - queue = EventQueue() - result_aggregator = ResultAggregator(task_manager) - request_context = await self._request_context_builder.build( - params=params, - task_id=task.id if task else None, - context_id=params.message.contextId, - task=task, - context=context, - ) - - task_id = cast('str', request_context.task_id) - queue = await self._queue_manager.create_or_tap(task_id) - producer_task = asyncio.create_task( - self._run_event_stream( - request_context, - queue, - ) - ) - await self._register_producer(task_id, producer_task) + ( + task_manager, + task_id, + queue, + result_aggregator, + producer_task, + ) = await self._setup_message_execution(params, context) try: consumer = EventConsumer(queue) producer_task.add_done_callback(consumer.agent_task_callback) async for event in result_aggregator.consume_and_emit(consumer): if isinstance(event, Task): - if task_id != event.id: - logger.error( - f'Agent generated task_id={event.id} does not match the RequestContext task_id={task_id}.' - ) - raise ServerError( - InternalError( - message='Task ID mismatch in agent response' - ) - ) - - if ( - self._push_notifier - and params.configuration - and params.configuration.pushNotificationConfig - ): - await self._push_notifier.set_info( - task_id, - params.configuration.pushNotificationConfig, - ) - - if self._push_notifier and task_id: - latest_task = await result_aggregator.current_result - if isinstance(latest_task, Task): - await self._push_notifier.send_notification(latest_task) + self._validate_task_id_match(task_id, event.id) + + if ( + self._push_notifier + and params.configuration + and params.configuration.pushNotificationConfig + ): + await self._push_notifier.set_info( + task_id, + params.configuration.pushNotificationConfig, + ) + + await self._send_push_notification_if_needed( + task_id, result_aggregator + ) yield event finally: await self._cleanup_producer(producer_task, task_id) @@ -376,7 +390,7 @@ async def on_set_task_push_notification_config( async def on_get_task_push_notification_config( self, - params: TaskIdParams, + params: TaskIdParams | GetTaskPushNotificationConfigParams, context: ServerCallContext | None = None, ) -> TaskPushNotificationConfig: """Default handler for 'tasks/pushNotificationConfig/get'. @@ -412,6 +426,13 @@ async def on_resubscribe_to_task( if not task: raise ServerError(error=TaskNotFoundError()) + if task.status.state in TERMINAL_TASK_STATES: + raise ServerError( + error=InvalidParamsError( + message=f'Task {task.id} is in terminal state: {task.status.state}' + ) + ) + task_manager = TaskManager( task_id=task.id, context_id=task.contextId, diff --git a/src/a2a/server/request_handlers/grpc_handler.py b/src/a2a/server/request_handlers/grpc_handler.py index b8c21070a..d2323115c 100644 --- a/src/a2a/server/request_handlers/grpc_handler.py +++ b/src/a2a/server/request_handlers/grpc_handler.py @@ -6,6 +6,7 @@ from collections.abc import AsyncIterable import grpc +import grpc.aio import a2a.grpc.a2a_pb2_grpc as a2a_grpc @@ -14,10 +15,7 @@ from a2a.grpc import a2a_pb2 from a2a.server.context import ServerCallContext from a2a.server.request_handlers.request_handler import RequestHandler -from a2a.types import ( - AgentCard, - TaskNotFoundError, -) +from a2a.types import AgentCard, TaskNotFoundError from a2a.utils import proto_utils from a2a.utils.errors import ServerError from a2a.utils.helpers import validate, validate_async_generator @@ -32,14 +30,14 @@ class CallContextBuilder(ABC): """A class for building ServerCallContexts using the Starlette Request.""" @abstractmethod - def build(self, context: grpc.ServicerContext) -> ServerCallContext: + def build(self, context: grpc.aio.ServicerContext) -> ServerCallContext: """Builds a ServerCallContext from a gRPC Request.""" class DefaultCallContextBuilder(CallContextBuilder): """A default implementation of CallContextBuilder.""" - def build(self, context: grpc.ServicerContext) -> ServerCallContext: + def build(self, context: grpc.aio.ServicerContext) -> ServerCallContext: """Builds the ServerCallContext.""" user = UnauthenticatedUser() state = {} @@ -55,7 +53,7 @@ def __init__( self, agent_card: AgentCard, request_handler: RequestHandler, - context_builder: CallContextBuilder | None = None + context_builder: CallContextBuilder | None = None, ): """Initializes the GrpcHandler. @@ -198,12 +196,12 @@ async def TaskSubscription( except ServerError as e: await self.abort_context(e, context) - async def GetTaskPushNotification( + async def GetTaskPushNotificationConfig( self, - request: a2a_pb2.GetTaskPushNotificationRequest, + request: a2a_pb2.GetTaskPushNotificationConfigRequest, context: grpc.aio.ServicerContext, ) -> a2a_pb2.TaskPushNotificationConfig: - """Handles the 'GetTaskPushNotification' gRPC method. + """Handles the 'GetTaskPushNotificationConfig' gRPC method. Args: request: The incoming `GetTaskPushNotificationConfigRequest` object. @@ -229,17 +227,17 @@ async def GetTaskPushNotification( lambda self: self.agent_card.capabilities.pushNotifications, 'Push notifications are not supported by the agent', ) - async def CreateTaskPushNotification( + async def CreateTaskPushNotificationConfig( self, - request: a2a_pb2.CreateTaskPushNotificationRequest, + request: a2a_pb2.CreateTaskPushNotificationConfigRequest, context: grpc.aio.ServicerContext, ) -> a2a_pb2.TaskPushNotificationConfig: - """Handles the 'CreateTaskPushNotification' gRPC method. + """Handles the 'CreateTaskPushNotificationConfig' gRPC method. Requires the agent to support push notifications. Args: - request: The incoming `CreateTaskPushNotificationRequest` object. + request: The incoming `CreateTaskPushNotificationConfigRequest` object. context: Context provided by the server. Returns: @@ -301,7 +299,7 @@ async def GetAgentCard( return proto_utils.ToProto.agent_card(self.agent_card) async def abort_context( - self, error: ServerError, context: grpc.ServicerContext + self, error: ServerError, context: grpc.aio.ServicerContext ) -> None: """Sets the grpc errors appropriately in the context.""" match error.error: diff --git a/src/a2a/server/request_handlers/request_handler.py b/src/a2a/server/request_handlers/request_handler.py index 811c8da25..3693d8b6a 100644 --- a/src/a2a/server/request_handlers/request_handler.py +++ b/src/a2a/server/request_handlers/request_handler.py @@ -4,6 +4,7 @@ from a2a.server.context import ServerCallContext from a2a.server.events.event_queue import Event from a2a.types import ( + GetTaskPushNotificationConfigParams, Message, MessageSendParams, Task, @@ -122,7 +123,7 @@ async def on_set_task_push_notification_config( @abstractmethod async def on_get_task_push_notification_config( self, - params: TaskIdParams, + params: TaskIdParams | GetTaskPushNotificationConfigParams, context: ServerCallContext | None = None, ) -> TaskPushNotificationConfig: """Handles the 'tasks/pushNotificationConfig/get' method. diff --git a/src/a2a/server/tasks/task_manager.py b/src/a2a/server/tasks/task_manager.py index ca42b69b9..5474e155a 100644 --- a/src/a2a/server/tasks/task_manager.py +++ b/src/a2a/server/tasks/task_manager.py @@ -107,7 +107,13 @@ async def save_task_event( ) if not self.task_id: self.task_id = task_id_from_event - if not self.context_id and self.context_id != event.contextId: + if self.context_id and self.context_id != event.contextId: + raise ServerError( + error=InvalidParamsError( + message=f"Context in event doesn't match TaskManager {self.context_id} : {event.contextId}" + ) + ) + if not self.context_id: self.context_id = event.contextId logger.debug( @@ -130,7 +136,10 @@ async def save_task_event( task.history = [task.status.message] else: task.history.append(task.status.message) - + if event.metadata: + if not task.metadata: + task.metadata = {} + task.metadata.update(event.metadata) task.status = event.status else: logger.debug('Appending artifact to task %s', task.id) diff --git a/src/a2a/server/tasks/task_updater.py b/src/a2a/server/tasks/task_updater.py index 26b93ba8f..169f3b975 100644 --- a/src/a2a/server/tasks/task_updater.py +++ b/src/a2a/server/tasks/task_updater.py @@ -65,12 +65,14 @@ async def update_status( ) ) - async def add_artifact( + async def add_artifact( # noqa: PLR0913 self, parts: list[Part], artifact_id: str | None = None, name: str | None = None, metadata: dict[str, Any] | None = None, + append: bool | None = None, + last_chunk: bool | None = None, ) -> None: """Adds an artifact chunk to the task and publishes a `TaskArtifactUpdateEvent`. @@ -79,6 +81,8 @@ async def add_artifact( artifact_id: The ID of the artifact. A new UUID is generated if not provided. name: Optional name for the artifact. metadata: Optional metadata for the artifact. + append: Optional boolean indicating if this chunk appends to a previous one. + last_chunk: Optional boolean indicating if this is the last chunk. """ if not artifact_id: artifact_id = str(uuid.uuid4()) @@ -93,6 +97,8 @@ async def add_artifact( parts=parts, metadata=metadata, ), + append=append, + lastChunk=last_chunk ) ) @@ -128,6 +134,30 @@ async def start_work(self, message: Message | None = None) -> None: message=message, ) + async def cancel(self, message: Message | None = None) -> None: + """Marks the task as cancelled and publishes a finalstatus update.""" + await self.update_status( + TaskState.canceled, message=message, final=True + ) + + async def requires_input( + self, message: Message | None = None, final: bool = False + ) -> None: + """Marks the task as input required and publishes a status update.""" + await self.update_status( + TaskState.input_required, + message=message, + final=final, + ) + + async def requires_auth( + self, message: Message | None = None, final: bool = False + ) -> None: + """Marks the task as auth required and publishes a status update.""" + await self.update_status( + TaskState.auth_required, message=message, final=final + ) + def new_agent_message( self, parts: list[Part], diff --git a/src/a2a/types.py b/src/a2a/types.py index 373b579bc..7f9523692 100644 --- a/src/a2a/types.py +++ b/src/a2a/types.py @@ -1,5 +1,5 @@ # generated by datamodel-codegen: -# filename: https://raw.githubusercontent.com/google-a2a/A2A/refs/heads/main/specification/json/a2a.json +# filename: https://raw.githubusercontent.com/a2aproject/A2A/refs/heads/main/specification/json/a2a.json from __future__ import annotations @@ -66,6 +66,21 @@ class AgentExtension(BaseModel): """ +class AgentInterface(BaseModel): + """ + AgentInterface provides a declaration of a combination of the + target url and the supported transport to interact with the agent. + """ + + transport: str + """ + The transport supported this url. This is an open form string, to be + easily extended for many transport protocols. The core ones officially + supported are JSONRPC, GRPC and HTTP+JSON. + """ + url: str + + class AgentProvider(BaseModel): """ Represents the service provider of an agent. @@ -91,7 +106,9 @@ class AgentSkill(BaseModel): Description of the skill - will be used by the client or a human as a hint to understand what the skill does. """ - examples: list[str] | None = None + examples: list[str] | None = Field( + default=None, examples=[['I need a recipe for bread']] + ) """ The set of example scenarios that the skill can perform. Will be used by the client as a hint to understand how the skill can be used. @@ -114,7 +131,7 @@ class AgentSkill(BaseModel): """ Supported media types for output. """ - tags: list[str] + tags: list[str] = Field(..., examples=[['cooking', 'customer support', 'billing']]) """ Set of tagwords describing classes of capabilities for this specific skill. """ @@ -208,6 +225,65 @@ class DataPart(BaseModel): """ +class DeleteTaskPushNotificationConfigParams(BaseModel): + """ + Parameters for removing pushNotificationConfiguration associated with a Task + """ + + id: str + """ + Task id. + """ + metadata: dict[str, Any] | None = None + pushNotificationConfigId: str + + +class DeleteTaskPushNotificationConfigRequest(BaseModel): + """ + JSON-RPC request model for the 'tasks/pushNotificationConfig/delete' method. + """ + + id: str | int + """ + An identifier established by the Client that MUST contain a String, Number. + Numbers SHOULD NOT contain fractional parts. + """ + jsonrpc: Literal['2.0'] = '2.0' + """ + Specifies the version of the JSON-RPC protocol. MUST be exactly "2.0". + """ + method: Literal['tasks/pushNotificationConfig/delete'] = ( + 'tasks/pushNotificationConfig/delete' + ) + """ + A String containing the name of the method to be invoked. + """ + params: DeleteTaskPushNotificationConfigParams + """ + A Structured value that holds the parameter values to be used during the invocation of the method. + """ + + +class DeleteTaskPushNotificationConfigSuccessResponse(BaseModel): + """ + JSON-RPC success response model for the 'tasks/pushNotificationConfig/delete' method. + """ + + id: str | int | None = None + """ + An identifier established by the Client that MUST contain a String, Number. + Numbers SHOULD NOT contain fractional parts. + """ + jsonrpc: Literal['2.0'] = '2.0' + """ + Specifies the version of the JSON-RPC protocol. MUST be exactly "2.0". + """ + result: None + """ + The result object on success. + """ + + class FileBase(BaseModel): """ Represents the base entity for FileParts @@ -261,6 +337,19 @@ class FileWithUri(BaseModel): """ +class GetTaskPushNotificationConfigParams(BaseModel): + """ + Parameters for fetching a pushNotificationConfiguration associated with a Task + """ + + id: str + """ + Task id. + """ + metadata: dict[str, Any] | None = None + pushNotificationConfigId: str | None = None + + class HTTPAuthSecurityScheme(BaseModel): """ HTTP Authentication security scheme. @@ -488,6 +577,44 @@ class JSONRPCSuccessResponse(BaseModel): """ +class ListTaskPushNotificationConfigParams(BaseModel): + """ + Parameters for getting list of pushNotificationConfigurations associated with a Task + """ + + id: str + """ + Task id. + """ + metadata: dict[str, Any] | None = None + + +class ListTaskPushNotificationConfigRequest(BaseModel): + """ + JSON-RPC request model for the 'tasks/pushNotificationConfig/list' method. + """ + + id: str | int + """ + An identifier established by the Client that MUST contain a String, Number. + Numbers SHOULD NOT contain fractional parts. + """ + jsonrpc: Literal['2.0'] = '2.0' + """ + Specifies the version of the JSON-RPC protocol. MUST be exactly "2.0". + """ + method: Literal['tasks/pushNotificationConfig/list'] = ( + 'tasks/pushNotificationConfig/list' + ) + """ + A String containing the name of the method to be invoked. + """ + params: ListTaskPushNotificationConfigParams + """ + A Structured value that holds the parameter values to be used during the invocation of the method. + """ + + class Role(str, Enum): """ Message sender's role @@ -910,9 +1037,10 @@ class GetTaskPushNotificationConfigRequest(BaseModel): """ A String containing the name of the method to be invoked. """ - params: TaskIdParams + params: TaskIdParams | GetTaskPushNotificationConfigParams """ A Structured value that holds the parameter values to be used during the invocation of the method. + TaskIdParams type is deprecated for this method use `GetTaskPushNotificationConfigParams` instead. """ @@ -990,6 +1118,26 @@ class JSONRPCErrorResponse(BaseModel): """ +class ListTaskPushNotificationConfigSuccessResponse(BaseModel): + """ + JSON-RPC success response model for the 'tasks/pushNotificationConfig/list' method. + """ + + id: str | int | None = None + """ + An identifier established by the Client that MUST contain a String, Number. + Numbers SHOULD NOT contain fractional parts. + """ + jsonrpc: Literal['2.0'] = '2.0' + """ + Specifies the version of the JSON-RPC protocol. MUST be exactly "2.0". + """ + result: list[TaskPushNotificationConfig] + """ + The result object on success. + """ + + class MessageSendConfiguration(BaseModel): """ Configuration for the send message request. @@ -1120,6 +1268,15 @@ class Artifact(BaseModel): """ +class DeleteTaskPushNotificationConfigResponse( + RootModel[JSONRPCErrorResponse | DeleteTaskPushNotificationConfigSuccessResponse] +): + root: JSONRPCErrorResponse | DeleteTaskPushNotificationConfigSuccessResponse + """ + JSON-RPC response for the 'tasks/pushNotificationConfig/delete' method. + """ + + class GetTaskPushNotificationConfigResponse( RootModel[JSONRPCErrorResponse | GetTaskPushNotificationConfigSuccessResponse] ): @@ -1129,6 +1286,15 @@ class GetTaskPushNotificationConfigResponse( """ +class ListTaskPushNotificationConfigResponse( + RootModel[JSONRPCErrorResponse | ListTaskPushNotificationConfigSuccessResponse] +): + root: JSONRPCErrorResponse | ListTaskPushNotificationConfigSuccessResponse + """ + JSON-RPC response for the 'tasks/pushNotificationConfig/list' method. + """ + + class Message(BaseModel): """ Represents a single message exchanged between user and agent. @@ -1329,7 +1495,7 @@ class TaskStatus(BaseModel): Additional status updates for client """ state: TaskState - timestamp: str | None = None + timestamp: str | None = Field(default=None, examples=['2023-10-27T10:00:00Z']) """ ISO 8601 datetime string when the status was recorded. """ @@ -1375,6 +1541,8 @@ class A2ARequest( | SetTaskPushNotificationConfigRequest | GetTaskPushNotificationConfigRequest | TaskResubscriptionRequest + | ListTaskPushNotificationConfigRequest + | DeleteTaskPushNotificationConfigRequest ] ): root: ( @@ -1385,6 +1553,8 @@ class A2ARequest( | SetTaskPushNotificationConfigRequest | GetTaskPushNotificationConfigRequest | TaskResubscriptionRequest + | ListTaskPushNotificationConfigRequest + | DeleteTaskPushNotificationConfigRequest ) """ A2A supported request types @@ -1400,6 +1570,11 @@ class AgentCard(BaseModel): - Authentication requirements """ + additionalInterfaces: list[AgentInterface] | None = None + """ + Announcement of additional supported transports. Client can use any of + the supported transports. + """ capabilities: AgentCapabilities """ Optional capabilities supported by the agent. @@ -1413,7 +1588,9 @@ class AgentCard(BaseModel): """ Supported media types for output. """ - description: str + description: str = Field( + ..., examples=['Agent that helps users with recipes and cooking.'] + ) """ A human-readable description of the agent. Used to assist users and other agents in understanding what the agent can do. @@ -1426,10 +1603,18 @@ class AgentCard(BaseModel): """ A URL to an icon for the agent. """ - name: str + name: str = Field(..., examples=['Recipe Agent']) """ Human readable name of the agent. """ + preferredTransport: str | None = None + """ + The transport of the preferred endpoint. If empty, defaults to JSONRPC. + """ + protocolVersion: str | None = '0.2.5' + """ + The version of the A2A protocol this agent supports. + """ provider: AgentProvider | None = None """ The service provider of the agent @@ -1453,9 +1638,10 @@ class AgentCard(BaseModel): """ url: str """ - A URL to the address the agent is hosted at. + A URL to the address the agent is hosted at. This represents the + preferred endpoint as declared by the agent. """ - version: str + version: str = Field(..., examples=['1.0.0']) """ The version of the agent - format is up to the provider. """ @@ -1592,6 +1778,8 @@ class JSONRPCResponse( | CancelTaskSuccessResponse | SetTaskPushNotificationConfigSuccessResponse | GetTaskPushNotificationConfigSuccessResponse + | ListTaskPushNotificationConfigSuccessResponse + | DeleteTaskPushNotificationConfigSuccessResponse ] ): root: ( @@ -1602,6 +1790,8 @@ class JSONRPCResponse( | CancelTaskSuccessResponse | SetTaskPushNotificationConfigSuccessResponse | GetTaskPushNotificationConfigSuccessResponse + | ListTaskPushNotificationConfigSuccessResponse + | DeleteTaskPushNotificationConfigSuccessResponse ) """ Represents a JSON-RPC 2.0 Response object. diff --git a/src/a2a/utils/proto_utils.py b/src/a2a/utils/proto_utils.py index e1dddc393..b67d6033e 100644 --- a/src/a2a/utils/proto_utils.py +++ b/src/a2a/utils/proto_utils.py @@ -15,7 +15,7 @@ # Regexp patterns for matching _TASK_NAME_MATCH = r'tasks/(\w+)' -_TASK_PUSH_CONFIG_NAME_MATCH = r'tasks/(\w+)/pushNotifications/(\w+)' +_TASK_PUSH_CONFIG_NAME_MATCH = r'tasks/(\w+)/pushNotificationConfigs/(\w+)' class ToProto: @@ -143,11 +143,16 @@ def authentication_info( def push_notification_config( cls, config: types.PushNotificationConfig ) -> a2a_pb2.PushNotificationConfig: + auth_info = ( + ToProto.authentication_info(config.authentication) + if config.authentication + else None + ) return a2a_pb2.PushNotificationConfig( id=config.id or '', url=config.url, token=config.token, - authentication=ToProto.authentication_info(config.authentication), + authentication=auth_info, ) @classmethod @@ -185,7 +190,9 @@ def message_send_configuration( accepted_output_modes=list(config.acceptedOutputModes), push_notification=ToProto.push_notification_config( config.pushNotificationConfig - ), + ) + if config.pushNotificationConfig + else None, history_length=config.historyLength, blocking=config.blocking or False, ) @@ -252,7 +259,7 @@ def task_push_notification_config( cls, config: types.TaskPushNotificationConfig ) -> a2a_pb2.TaskPushNotificationConfig: return a2a_pb2.TaskPushNotificationConfig( - name=f'tasks/{config.taskId}/pushNotifications/{config.taskId}', + name=f'tasks/{config.taskId}/pushNotificationConfigs/{config.taskId}', push_notification_config=cls.push_notification_config( config.pushNotificationConfig, ), @@ -276,7 +283,9 @@ def agent_card( skills=[cls.skill(x) for x in card.skills] if card.skills else [], url=card.url, version=card.version, - supports_authenticated_extended_card=card.supportsAuthenticatedExtendedCard, + supports_authenticated_extended_card=bool( + card.supportsAuthenticatedExtendedCard + ), ) @classmethod @@ -284,8 +293,8 @@ def capabilities( cls, capabilities: types.AgentCapabilities ) -> a2a_pb2.AgentCapabilities: return a2a_pb2.AgentCapabilities( - streaming=capabilities.streaming, - push_notifications=capabilities.pushNotifications, + streaming=bool(capabilities.streaming), + push_notifications=bool(capabilities.pushNotifications), ) @classmethod @@ -335,7 +344,7 @@ def security_scheme( return a2a_pb2.SecurityScheme( api_key_security_scheme=a2a_pb2.APIKeySecurityScheme( description=scheme.root.description, - location=scheme.root.in_, + location=scheme.root.in_.value, name=scheme.root.name, ) ) @@ -548,7 +557,9 @@ def push_notification_config( id=config.id, url=config.url, token=config.token, - authentication=FromProto.authentication_info(config.authentication), + authentication=FromProto.authentication_info(config.authentication) + if config.HasField('authentication') + else None, ) @classmethod @@ -568,7 +579,9 @@ def message_send_configuration( acceptedOutputModes=list(config.accepted_output_modes), pushNotificationConfig=FromProto.push_notification_config( config.push_notification - ), + ) + if config.HasField('push_notification') + else None, historyLength=config.history_length, blocking=config.blocking, ) @@ -589,12 +602,12 @@ def task_id_params( request: ( a2a_pb2.CancelTaskRequest | a2a_pb2.TaskSubscriptionRequest - | a2a_pb2.GetTaskPushNotificationRequest + | a2a_pb2.GetTaskPushNotificationConfigRequest ), ) -> types.TaskIdParams: # This is currently incomplete until the core sdk supports multiple # configs for a single task. - if isinstance(request, a2a_pb2.GetTaskPushNotificationRequest): + if isinstance(request, a2a_pb2.GetTaskPushNotificationConfigRequest): m = re.match(_TASK_PUSH_CONFIG_NAME_MATCH, request.name) if not m: raise ServerError( @@ -615,7 +628,7 @@ def task_id_params( @classmethod def task_push_notification_config( cls, - request: a2a_pb2.CreateTaskPushNotificationRequest, + request: a2a_pb2.CreateTaskPushNotificationConfigRequest, ) -> types.TaskPushNotificationConfig: m = re.match(_TASK_NAME_MATCH, request.parent) if not m: @@ -720,7 +733,7 @@ def security_scheme( root=types.APIKeySecurityScheme( description=scheme.api_key_security_scheme.description, name=scheme.api_key_security_scheme.name, - in_=scheme.api_key_security_scheme.location, # type: ignore[call-arg] + in_=types.In(scheme.api_key_security_scheme.location), # type: ignore[call-arg] ) ) if scheme.HasField('http_auth_security_scheme'): diff --git a/tests/client/test_auth_middleware.py b/tests/client/test_auth_middleware.py new file mode 100644 index 000000000..e03b97596 --- /dev/null +++ b/tests/client/test_auth_middleware.py @@ -0,0 +1,384 @@ +from typing import Any + +import httpx +import pytest +import respx + +from a2a.client import A2AClient, ClientCallContext, ClientCallInterceptor +from a2a.client.auth import AuthInterceptor, InMemoryContextCredentialStore +from a2a.types import ( + APIKeySecurityScheme, + AgentCapabilities, + AgentCard, + AuthorizationCodeOAuthFlow, + In, + OAuth2SecurityScheme, + OAuthFlows, + OpenIdConnectSecurityScheme, + SecurityScheme, + SendMessageRequest, +) + + +# A simple mock interceptor for testing basic middleware functionality +class HeaderInterceptor(ClientCallInterceptor): + def __init__(self, header_name: str, header_value: str): + self.header_name = header_name + self.header_value = header_value + + async def intercept( + self, + method_name: str, + request_payload: dict[str, Any], + http_kwargs: dict[str, Any], + agent_card: AgentCard | None, + context: ClientCallContext | None, + ) -> tuple[dict[str, Any], dict[str, Any]]: + headers = http_kwargs.get('headers', {}) + headers[self.header_name] = self.header_value + http_kwargs['headers'] = headers + return request_payload, http_kwargs + + +@pytest.mark.asyncio +@respx.mock +async def test_client_with_simple_interceptor(): + """ + Tests that a basic interceptor is called and successfully + modifies the outgoing request headers. + """ + # Arrange + test_url = 'http://fake-agent.com/rpc' + header_interceptor = HeaderInterceptor('X-Test-Header', 'Test-Value-123') + async with httpx.AsyncClient() as http_client: + client = A2AClient( + httpx_client=http_client, + url=test_url, + interceptors=[header_interceptor], + ) + + # Mock the HTTP response with a minimal valid success response + minimal_success_response = { + 'jsonrpc': '2.0', + 'id': '1', + 'result': { + 'kind': 'message', + 'messageId': 'response-msg', + 'role': 'agent', + 'parts': [], + }, + } + respx.post(test_url).mock( + return_value=httpx.Response(200, json=minimal_success_response) + ) + + # Act + await client.send_message( + request=SendMessageRequest( + id='1', + params={ + 'message': { + 'messageId': 'msg1', + 'role': 'user', + 'parts': [], + } + }, + ) + ) + + # Assert + assert len(respx.calls) == 1 + request = respx.calls.last.request + assert 'x-test-header' in request.headers + assert request.headers['x-test-header'] == 'Test-Value-123' + + +@pytest.mark.asyncio +async def test_in_memory_context_credential_store(): + """ + Tests the functionality of the InMemoryContextCredentialStore to ensure + it correctly stores and retrieves credentials based on sessionId. + """ + # Arrange + store = InMemoryContextCredentialStore() + session_id = 'test-session-123' + scheme_name = 'test-scheme' + credential = 'test-token' + + # Act + await store.set_credentials(session_id, scheme_name, credential) + + # Assert: Successful retrieval + context = ClientCallContext(state={'sessionId': session_id}) + retrieved_credential = await store.get_credentials(scheme_name, context) + assert retrieved_credential == credential + + # Assert: Retrieval with wrong session ID returns None + wrong_context = ClientCallContext(state={'sessionId': 'wrong-session'}) + retrieved_credential_wrong = await store.get_credentials( + scheme_name, wrong_context + ) + assert retrieved_credential_wrong is None + + # Assert: Retrieval with no context returns None + retrieved_credential_none = await store.get_credentials(scheme_name, None) + assert retrieved_credential_none is None + + # Assert: Retrieval with context but no sessionId returns None + empty_context = ClientCallContext(state={}) + retrieved_credential_empty = await store.get_credentials( + scheme_name, empty_context + ) + assert retrieved_credential_empty is None + + +@pytest.mark.asyncio +@respx.mock +async def test_auth_interceptor_with_api_key(): + """ + Tests the authentication flow with an API key in the header. + """ + # Arrange + test_url = 'http://apikey-agent.com/rpc' + session_id = 'user-session-2' + scheme_name = 'apiKeyAuth' + api_key = 'secret-api-key' + + cred_store = InMemoryContextCredentialStore() + await cred_store.set_credentials(session_id, scheme_name, api_key) + + auth_interceptor = AuthInterceptor(credential_service=cred_store) + + api_key_scheme_params = { + 'type': 'apiKey', + 'name': 'X-API-Key', + 'in': In.header, + } + + agent_card = AgentCard( + url=test_url, + name='ApiKeyBot', + description='A bot that requires an API Key', + version='1.0', + defaultInputModes=[], + defaultOutputModes=[], + skills=[], + capabilities=AgentCapabilities(), + security=[{scheme_name: []}], + securitySchemes={ + scheme_name: SecurityScheme( + root=APIKeySecurityScheme(**api_key_scheme_params) + ) + }, + ) + + async with httpx.AsyncClient() as http_client: + client = A2AClient( + httpx_client=http_client, + agent_card=agent_card, + interceptors=[auth_interceptor], + ) + + minimal_success_response = { + 'jsonrpc': '2.0', + 'id': '1', + 'result': { + 'kind': 'message', + 'messageId': 'response-msg', + 'role': 'agent', + 'parts': [], + }, + } + respx.post(test_url).mock( + return_value=httpx.Response(200, json=minimal_success_response) + ) + + # Act + context = ClientCallContext(state={'sessionId': session_id}) + await client.send_message( + request=SendMessageRequest( + id='1', + params={ + 'message': { + 'messageId': 'msg1', + 'role': 'user', + 'parts': [], + } + }, + ), + context=context, + ) + + # Assert + assert len(respx.calls) == 1 + request = respx.calls.last.request + assert 'x-api-key' in request.headers + assert request.headers['x-api-key'] == api_key + + +@pytest.mark.asyncio +@respx.mock +async def test_auth_interceptor_with_oauth2_scheme(): + """ + Tests the AuthInterceptor with an OAuth2 security scheme defined in AgentCard. + Ensures it correctly sets the Authorization: Bearer header. + """ + test_url = 'http://oauth-agent.com/rpc' + session_id = 'user-session-oauth' + scheme_name = 'myOAuthScheme' + access_token = 'secret-oauth-access-token' + + cred_store = InMemoryContextCredentialStore() + await cred_store.set_credentials(session_id, scheme_name, access_token) + + auth_interceptor = AuthInterceptor(credential_service=cred_store) + + oauth_flows = OAuthFlows( + authorizationCode=AuthorizationCodeOAuthFlow( + authorizationUrl='http://provider.com/auth', + tokenUrl='http://provider.com/token', + scopes={'read': 'Read scope'}, + ) + ) + + agent_card = AgentCard( + url=test_url, + name='OAuthBot', + description='A bot that uses OAuth2', + version='1.0', + defaultInputModes=[], + defaultOutputModes=[], + skills=[], + capabilities=AgentCapabilities(), + security=[{scheme_name: ['read']}], + securitySchemes={ + scheme_name: SecurityScheme( + root=OAuth2SecurityScheme(type='oauth2', flows=oauth_flows) + ) + }, + ) + + async with httpx.AsyncClient() as http_client: + client = A2AClient( + httpx_client=http_client, + agent_card=agent_card, + interceptors=[auth_interceptor], + ) + + minimal_success_response = { + 'jsonrpc': '2.0', + 'id': 'oauth_test_1', + 'result': { + 'kind': 'message', + 'messageId': 'response-msg-oauth', + 'role': 'agent', + 'parts': [], + }, + } + respx.post(test_url).mock( + return_value=httpx.Response(200, json=minimal_success_response) + ) + + # Act + context = ClientCallContext(state={'sessionId': session_id}) + await client.send_message( + request=SendMessageRequest( + id='oauth_test_1', + params={ + 'message': { + 'messageId': 'msg-oauth', + 'role': 'user', + 'parts': [], + } + }, + ), + context=context, + ) + + # Assert + assert len(respx.calls) == 1 + request_sent = respx.calls.last.request + assert 'Authorization' in request_sent.headers + assert request_sent.headers['Authorization'] == f'Bearer {access_token}' + + +@pytest.mark.asyncio +@respx.mock +async def test_auth_interceptor_with_oidc_scheme(): + """ + Tests the AuthInterceptor with an OpenIdConnectSecurityScheme. + Ensures it correctly sets the Authorization: Bearer header. + """ + # Arrange + test_url = 'http://oidc-agent.com/rpc' + session_id = 'user-session-oidc' + scheme_name = 'myOidcScheme' + id_token = 'secret-oidc-id-token' + + cred_store = InMemoryContextCredentialStore() + await cred_store.set_credentials(session_id, scheme_name, id_token) + + auth_interceptor = AuthInterceptor(credential_service=cred_store) + + agent_card = AgentCard( + url=test_url, + name='OidcBot', + description='A bot that uses OpenID Connect', + version='1.0', + defaultInputModes=[], + defaultOutputModes=[], + skills=[], + capabilities=AgentCapabilities(), + security=[{scheme_name: []}], + securitySchemes={ + scheme_name: SecurityScheme( + root=OpenIdConnectSecurityScheme( + type='openIdConnect', + openIdConnectUrl='http://provider.com/.well-known/openid-configuration', + ) + ) + }, + ) + + async with httpx.AsyncClient() as http_client: + client = A2AClient( + httpx_client=http_client, + agent_card=agent_card, + interceptors=[auth_interceptor], + ) + + minimal_success_response = { + 'jsonrpc': '2.0', + 'id': 'oidc_test_1', + 'result': { + 'kind': 'message', + 'messageId': 'response-msg-oidc', + 'role': 'agent', + 'parts': [], + }, + } + respx.post(test_url).mock( + return_value=httpx.Response(200, json=minimal_success_response) + ) + + # Act + context = ClientCallContext(state={'sessionId': session_id}) + await client.send_message( + request=SendMessageRequest( + id='oidc_test_1', + params={ + 'message': { + 'messageId': 'msg-oidc', + 'role': 'user', + 'parts': [], + } + }, + ), + context=context, + ) + + # Assert + assert len(respx.calls) == 1 + request_sent = respx.calls.last.request + assert 'Authorization' in request_sent.headers + assert request_sent.headers['Authorization'] == f'Bearer {id_token}' diff --git a/tests/client/test_grpc_client.py b/tests/client/test_grpc_client.py new file mode 100644 index 000000000..ff823342c --- /dev/null +++ b/tests/client/test_grpc_client.py @@ -0,0 +1,145 @@ +from unittest.mock import AsyncMock + +import pytest + +from a2a.client import A2AGrpcClient +from a2a.grpc import a2a_pb2, a2a_pb2_grpc +from a2a.types import ( + AgentCapabilities, + AgentCard, + Message, + MessageSendParams, + Part, + Role, + Task, + TaskIdParams, + TaskQueryParams, + TaskState, + TaskStatus, + TextPart, +) +from a2a.utils import proto_utils + + +# Fixtures +@pytest.fixture +def mock_grpc_stub() -> AsyncMock: + """Provides a mock gRPC stub with methods mocked.""" + stub = AsyncMock(spec=a2a_pb2_grpc.A2AServiceStub) + stub.SendMessage = AsyncMock() + stub.SendStreamingMessage = AsyncMock() + stub.GetTask = AsyncMock() + stub.CancelTask = AsyncMock() + stub.CreateTaskPushNotification = AsyncMock() + stub.GetTaskPushNotification = AsyncMock() + return stub + + +@pytest.fixture +def sample_agent_card() -> AgentCard: + """Provides a minimal agent card for initialization.""" + return AgentCard( + name='gRPC Test Agent', + description='Agent for testing gRPC client', + url='grpc://localhost:50051', + version='1.0', + capabilities=AgentCapabilities(streaming=True, pushNotifications=True), + defaultInputModes=['text/plain'], + defaultOutputModes=['text/plain'], + skills=[], + ) + + +@pytest.fixture +def grpc_client( + mock_grpc_stub: AsyncMock, sample_agent_card: AgentCard +) -> A2AGrpcClient: + """Provides an A2AGrpcClient instance.""" + return A2AGrpcClient(grpc_stub=mock_grpc_stub, agent_card=sample_agent_card) + + +@pytest.fixture +def sample_message_send_params() -> MessageSendParams: + """Provides a sample MessageSendParams object.""" + return MessageSendParams( + message=Message( + role=Role.user, + messageId='msg-1', + parts=[Part(root=TextPart(text='Hello'))], + ) + ) + + +@pytest.fixture +def sample_task() -> Task: + """Provides a sample Task object.""" + return Task( + id='task-1', + contextId='ctx-1', + status=TaskStatus(state=TaskState.completed), + ) + + +@pytest.fixture +def sample_message() -> Message: + """Provides a sample Message object.""" + return Message( + role=Role.agent, + messageId='msg-response', + parts=[Part(root=TextPart(text='Hi there'))], + ) + + +@pytest.mark.asyncio +async def test_send_message_task_response( + grpc_client: A2AGrpcClient, + mock_grpc_stub: AsyncMock, + sample_message_send_params: MessageSendParams, + sample_task: Task, +): + """Test send_message that returns a Task.""" + mock_grpc_stub.SendMessage.return_value = a2a_pb2.SendMessageResponse( + task=proto_utils.ToProto.task(sample_task) + ) + + response = await grpc_client.send_message(sample_message_send_params) + + mock_grpc_stub.SendMessage.assert_awaited_once() + assert isinstance(response, Task) + assert response.id == sample_task.id + + +@pytest.mark.asyncio +async def test_get_task( + grpc_client: A2AGrpcClient, mock_grpc_stub: AsyncMock, sample_task: Task +): + """Test retrieving a task.""" + mock_grpc_stub.GetTask.return_value = proto_utils.ToProto.task(sample_task) + params = TaskQueryParams(id=sample_task.id) + + response = await grpc_client.get_task(params) + + mock_grpc_stub.GetTask.assert_awaited_once_with( + a2a_pb2.GetTaskRequest(name=f'tasks/{sample_task.id}') + ) + assert response.id == sample_task.id + + +@pytest.mark.asyncio +async def test_cancel_task( + grpc_client: A2AGrpcClient, mock_grpc_stub: AsyncMock, sample_task: Task +): + """Test cancelling a task.""" + cancelled_task = sample_task.model_copy() + cancelled_task.status.state = TaskState.canceled + mock_grpc_stub.CancelTask.return_value = proto_utils.ToProto.task( + cancelled_task + ) + params = TaskIdParams(id=sample_task.id) + + response = await grpc_client.cancel_task(params) + + mock_grpc_stub.CancelTask.assert_awaited_once_with( + a2a_pb2.CancelTaskRequest(name=f'tasks/{sample_task.id}') + ) + assert response.status.state == TaskState.canceled diff --git a/tests/server/agent_execution/test_context.py b/tests/server/agent_execution/test_context.py index 92d097073..ea90631cf 100644 --- a/tests/server/agent_execution/test_context.py +++ b/tests/server/agent_execution/test_context.py @@ -10,6 +10,7 @@ MessageSendParams, Task, ) +from a2a.utils.errors import ServerError class TestRequestContext: @@ -165,6 +166,33 @@ def test_check_or_generate_context_id_with_existing_context_id( assert context.context_id == existing_id assert mock_params.message.contextId == existing_id + def test_init_raises_error_on_task_id_mismatch( + self, mock_params, mock_task + ): + """Test that an error is raised if provided task_id mismatches task.id.""" + with pytest.raises(ServerError) as exc_info: + RequestContext( + request=mock_params, task_id='wrong-task-id', task=mock_task + ) + assert 'bad task id' in str(exc_info.value.error.message) + + def test_init_raises_error_on_context_id_mismatch( + self, mock_params, mock_task + ): + """Test that an error is raised if provided context_id mismatches task.contextId.""" + # Set a valid task_id to avoid that error + mock_params.message.taskId = mock_task.id + + with pytest.raises(ServerError) as exc_info: + RequestContext( + request=mock_params, + task_id=mock_task.id, + context_id='wrong-context-id', + task=mock_task, + ) + + assert 'bad context id' in str(exc_info.value.error.message) + def test_with_related_tasks_provided(self, mock_task): """Test initialization with related tasks provided.""" related_tasks = [mock_task, Mock(spec=Task)] diff --git a/tests/server/apps/jsonrpc/test_jsonrpc_app.py b/tests/server/apps/jsonrpc/test_jsonrpc_app.py index 8fc4e8a85..6670e40be 100644 --- a/tests/server/apps/jsonrpc/test_jsonrpc_app.py +++ b/tests/server/apps/jsonrpc/test_jsonrpc_app.py @@ -70,17 +70,20 @@ def test_jsonrpc_app_build_method_abstract_raises_typeerror( # Ensure 'supportsAuthenticatedExtendedCard' attribute exists mock_agent_card.supportsAuthenticatedExtendedCard = False - class AbstractTester(JSONRPCApplication): - # No 'build' method implemented - pass - - # Instantiating an ABC subclass that doesn't implement all abstract methods raises TypeError + # This will fail at definition time if an abstract method is not implemented with pytest.raises( TypeError, - match="Can't instantiate abstract class AbstractTester with abstract method build", + match="Can't instantiate abstract class IncompleteJSONRPCApp with abstract method build", ): - # Using positional arguments for the abstract class constructor - AbstractTester(mock_handler, mock_agent_card) + + class IncompleteJSONRPCApp(JSONRPCApplication): + # Intentionally not implementing 'build' + def some_other_method(self): + pass + + IncompleteJSONRPCApp( + agent_card=mock_agent_card, http_handler=mock_handler + ) if __name__ == '__main__': diff --git a/tests/server/apps/jsonrpc/test_serialization.py b/tests/server/apps/jsonrpc/test_serialization.py new file mode 100644 index 000000000..ea3da1c09 --- /dev/null +++ b/tests/server/apps/jsonrpc/test_serialization.py @@ -0,0 +1,94 @@ +from unittest import mock + +import pytest +from starlette.testclient import TestClient + +from a2a.server.apps import A2AFastAPIApplication, A2AStarletteApplication +from a2a.types import ( + APIKeySecurityScheme, + AgentCapabilities, + AgentCard, + In, + SecurityScheme, +) +from pydantic import ValidationError + + +@pytest.fixture +def agent_card_with_api_key(): + """Provides an AgentCard with an APIKeySecurityScheme for testing serialization.""" + # This data uses the alias 'in', which is correct for creating the model. + api_key_scheme_data = { + 'type': 'apiKey', + 'name': 'X-API-KEY', + 'in': 'header', + } + api_key_scheme = APIKeySecurityScheme.model_validate(api_key_scheme_data) + + agent_card = AgentCard( + name='APIKeyAgent', + description='An agent that uses API Key auth.', + url='http://example.com/apikey-agent', + version='1.0.0', + capabilities=AgentCapabilities(), + defaultInputModes=['text/plain'], + defaultOutputModes=['text/plain'], + skills=[], + securitySchemes={'api_key_auth': SecurityScheme(root=api_key_scheme)}, + security=[{'api_key_auth': []}], + ) + return agent_card + + +def test_starlette_agent_card_with_api_key_scheme_alias( + agent_card_with_api_key: AgentCard, +): + """ + Tests that the A2AStarletteApplication endpoint correctly serializes aliased fields. + + This verifies the fix for `APIKeySecurityScheme.in_` being serialized as `in_` instead of `in`. + """ + handler = mock.AsyncMock() + app_instance = A2AStarletteApplication(agent_card_with_api_key, handler) + client = TestClient(app_instance.build()) + + response = client.get('/.well-known/agent.json') + assert response.status_code == 200 + response_data = response.json() + + security_scheme_json = response_data['securitySchemes']['api_key_auth'] + assert 'in' in security_scheme_json + assert security_scheme_json['in'] == 'header' + assert 'in_' not in security_scheme_json + + try: + parsed_card = AgentCard.model_validate(response_data) + parsed_scheme_wrapper = parsed_card.securitySchemes['api_key_auth'] + assert isinstance(parsed_scheme_wrapper.root, APIKeySecurityScheme) + assert parsed_scheme_wrapper.root.in_ == In.header + except ValidationError as e: + pytest.fail( + f"AgentCard.model_validate failed on the server's response: {e}" + ) + + +def test_fastapi_agent_card_with_api_key_scheme_alias( + agent_card_with_api_key: AgentCard, +): + """ + Tests that the A2AFastAPIApplication endpoint correctly serializes aliased fields. + + This verifies the fix for `APIKeySecurityScheme.in_` being serialized as `in_` instead of `in`. + """ + handler = mock.AsyncMock() + app_instance = A2AFastAPIApplication(agent_card_with_api_key, handler) + client = TestClient(app_instance.build()) + + response = client.get('/.well-known/agent.json') + assert response.status_code == 200 + response_data = response.json() + + security_scheme_json = response_data['securitySchemes']['api_key_auth'] + assert 'in' in security_scheme_json + assert 'in_' not in security_scheme_json + assert security_scheme_json['in'] == 'header' diff --git a/tests/server/request_handlers/test_default_request_handler.py b/tests/server/request_handlers/test_default_request_handler.py index e30b843c8..dd713752c 100644 --- a/tests/server/request_handlers/test_default_request_handler.py +++ b/tests/server/request_handlers/test_default_request_handler.py @@ -28,6 +28,7 @@ ) from a2a.types import ( InternalError, + InvalidParamsError, Message, MessageSendConfiguration, MessageSendParams, @@ -360,6 +361,15 @@ async def test_on_message_send_with_push_notification(): False, ) + # Mock the current_result property to return the final task result + async def get_current_result(): + return final_task_result + + # Configure the 'current_result' property on the type of the mock instance + type(mock_result_aggregator_instance).current_result = PropertyMock( + return_value=get_current_result() + ) + with ( patch( 'a2a.server.request_handlers.default_request_handler.ResultAggregator', @@ -379,6 +389,9 @@ async def test_on_message_send_with_push_notification(): ) mock_push_notifier.set_info.assert_awaited_once_with(task_id, push_config) + mock_push_notifier.send_notification.assert_awaited_once_with( + final_task_result + ) # Other assertions for full flow if needed (e.g., agent execution) mock_agent_executor.execute.assert_awaited_once() @@ -1137,3 +1150,138 @@ async def consume_stream(): texts = [p.root.text for e in events for p in e.status.message.parts] assert texts == ['Event 0', 'Event 1', 'Event 2'] + + +TERMINAL_TASK_STATES = { + TaskState.completed, + TaskState.canceled, + TaskState.failed, + TaskState.rejected, +} + + +@pytest.mark.asyncio +@pytest.mark.parametrize('terminal_state', TERMINAL_TASK_STATES) +async def test_on_message_send_task_in_terminal_state(terminal_state): + """Test on_message_send when task is already in a terminal state.""" + task_id = f'terminal_task_{terminal_state.value}' + terminal_task = create_sample_task( + task_id=task_id, status_state=terminal_state + ) + + mock_task_store = AsyncMock(spec=TaskStore) + # The get method of TaskManager calls task_store.get. + # We mock TaskManager.get_task which is an async method. + # So we should patch that instead. + + request_handler = DefaultRequestHandler( + agent_executor=DummyAgentExecutor(), task_store=mock_task_store + ) + + params = MessageSendParams( + message=Message( + role=Role.user, + messageId='msg_terminal', + parts=[], + taskId=task_id, + ) + ) + + from a2a.utils.errors import ServerError + + # Patch the TaskManager's get_task method to return our terminal task + with patch( + 'a2a.server.request_handlers.default_request_handler.TaskManager.get_task', + return_value=terminal_task, + ): + with pytest.raises(ServerError) as exc_info: + await request_handler.on_message_send( + params, create_server_call_context() + ) + + assert isinstance(exc_info.value.error, InvalidParamsError) + assert exc_info.value.error.message + assert ( + f'Task {task_id} is in terminal state: {terminal_state.value}' + in exc_info.value.error.message + ) + + +@pytest.mark.asyncio +@pytest.mark.parametrize('terminal_state', TERMINAL_TASK_STATES) +async def test_on_message_send_stream_task_in_terminal_state(terminal_state): + """Test on_message_send_stream when task is already in a terminal state.""" + task_id = f'terminal_stream_task_{terminal_state.value}' + terminal_task = create_sample_task( + task_id=task_id, status_state=terminal_state + ) + + mock_task_store = AsyncMock(spec=TaskStore) + + request_handler = DefaultRequestHandler( + agent_executor=DummyAgentExecutor(), task_store=mock_task_store + ) + + params = MessageSendParams( + message=Message( + role=Role.user, + messageId='msg_terminal_stream', + parts=[], + taskId=task_id, + ) + ) + + from a2a.utils.errors import ServerError + + with patch( + 'a2a.server.request_handlers.default_request_handler.TaskManager.get_task', + return_value=terminal_task, + ): + with pytest.raises(ServerError) as exc_info: + async for _ in request_handler.on_message_send_stream( + params, create_server_call_context() + ): + pass # pragma: no cover + + assert isinstance(exc_info.value.error, InvalidParamsError) + assert exc_info.value.error.message + assert ( + f'Task {task_id} is in terminal state: {terminal_state.value}' + in exc_info.value.error.message + ) + + +@pytest.mark.asyncio +@pytest.mark.parametrize('terminal_state', TERMINAL_TASK_STATES) +async def test_on_resubscribe_to_task_in_terminal_state(terminal_state): + """Test on_resubscribe_to_task when task is in a terminal state.""" + task_id = f'resub_terminal_task_{terminal_state.value}' + terminal_task = create_sample_task( + task_id=task_id, status_state=terminal_state + ) + + mock_task_store = AsyncMock(spec=TaskStore) + mock_task_store.get.return_value = terminal_task + + request_handler = DefaultRequestHandler( + agent_executor=DummyAgentExecutor(), + task_store=mock_task_store, + queue_manager=AsyncMock(spec=QueueManager), + ) + params = TaskIdParams(id=task_id) + + from a2a.utils.errors import ServerError + + with pytest.raises(ServerError) as exc_info: + async for _ in request_handler.on_resubscribe_to_task( + params, create_server_call_context() + ): + pass # pragma: no cover + + assert isinstance(exc_info.value.error, InvalidParamsError) + assert exc_info.value.error.message + assert ( + f'Task {task_id} is in terminal state: {terminal_state.value}' + in exc_info.value.error.message + ) + mock_task_store.get.assert_awaited_once_with(task_id) diff --git a/tests/server/request_handlers/test_grpc_handler.py b/tests/server/request_handlers/test_grpc_handler.py index 016e05bf1..e1b8b9403 100644 --- a/tests/server/request_handlers/test_grpc_handler.py +++ b/tests/server/request_handlers/test_grpc_handler.py @@ -195,3 +195,87 @@ async def test_get_agent_card( assert response.name == sample_agent_card.name assert response.version == sample_agent_card.version + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + 'server_error, grpc_status_code, error_message_part', + [ + ( + ServerError(error=types.JSONParseError()), + grpc.StatusCode.INTERNAL, + 'JSONParseError', + ), + ( + ServerError(error=types.InvalidRequestError()), + grpc.StatusCode.INVALID_ARGUMENT, + 'InvalidRequestError', + ), + ( + ServerError(error=types.MethodNotFoundError()), + grpc.StatusCode.NOT_FOUND, + 'MethodNotFoundError', + ), + ( + ServerError(error=types.InvalidParamsError()), + grpc.StatusCode.INVALID_ARGUMENT, + 'InvalidParamsError', + ), + ( + ServerError(error=types.InternalError()), + grpc.StatusCode.INTERNAL, + 'InternalError', + ), + ( + ServerError(error=types.TaskNotFoundError()), + grpc.StatusCode.NOT_FOUND, + 'TaskNotFoundError', + ), + ( + ServerError(error=types.TaskNotCancelableError()), + grpc.StatusCode.UNIMPLEMENTED, + 'TaskNotCancelableError', + ), + ( + ServerError(error=types.PushNotificationNotSupportedError()), + grpc.StatusCode.UNIMPLEMENTED, + 'PushNotificationNotSupportedError', + ), + ( + ServerError(error=types.UnsupportedOperationError()), + grpc.StatusCode.UNIMPLEMENTED, + 'UnsupportedOperationError', + ), + ( + ServerError(error=types.ContentTypeNotSupportedError()), + grpc.StatusCode.UNIMPLEMENTED, + 'ContentTypeNotSupportedError', + ), + ( + ServerError(error=types.InvalidAgentResponseError()), + grpc.StatusCode.INTERNAL, + 'InvalidAgentResponseError', + ), + ( + ServerError(error=types.JSONRPCError(code=99, message='Unknown')), + grpc.StatusCode.UNKNOWN, + 'Unknown error', + ), + ], +) +async def test_abort_context_error_mapping( + grpc_handler: GrpcHandler, + mock_request_handler: AsyncMock, + mock_grpc_context: AsyncMock, + server_error, + grpc_status_code, + error_message_part, +): + mock_request_handler.on_get_task.side_effect = server_error + request_proto = a2a_pb2.GetTaskRequest(name='tasks/any') + await grpc_handler.GetTask(request_proto, mock_grpc_context) + + mock_grpc_context.abort.assert_awaited_once() + call_args, _ = mock_grpc_context.abort.call_args + assert call_args[0] == grpc_status_code + assert error_message_part in call_args[1] diff --git a/tests/server/tasks/test_result_aggregator.py b/tests/server/tasks/test_result_aggregator.py index 081d5ac0d..025c940b2 100644 --- a/tests/server/tasks/test_result_aggregator.py +++ b/tests/server/tasks/test_result_aggregator.py @@ -1,3 +1,4 @@ +import asyncio import unittest from collections.abc import AsyncIterator @@ -252,6 +253,7 @@ async def mock_consume_generator(): # Mock _continue_consuming to check if it's called by create_task self.aggregator._continue_consuming = AsyncMock() + mock_create_task.side_effect = lambda coro: asyncio.ensure_future(coro) ( result, @@ -303,6 +305,7 @@ async def mock_consume_generator(): current_task_state_after_update ) self.aggregator._continue_consuming = AsyncMock() + mock_create_task.side_effect = lambda coro: asyncio.ensure_future(coro) ( result, @@ -417,6 +420,7 @@ async def initial_consume_generator(): self.mock_task_manager.get_task.return_value = ( auth_event # Task state at interrupt ) + mock_create_task.side_effect = lambda coro: asyncio.ensure_future(coro) # Call the main method that triggers _continue_consuming via create_task _, _ = await self.aggregator.consume_and_break_on_interrupt( diff --git a/tests/server/tasks/test_task_manager.py b/tests/server/tasks/test_task_manager.py index 56205fab6..952979710 100644 --- a/tests/server/tasks/test_task_manager.py +++ b/tests/server/tasks/test_task_manager.py @@ -6,6 +6,7 @@ from a2a.server.tasks import TaskManager from a2a.types import ( Artifact, + InvalidParamsError, Message, Part, Role, @@ -16,6 +17,7 @@ TaskStatusUpdateEvent, TextPart, ) +from a2a.utils.errors import ServerError MINIMAL_TASK: dict[str, Any] = { @@ -126,6 +128,28 @@ async def test_save_task_event_artifact_update( mock_task_store.save.assert_called_once_with(updated_task) +@pytest.mark.asyncio +async def test_save_task_event_metadata_update( + task_manager: TaskManager, mock_task_store: AsyncMock +) -> None: + """Test saving an updated metadata for an existing task.""" + initial_task = Task(**MINIMAL_TASK) + mock_task_store.get.return_value = initial_task + new_metadata = {'meta_key_test': 'meta_value_test'} + + event = TaskStatusUpdateEvent( + taskId=MINIMAL_TASK['id'], + contextId=MINIMAL_TASK['contextId'], + metadata=new_metadata, + status=TaskStatus(state=TaskState.working), + final=False, + ) + await task_manager.save_task_event(event) + + updated_task = mock_task_store.save.call_args.args[0] + assert updated_task.metadata == new_metadata + + @pytest.mark.asyncio async def test_ensure_task_existing( task_manager: TaskManager, mock_task_store: AsyncMock @@ -190,6 +214,23 @@ async def test_save_task( mock_task_store.save.assert_called_once_with(task) +@pytest.mark.asyncio +async def test_save_task_event_mismatched_id_raises_error( + task_manager: TaskManager, +) -> None: + """Test that save_task_event raises ServerError on task ID mismatch.""" + # The task_manager is initialized with 'task-abc' + mismatched_task = Task( + id='wrong-id', + contextId='session-xyz', + status=TaskStatus(state=TaskState.submitted), + ) + + with pytest.raises(ServerError) as exc_info: + await task_manager.save_task_event(mismatched_task) + assert isinstance(exc_info.value.error, InvalidParamsError) + + @pytest.mark.asyncio async def test_save_task_event_new_task_no_task_id( mock_task_store: AsyncMock, diff --git a/tests/server/tasks/test_task_updater.py b/tests/server/tasks/test_task_updater.py index 8b105b7b5..2a8d5deb6 100644 --- a/tests/server/tasks/test_task_updater.py +++ b/tests/server/tasks/test_task_updater.py @@ -146,6 +146,39 @@ async def test_add_artifact_generates_id( assert isinstance(event, TaskArtifactUpdateEvent) assert event.artifact.artifactId == str(known_uuid) assert event.artifact.parts == sample_parts + assert event.append == None + assert event.lastChunk == None + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + 'append_val, last_chunk_val', + [ + (False, False), + (True, True), + (True, False), + (False, True), + ], +) +async def test_add_artifact_with_append_last_chunk( + task_updater, event_queue, sample_parts, append_val, last_chunk_val +): + """Test add_artifact with append and last_chunk flags.""" + await task_updater.add_artifact( + parts=sample_parts, + artifact_id='id1', + append=append_val, + last_chunk=last_chunk_val, + ) + + event_queue.enqueue_event.assert_called_once() + event = event_queue.enqueue_event.call_args[0][0] + + assert isinstance(event, TaskArtifactUpdateEvent) + assert event.artifact.artifactId == 'id1' + assert event.artifact.parts == sample_parts + assert event.append == append_val + assert event.lastChunk == last_chunk_val @pytest.mark.asyncio @@ -324,3 +357,151 @@ async def test_reject_with_message(task_updater, event_queue, sample_message): assert event.status.state == TaskState.rejected assert event.final is True assert event.status.message == sample_message + + +@pytest.mark.asyncio +async def test_requires_input_without_message(task_updater, event_queue): + """Test marking a task as input required without a message.""" + await task_updater.requires_input() + + event_queue.enqueue_event.assert_called_once() + event = event_queue.enqueue_event.call_args[0][0] + + assert isinstance(event, TaskStatusUpdateEvent) + assert event.status.state == TaskState.input_required + assert event.final is False + assert event.status.message is None + + +@pytest.mark.asyncio +async def test_requires_input_with_message( + task_updater, event_queue, sample_message +): + """Test marking a task as input required with a message.""" + await task_updater.requires_input(message=sample_message) + + event_queue.enqueue_event.assert_called_once() + event = event_queue.enqueue_event.call_args[0][0] + + assert isinstance(event, TaskStatusUpdateEvent) + assert event.status.state == TaskState.input_required + assert event.final is False + assert event.status.message == sample_message + + +@pytest.mark.asyncio +async def test_requires_input_final_true(task_updater, event_queue): + """Test marking a task as input required with final=True.""" + await task_updater.requires_input(final=True) + + event_queue.enqueue_event.assert_called_once() + event = event_queue.enqueue_event.call_args[0][0] + + assert isinstance(event, TaskStatusUpdateEvent) + assert event.status.state == TaskState.input_required + assert event.final is True + assert event.status.message is None + + +@pytest.mark.asyncio +async def test_requires_input_with_message_and_final( + task_updater, event_queue, sample_message +): + """Test marking a task as input required with message and final=True.""" + await task_updater.requires_input(message=sample_message, final=True) + + event_queue.enqueue_event.assert_called_once() + event = event_queue.enqueue_event.call_args[0][0] + + assert isinstance(event, TaskStatusUpdateEvent) + assert event.status.state == TaskState.input_required + assert event.final is True + assert event.status.message == sample_message + + +@pytest.mark.asyncio +async def test_requires_auth_without_message(task_updater, event_queue): + """Test marking a task as auth required without a message.""" + await task_updater.requires_auth() + + event_queue.enqueue_event.assert_called_once() + event = event_queue.enqueue_event.call_args[0][0] + + assert isinstance(event, TaskStatusUpdateEvent) + assert event.status.state == TaskState.auth_required + assert event.final is False + assert event.status.message is None + + +@pytest.mark.asyncio +async def test_requires_auth_with_message( + task_updater, event_queue, sample_message +): + """Test marking a task as auth required with a message.""" + await task_updater.requires_auth(message=sample_message) + + event_queue.enqueue_event.assert_called_once() + event = event_queue.enqueue_event.call_args[0][0] + + assert isinstance(event, TaskStatusUpdateEvent) + assert event.status.state == TaskState.auth_required + assert event.final is False + assert event.status.message == sample_message + + +@pytest.mark.asyncio +async def test_requires_auth_final_true(task_updater, event_queue): + """Test marking a task as auth required with final=True.""" + await task_updater.requires_auth(final=True) + + event_queue.enqueue_event.assert_called_once() + event = event_queue.enqueue_event.call_args[0][0] + + assert isinstance(event, TaskStatusUpdateEvent) + assert event.status.state == TaskState.auth_required + assert event.final is True + assert event.status.message is None + + +@pytest.mark.asyncio +async def test_requires_auth_with_message_and_final( + task_updater, event_queue, sample_message +): + """Test marking a task as auth required with message and final=True.""" + await task_updater.requires_auth(message=sample_message, final=True) + + event_queue.enqueue_event.assert_called_once() + event = event_queue.enqueue_event.call_args[0][0] + + assert isinstance(event, TaskStatusUpdateEvent) + assert event.status.state == TaskState.auth_required + assert event.final is True + assert event.status.message == sample_message + + +@pytest.mark.asyncio +async def test_cancel_without_message(task_updater, event_queue): + """Test marking a task as cancelled without a message.""" + await task_updater.cancel() + + event_queue.enqueue_event.assert_called_once() + event = event_queue.enqueue_event.call_args[0][0] + + assert isinstance(event, TaskStatusUpdateEvent) + assert event.status.state == TaskState.canceled + assert event.final is True + assert event.status.message is None + + +@pytest.mark.asyncio +async def test_cancel_with_message(task_updater, event_queue, sample_message): + """Test marking a task as cancelled with a message.""" + await task_updater.cancel(message=sample_message) + + event_queue.enqueue_event.assert_called_once() + event = event_queue.enqueue_event.call_args[0][0] + + assert isinstance(event, TaskStatusUpdateEvent) + assert event.status.state == TaskState.canceled + assert event.final is True + assert event.status.message == sample_message diff --git a/tests/utils/test_message.py b/tests/utils/test_message.py index 6851a3ca4..55a81ec94 100644 --- a/tests/utils/test_message.py +++ b/tests/utils/test_message.py @@ -3,12 +3,18 @@ from unittest.mock import patch from a2a.types import ( + DataPart, Message, Part, Role, TextPart, ) -from a2a.utils import get_message_text, get_text_parts, new_agent_text_message +from a2a.utils.message import ( + get_message_text, + get_text_parts, + new_agent_parts_message, + new_agent_text_message, +) class TestNewAgentTextMessage: @@ -108,6 +114,34 @@ def test_new_agent_text_message_empty_text(self): assert message.messageId == '12345678-1234-5678-1234-567812345678' +class TestNewAgentPartsMessage: + def test_new_agent_parts_message(self): + """Test creating an agent message with multiple, mixed parts.""" + # Setup + parts = [ + Part(root=TextPart(text='Here is some text.')), + Part(root=DataPart(data={'product_id': 123, 'quantity': 2})), + ] + context_id = 'ctx-multi-part' + task_id = 'task-multi-part' + + # Exercise + with patch( + 'uuid.uuid4', + return_value=uuid.UUID('abcdefab-cdef-abcd-efab-cdefabcdefab'), + ): + message = new_agent_parts_message( + parts, context_id=context_id, task_id=task_id + ) + + # Verify + assert message.role == Role.agent + assert message.parts == parts + assert message.contextId == context_id + assert message.taskId == task_id + assert message.messageId == 'abcdefab-cdef-abcd-efab-cdefabcdefab' + + class TestGetTextParts: def test_get_text_parts_single_text_part(self): # Setup diff --git a/tests/utils/test_proto_utils.py b/tests/utils/test_proto_utils.py index b8db386c4..b1a73ffff 100644 --- a/tests/utils/test_proto_utils.py +++ b/tests/utils/test_proto_utils.py @@ -1,8 +1,11 @@ +from unittest import mock + import pytest from a2a import types from a2a.grpc import a2a_pb2 from a2a.utils import proto_utils +from a2a.utils.errors import ServerError # --- Test Data --- @@ -106,6 +109,37 @@ def sample_agent_card() -> types.AgentCard: # --- Test Cases --- +class TestToProto: + def test_part_unsupported_type(self): + """Test that ToProto.part raises ValueError for an unsupported Part type.""" + + class FakePartType: + kind = 'fake' + + # Create a mock Part object that has a .root attribute pointing to the fake type + mock_part = mock.MagicMock(spec=types.Part) + mock_part.root = FakePartType() + + with pytest.raises(ValueError, match='Unsupported part type'): + proto_utils.ToProto.part(mock_part) + + +class TestFromProto: + def test_part_unsupported_type(self): + """Test that FromProto.part raises ValueError for an unsupported part type in proto.""" + unsupported_proto_part = ( + a2a_pb2.Part() + ) # An empty part with no oneof field set + with pytest.raises(ValueError, match='Unsupported part type'): + proto_utils.FromProto.part(unsupported_proto_part) + + def test_task_query_params_invalid_name(self): + request = a2a_pb2.GetTaskRequest(name='invalid-name-format') + with pytest.raises(ServerError) as exc_info: + proto_utils.FromProto.task_query_params(request) + assert isinstance(exc_info.value.error, types.InvalidParamsError) + + class TestProtoUtils: def test_roundtrip_message(self, sample_message: types.Message): """Test conversion of Message to proto and back.""" @@ -130,10 +164,10 @@ def test_enum_conversions(self): ) for state in types.TaskState: - if ( - state != types.TaskState.unknown - and state != types.TaskState.rejected - and state != types.TaskState.auth_required + if state not in ( + types.TaskState.unknown, + types.TaskState.rejected, + types.TaskState.auth_required, ): proto_state = proto_utils.ToProto.task_state(state) assert proto_utils.FromProto.task_state(proto_state) == state @@ -196,6 +230,20 @@ def test_oauth_flows_conversion(self): ) assert roundtrip_implicit.implicit is not None + def test_task_id_params_from_proto_invalid_name(self): + request = a2a_pb2.CancelTaskRequest(name='invalid-name-format') + with pytest.raises(ServerError) as exc_info: + proto_utils.FromProto.task_id_params(request) + assert isinstance(exc_info.value.error, types.InvalidParamsError) + + def test_task_push_config_from_proto_invalid_parent(self): + request = a2a_pb2.CreateTaskPushNotificationConfigRequest( + parent='invalid-parent' + ) + with pytest.raises(ServerError) as exc_info: + proto_utils.FromProto.task_push_notification_config(request) + assert isinstance(exc_info.value.error, types.InvalidParamsError) + def test_none_handling(self): """Test that None inputs are handled gracefully.""" assert proto_utils.ToProto.message(None) is None diff --git a/uv.lock b/uv.lock index c62060260..7bdb8310a 100644 --- a/uv.lock +++ b/uv.lock @@ -1,11 +1,9 @@ version = 1 -revision = 1 +revision = 2 requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.13'", - "python_full_version >= '3.12.4' and python_full_version < '3.13'", - "python_full_version >= '3.11' and python_full_version < '3.12.4'", - "python_full_version < '3.11'", + "python_full_version < '3.13'", ] [[package]] @@ -13,10 +11,15 @@ name = "a2a-sdk" source = { editable = "." } dependencies = [ { name = "fastapi" }, + { name = "google-api-core" }, + { name = "grpcio" }, + { name = "grpcio-reflection" }, + { name = "grpcio-tools" }, { name = "httpx" }, { name = "httpx-sse" }, { name = "opentelemetry-api" }, { name = "opentelemetry-sdk" }, + { name = "protobuf" }, { name = "pydantic" }, { name = "sse-starlette" }, { name = "starlette" }, @@ -26,35 +29,48 @@ dependencies = [ dev = [ { name = "datamodel-code-generator" }, { name = "mypy" }, + { name = "pre-commit" }, { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-cov" }, { name = "pytest-mock" }, + { name = "respx" }, { name = "ruff" }, + { name = "types-protobuf" }, + { name = "types-requests" }, { name = "uv-dynamic-versioning" }, ] [package.metadata] requires-dist = [ - { name = "fastapi", specifier = ">=0.115.12" }, + { name = "fastapi", specifier = ">=0.115.2" }, + { name = "google-api-core", specifier = ">=1.26.0" }, + { name = "grpcio", specifier = ">=1.60" }, + { name = "grpcio-reflection", specifier = ">=1.7.0" }, + { name = "grpcio-tools", specifier = ">=1.60" }, { name = "httpx", specifier = ">=0.28.1" }, { name = "httpx-sse", specifier = ">=0.4.0" }, { name = "opentelemetry-api", specifier = ">=1.33.0" }, { name = "opentelemetry-sdk", specifier = ">=1.33.0" }, + { name = "protobuf", specifier = "==5.29.5" }, { name = "pydantic", specifier = ">=2.11.3" }, - { name = "sse-starlette", specifier = ">=2.3.3" }, - { name = "starlette", specifier = ">=0.46.2" }, + { name = "sse-starlette" }, + { name = "starlette" }, ] [package.metadata.requires-dev] dev = [ { name = "datamodel-code-generator", specifier = ">=0.30.0" }, { name = "mypy", specifier = ">=1.15.0" }, + { name = "pre-commit" }, { name = "pytest", specifier = ">=8.3.5" }, { name = "pytest-asyncio", specifier = ">=0.26.0" }, { name = "pytest-cov", specifier = ">=6.1.1" }, { name = "pytest-mock", specifier = ">=3.14.0" }, + { name = "respx", specifier = ">=0.20.2" }, { name = "ruff", specifier = ">=0.11.6" }, + { name = "types-protobuf" }, + { name = "types-requests" }, { name = "uv-dynamic-versioning", specifier = ">=0.8.2" }, ] @@ -125,13 +141,92 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, ] +[[package]] +name = "cachetools" +version = "5.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, +] + [[package]] name = "certifi" -version = "2025.4.26" +version = "2025.6.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753, upload-time = "2025-06-15T02:45:51.329Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload-time = "2025-06-15T02:45:49.977Z" }, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, + { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, + { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, + { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, + { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, + { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, + { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, + { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, + { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, + { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, + { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, + { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, + { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, + { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, + { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, + { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, + { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, ] [[package]] @@ -157,66 +252,66 @@ wheels = [ [[package]] name = "coverage" -version = "7.8.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/07/998afa4a0ecdf9b1981ae05415dad2d4e7716e1b1f00abbd91691ac09ac9/coverage-7.8.2.tar.gz", hash = "sha256:a886d531373a1f6ff9fad2a2ba4a045b68467b779ae729ee0b3b10ac20033b27", size = 812759, upload-time = "2025-05-23T11:39:57.856Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/6b/7dd06399a5c0b81007e3a6af0395cd60e6a30f959f8d407d3ee04642e896/coverage-7.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd8ec21e1443fd7a447881332f7ce9d35b8fbd2849e761bb290b584535636b0a", size = 211573, upload-time = "2025-05-23T11:37:47.207Z" }, - { url = "https://files.pythonhosted.org/packages/f0/df/2b24090820a0bac1412955fb1a4dade6bc3b8dcef7b899c277ffaf16916d/coverage-7.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26c2396674816deaeae7ded0e2b42c26537280f8fe313335858ffff35019be", size = 212006, upload-time = "2025-05-23T11:37:50.289Z" }, - { url = "https://files.pythonhosted.org/packages/c5/c4/e4e3b998e116625562a872a342419652fa6ca73f464d9faf9f52f1aff427/coverage-7.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1aec326ed237e5880bfe69ad41616d333712c7937bcefc1343145e972938f9b3", size = 241128, upload-time = "2025-05-23T11:37:52.229Z" }, - { url = "https://files.pythonhosted.org/packages/b1/67/b28904afea3e87a895da850ba587439a61699bf4b73d04d0dfd99bbd33b4/coverage-7.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e818796f71702d7a13e50c70de2a1924f729228580bcba1607cccf32eea46e6", size = 239026, upload-time = "2025-05-23T11:37:53.846Z" }, - { url = "https://files.pythonhosted.org/packages/8c/0f/47bf7c5630d81bc2cd52b9e13043685dbb7c79372a7f5857279cc442b37c/coverage-7.8.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:546e537d9e24efc765c9c891328f30f826e3e4808e31f5d0f87c4ba12bbd1622", size = 240172, upload-time = "2025-05-23T11:37:55.711Z" }, - { url = "https://files.pythonhosted.org/packages/ba/38/af3eb9d36d85abc881f5aaecf8209383dbe0fa4cac2d804c55d05c51cb04/coverage-7.8.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab9b09a2349f58e73f8ebc06fac546dd623e23b063e5398343c5270072e3201c", size = 240086, upload-time = "2025-05-23T11:37:57.724Z" }, - { url = "https://files.pythonhosted.org/packages/9e/64/c40c27c2573adeba0fe16faf39a8aa57368a1f2148865d6bb24c67eadb41/coverage-7.8.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd51355ab8a372d89fb0e6a31719e825cf8df8b6724bee942fb5b92c3f016ba3", size = 238792, upload-time = "2025-05-23T11:37:59.737Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ab/b7c85146f15457671c1412afca7c25a5696d7625e7158002aa017e2d7e3c/coverage-7.8.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0774df1e093acb6c9e4d58bce7f86656aeed6c132a16e2337692c12786b32404", size = 239096, upload-time = "2025-05-23T11:38:01.693Z" }, - { url = "https://files.pythonhosted.org/packages/d3/50/9446dad1310905fb1dc284d60d4320a5b25d4e3e33f9ea08b8d36e244e23/coverage-7.8.2-cp310-cp310-win32.whl", hash = "sha256:00f2e2f2e37f47e5f54423aeefd6c32a7dbcedc033fcd3928a4f4948e8b96af7", size = 214144, upload-time = "2025-05-23T11:38:03.68Z" }, - { url = "https://files.pythonhosted.org/packages/23/ed/792e66ad7b8b0df757db8d47af0c23659cdb5a65ef7ace8b111cacdbee89/coverage-7.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:145b07bea229821d51811bf15eeab346c236d523838eda395ea969d120d13347", size = 215043, upload-time = "2025-05-23T11:38:05.217Z" }, - { url = "https://files.pythonhosted.org/packages/6a/4d/1ff618ee9f134d0de5cc1661582c21a65e06823f41caf801aadf18811a8e/coverage-7.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b99058eef42e6a8dcd135afb068b3d53aff3921ce699e127602efff9956457a9", size = 211692, upload-time = "2025-05-23T11:38:08.485Z" }, - { url = "https://files.pythonhosted.org/packages/96/fa/c3c1b476de96f2bc7a8ca01a9f1fcb51c01c6b60a9d2c3e66194b2bdb4af/coverage-7.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5feb7f2c3e6ea94d3b877def0270dff0947b8d8c04cfa34a17be0a4dc1836879", size = 212115, upload-time = "2025-05-23T11:38:09.989Z" }, - { url = "https://files.pythonhosted.org/packages/f7/c2/5414c5a1b286c0f3881ae5adb49be1854ac5b7e99011501f81c8c1453065/coverage-7.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:670a13249b957bb9050fab12d86acef7bf8f6a879b9d1a883799276e0d4c674a", size = 244740, upload-time = "2025-05-23T11:38:11.947Z" }, - { url = "https://files.pythonhosted.org/packages/cd/46/1ae01912dfb06a642ef3dd9cf38ed4996fda8fe884dab8952da616f81a2b/coverage-7.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bdc8bf760459a4a4187b452213e04d039990211f98644c7292adf1e471162b5", size = 242429, upload-time = "2025-05-23T11:38:13.955Z" }, - { url = "https://files.pythonhosted.org/packages/06/58/38c676aec594bfe2a87c7683942e5a30224791d8df99bcc8439fde140377/coverage-7.8.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07a989c867986c2a75f158f03fdb413128aad29aca9d4dbce5fc755672d96f11", size = 244218, upload-time = "2025-05-23T11:38:15.631Z" }, - { url = "https://files.pythonhosted.org/packages/80/0c/95b1023e881ce45006d9abc250f76c6cdab7134a1c182d9713878dfefcb2/coverage-7.8.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2db10dedeb619a771ef0e2949ccba7b75e33905de959c2643a4607bef2f3fb3a", size = 243865, upload-time = "2025-05-23T11:38:17.622Z" }, - { url = "https://files.pythonhosted.org/packages/57/37/0ae95989285a39e0839c959fe854a3ae46c06610439350d1ab860bf020ac/coverage-7.8.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e6ea7dba4e92926b7b5f0990634b78ea02f208d04af520c73a7c876d5a8d36cb", size = 242038, upload-time = "2025-05-23T11:38:19.966Z" }, - { url = "https://files.pythonhosted.org/packages/4d/82/40e55f7c0eb5e97cc62cbd9d0746fd24e8caf57be5a408b87529416e0c70/coverage-7.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ef2f22795a7aca99fc3c84393a55a53dd18ab8c93fb431004e4d8f0774150f54", size = 242567, upload-time = "2025-05-23T11:38:21.912Z" }, - { url = "https://files.pythonhosted.org/packages/f9/35/66a51adc273433a253989f0d9cc7aa6bcdb4855382cf0858200afe578861/coverage-7.8.2-cp311-cp311-win32.whl", hash = "sha256:641988828bc18a6368fe72355df5f1703e44411adbe49bba5644b941ce6f2e3a", size = 214194, upload-time = "2025-05-23T11:38:23.571Z" }, - { url = "https://files.pythonhosted.org/packages/f6/8f/a543121f9f5f150eae092b08428cb4e6b6d2d134152c3357b77659d2a605/coverage-7.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8ab4a51cb39dc1933ba627e0875046d150e88478dbe22ce145a68393e9652975", size = 215109, upload-time = "2025-05-23T11:38:25.137Z" }, - { url = "https://files.pythonhosted.org/packages/77/65/6cc84b68d4f35186463cd7ab1da1169e9abb59870c0f6a57ea6aba95f861/coverage-7.8.2-cp311-cp311-win_arm64.whl", hash = "sha256:8966a821e2083c74d88cca5b7dcccc0a3a888a596a04c0b9668a891de3a0cc53", size = 213521, upload-time = "2025-05-23T11:38:27.123Z" }, - { url = "https://files.pythonhosted.org/packages/8d/2a/1da1ada2e3044fcd4a3254fb3576e160b8fe5b36d705c8a31f793423f763/coverage-7.8.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2f6fe3654468d061942591aef56686131335b7a8325684eda85dacdf311356c", size = 211876, upload-time = "2025-05-23T11:38:29.01Z" }, - { url = "https://files.pythonhosted.org/packages/70/e9/3d715ffd5b6b17a8be80cd14a8917a002530a99943cc1939ad5bb2aa74b9/coverage-7.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76090fab50610798cc05241bf83b603477c40ee87acd358b66196ab0ca44ffa1", size = 212130, upload-time = "2025-05-23T11:38:30.675Z" }, - { url = "https://files.pythonhosted.org/packages/a0/02/fdce62bb3c21649abfd91fbdcf041fb99be0d728ff00f3f9d54d97ed683e/coverage-7.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd0a0a5054be160777a7920b731a0570284db5142abaaf81bcbb282b8d99279", size = 246176, upload-time = "2025-05-23T11:38:32.395Z" }, - { url = "https://files.pythonhosted.org/packages/a7/52/decbbed61e03b6ffe85cd0fea360a5e04a5a98a7423f292aae62423b8557/coverage-7.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da23ce9a3d356d0affe9c7036030b5c8f14556bd970c9b224f9c8205505e3b99", size = 243068, upload-time = "2025-05-23T11:38:33.989Z" }, - { url = "https://files.pythonhosted.org/packages/38/6c/d0e9c0cce18faef79a52778219a3c6ee8e336437da8eddd4ab3dbd8fadff/coverage-7.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9392773cffeb8d7e042a7b15b82a414011e9d2b5fdbbd3f7e6a6b17d5e21b20", size = 245328, upload-time = "2025-05-23T11:38:35.568Z" }, - { url = "https://files.pythonhosted.org/packages/f0/70/f703b553a2f6b6c70568c7e398ed0789d47f953d67fbba36a327714a7bca/coverage-7.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:876cbfd0b09ce09d81585d266c07a32657beb3eaec896f39484b631555be0fe2", size = 245099, upload-time = "2025-05-23T11:38:37.627Z" }, - { url = "https://files.pythonhosted.org/packages/ec/fb/4cbb370dedae78460c3aacbdad9d249e853f3bc4ce5ff0e02b1983d03044/coverage-7.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3da9b771c98977a13fbc3830f6caa85cae6c9c83911d24cb2d218e9394259c57", size = 243314, upload-time = "2025-05-23T11:38:39.238Z" }, - { url = "https://files.pythonhosted.org/packages/39/9f/1afbb2cb9c8699b8bc38afdce00a3b4644904e6a38c7bf9005386c9305ec/coverage-7.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a990f6510b3292686713bfef26d0049cd63b9c7bb17e0864f133cbfd2e6167f", size = 244489, upload-time = "2025-05-23T11:38:40.845Z" }, - { url = "https://files.pythonhosted.org/packages/79/fa/f3e7ec7d220bff14aba7a4786ae47043770cbdceeea1803083059c878837/coverage-7.8.2-cp312-cp312-win32.whl", hash = "sha256:bf8111cddd0f2b54d34e96613e7fbdd59a673f0cf5574b61134ae75b6f5a33b8", size = 214366, upload-time = "2025-05-23T11:38:43.551Z" }, - { url = "https://files.pythonhosted.org/packages/54/aa/9cbeade19b7e8e853e7ffc261df885d66bf3a782c71cba06c17df271f9e6/coverage-7.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:86a323a275e9e44cdf228af9b71c5030861d4d2610886ab920d9945672a81223", size = 215165, upload-time = "2025-05-23T11:38:45.148Z" }, - { url = "https://files.pythonhosted.org/packages/c4/73/e2528bf1237d2448f882bbebaec5c3500ef07301816c5c63464b9da4d88a/coverage-7.8.2-cp312-cp312-win_arm64.whl", hash = "sha256:820157de3a589e992689ffcda8639fbabb313b323d26388d02e154164c57b07f", size = 213548, upload-time = "2025-05-23T11:38:46.74Z" }, - { url = "https://files.pythonhosted.org/packages/1a/93/eb6400a745ad3b265bac36e8077fdffcf0268bdbbb6c02b7220b624c9b31/coverage-7.8.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ea561010914ec1c26ab4188aef8b1567272ef6de096312716f90e5baa79ef8ca", size = 211898, upload-time = "2025-05-23T11:38:49.066Z" }, - { url = "https://files.pythonhosted.org/packages/1b/7c/bdbf113f92683024406a1cd226a199e4200a2001fc85d6a6e7e299e60253/coverage-7.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cb86337a4fcdd0e598ff2caeb513ac604d2f3da6d53df2c8e368e07ee38e277d", size = 212171, upload-time = "2025-05-23T11:38:51.207Z" }, - { url = "https://files.pythonhosted.org/packages/91/22/594513f9541a6b88eb0dba4d5da7d71596dadef6b17a12dc2c0e859818a9/coverage-7.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a4636ddb666971345541b59899e969f3b301143dd86b0ddbb570bd591f1e85", size = 245564, upload-time = "2025-05-23T11:38:52.857Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f4/2860fd6abeebd9f2efcfe0fd376226938f22afc80c1943f363cd3c28421f/coverage-7.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5040536cf9b13fb033f76bcb5e1e5cb3b57c4807fef37db9e0ed129c6a094257", size = 242719, upload-time = "2025-05-23T11:38:54.529Z" }, - { url = "https://files.pythonhosted.org/packages/89/60/f5f50f61b6332451520e6cdc2401700c48310c64bc2dd34027a47d6ab4ca/coverage-7.8.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc67994df9bcd7e0150a47ef41278b9e0a0ea187caba72414b71dc590b99a108", size = 244634, upload-time = "2025-05-23T11:38:57.326Z" }, - { url = "https://files.pythonhosted.org/packages/3b/70/7f4e919039ab7d944276c446b603eea84da29ebcf20984fb1fdf6e602028/coverage-7.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e6c86888fd076d9e0fe848af0a2142bf606044dc5ceee0aa9eddb56e26895a0", size = 244824, upload-time = "2025-05-23T11:38:59.421Z" }, - { url = "https://files.pythonhosted.org/packages/26/45/36297a4c0cea4de2b2c442fe32f60c3991056c59cdc3cdd5346fbb995c97/coverage-7.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:684ca9f58119b8e26bef860db33524ae0365601492e86ba0b71d513f525e7050", size = 242872, upload-time = "2025-05-23T11:39:01.049Z" }, - { url = "https://files.pythonhosted.org/packages/a4/71/e041f1b9420f7b786b1367fa2a375703889ef376e0d48de9f5723fb35f11/coverage-7.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8165584ddedb49204c4e18da083913bdf6a982bfb558632a79bdaadcdafd0d48", size = 244179, upload-time = "2025-05-23T11:39:02.709Z" }, - { url = "https://files.pythonhosted.org/packages/bd/db/3c2bf49bdc9de76acf2491fc03130c4ffc51469ce2f6889d2640eb563d77/coverage-7.8.2-cp313-cp313-win32.whl", hash = "sha256:34759ee2c65362163699cc917bdb2a54114dd06d19bab860725f94ef45a3d9b7", size = 214393, upload-time = "2025-05-23T11:39:05.457Z" }, - { url = "https://files.pythonhosted.org/packages/c6/dc/947e75d47ebbb4b02d8babb1fad4ad381410d5bc9da7cfca80b7565ef401/coverage-7.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:2f9bc608fbafaee40eb60a9a53dbfb90f53cc66d3d32c2849dc27cf5638a21e3", size = 215194, upload-time = "2025-05-23T11:39:07.171Z" }, - { url = "https://files.pythonhosted.org/packages/90/31/a980f7df8a37eaf0dc60f932507fda9656b3a03f0abf188474a0ea188d6d/coverage-7.8.2-cp313-cp313-win_arm64.whl", hash = "sha256:9fe449ee461a3b0c7105690419d0b0aba1232f4ff6d120a9e241e58a556733f7", size = 213580, upload-time = "2025-05-23T11:39:08.862Z" }, - { url = "https://files.pythonhosted.org/packages/8a/6a/25a37dd90f6c95f59355629417ebcb74e1c34e38bb1eddf6ca9b38b0fc53/coverage-7.8.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8369a7c8ef66bded2b6484053749ff220dbf83cba84f3398c84c51a6f748a008", size = 212734, upload-time = "2025-05-23T11:39:11.109Z" }, - { url = "https://files.pythonhosted.org/packages/36/8b/3a728b3118988725f40950931abb09cd7f43b3c740f4640a59f1db60e372/coverage-7.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:159b81df53a5fcbc7d45dae3adad554fdbde9829a994e15227b3f9d816d00b36", size = 212959, upload-time = "2025-05-23T11:39:12.751Z" }, - { url = "https://files.pythonhosted.org/packages/53/3c/212d94e6add3a3c3f412d664aee452045ca17a066def8b9421673e9482c4/coverage-7.8.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6fcbbd35a96192d042c691c9e0c49ef54bd7ed865846a3c9d624c30bb67ce46", size = 257024, upload-time = "2025-05-23T11:39:15.569Z" }, - { url = "https://files.pythonhosted.org/packages/a4/40/afc03f0883b1e51bbe804707aae62e29c4e8c8bbc365c75e3e4ddeee9ead/coverage-7.8.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05364b9cc82f138cc86128dc4e2e1251c2981a2218bfcd556fe6b0fbaa3501be", size = 252867, upload-time = "2025-05-23T11:39:17.64Z" }, - { url = "https://files.pythonhosted.org/packages/18/a2/3699190e927b9439c6ded4998941a3c1d6fa99e14cb28d8536729537e307/coverage-7.8.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46d532db4e5ff3979ce47d18e2fe8ecad283eeb7367726da0e5ef88e4fe64740", size = 255096, upload-time = "2025-05-23T11:39:19.328Z" }, - { url = "https://files.pythonhosted.org/packages/b4/06/16e3598b9466456b718eb3e789457d1a5b8bfb22e23b6e8bbc307df5daf0/coverage-7.8.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4000a31c34932e7e4fa0381a3d6deb43dc0c8f458e3e7ea6502e6238e10be625", size = 256276, upload-time = "2025-05-23T11:39:21.077Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d5/4b5a120d5d0223050a53d2783c049c311eea1709fa9de12d1c358e18b707/coverage-7.8.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:43ff5033d657cd51f83015c3b7a443287250dc14e69910577c3e03bd2e06f27b", size = 254478, upload-time = "2025-05-23T11:39:22.838Z" }, - { url = "https://files.pythonhosted.org/packages/ba/85/f9ecdb910ecdb282b121bfcaa32fa8ee8cbd7699f83330ee13ff9bbf1a85/coverage-7.8.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94316e13f0981cbbba132c1f9f365cac1d26716aaac130866ca812006f662199", size = 255255, upload-time = "2025-05-23T11:39:24.644Z" }, - { url = "https://files.pythonhosted.org/packages/50/63/2d624ac7d7ccd4ebbd3c6a9eba9d7fc4491a1226071360d59dd84928ccb2/coverage-7.8.2-cp313-cp313t-win32.whl", hash = "sha256:3f5673888d3676d0a745c3d0e16da338c5eea300cb1f4ada9c872981265e76d8", size = 215109, upload-time = "2025-05-23T11:39:26.722Z" }, - { url = "https://files.pythonhosted.org/packages/22/5e/7053b71462e970e869111c1853afd642212568a350eba796deefdfbd0770/coverage-7.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:2c08b05ee8d7861e45dc5a2cc4195c8c66dca5ac613144eb6ebeaff2d502e73d", size = 216268, upload-time = "2025-05-23T11:39:28.429Z" }, - { url = "https://files.pythonhosted.org/packages/07/69/afa41aa34147655543dbe96994f8a246daf94b361ccf5edfd5df62ce066a/coverage-7.8.2-cp313-cp313t-win_arm64.whl", hash = "sha256:1e1448bb72b387755e1ff3ef1268a06617afd94188164960dba8d0245a46004b", size = 214071, upload-time = "2025-05-23T11:39:30.55Z" }, - { url = "https://files.pythonhosted.org/packages/69/2f/572b29496d8234e4a7773200dd835a0d32d9e171f2d974f3fe04a9dbc271/coverage-7.8.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:ec455eedf3ba0bbdf8f5a570012617eb305c63cb9f03428d39bf544cb2b94837", size = 203636, upload-time = "2025-05-23T11:39:52.002Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1a/0b9c32220ad694d66062f571cc5cedfa9997b64a591e8a500bb63de1bd40/coverage-7.8.2-py3-none-any.whl", hash = "sha256:726f32ee3713f7359696331a18daf0c3b3a70bb0ae71141b9d3c52be7c595e32", size = 203623, upload-time = "2025-05-23T11:39:53.846Z" }, +version = "7.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/e0/98670a80884f64578f0c22cd70c5e81a6e07b08167721c7487b4d70a7ca0/coverage-7.9.1.tar.gz", hash = "sha256:6cf43c78c4282708a28e466316935ec7489a9c487518a77fa68f716c67909cec", size = 813650, upload-time = "2025-06-13T13:02:28.627Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/78/1c1c5ec58f16817c09cbacb39783c3655d54a221b6552f47ff5ac9297603/coverage-7.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cc94d7c5e8423920787c33d811c0be67b7be83c705f001f7180c7b186dcf10ca", size = 212028, upload-time = "2025-06-13T13:00:29.293Z" }, + { url = "https://files.pythonhosted.org/packages/98/db/e91b9076f3a888e3b4ad7972ea3842297a52cc52e73fd1e529856e473510/coverage-7.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16aa0830d0c08a2c40c264cef801db8bc4fc0e1892782e45bcacbd5889270509", size = 212420, upload-time = "2025-06-13T13:00:34.027Z" }, + { url = "https://files.pythonhosted.org/packages/0e/d0/2b3733412954576b0aea0a16c3b6b8fbe95eb975d8bfa10b07359ead4252/coverage-7.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf95981b126f23db63e9dbe4cf65bd71f9a6305696fa5e2262693bc4e2183f5b", size = 241529, upload-time = "2025-06-13T13:00:35.786Z" }, + { url = "https://files.pythonhosted.org/packages/b3/00/5e2e5ae2e750a872226a68e984d4d3f3563cb01d1afb449a17aa819bc2c4/coverage-7.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f05031cf21699785cd47cb7485f67df619e7bcdae38e0fde40d23d3d0210d3c3", size = 239403, upload-time = "2025-06-13T13:00:37.399Z" }, + { url = "https://files.pythonhosted.org/packages/37/3b/a2c27736035156b0a7c20683afe7df498480c0dfdf503b8c878a21b6d7fb/coverage-7.9.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb4fbcab8764dc072cb651a4bcda4d11fb5658a1d8d68842a862a6610bd8cfa3", size = 240548, upload-time = "2025-06-13T13:00:39.647Z" }, + { url = "https://files.pythonhosted.org/packages/98/f5/13d5fc074c3c0e0dc80422d9535814abf190f1254d7c3451590dc4f8b18c/coverage-7.9.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0f16649a7330ec307942ed27d06ee7e7a38417144620bb3d6e9a18ded8a2d3e5", size = 240459, upload-time = "2025-06-13T13:00:40.934Z" }, + { url = "https://files.pythonhosted.org/packages/36/24/24b9676ea06102df824c4a56ffd13dc9da7904478db519efa877d16527d5/coverage-7.9.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cea0a27a89e6432705fffc178064503508e3c0184b4f061700e771a09de58187", size = 239128, upload-time = "2025-06-13T13:00:42.343Z" }, + { url = "https://files.pythonhosted.org/packages/be/05/242b7a7d491b369ac5fee7908a6e5ba42b3030450f3ad62c645b40c23e0e/coverage-7.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e980b53a959fa53b6f05343afbd1e6f44a23ed6c23c4b4c56c6662bbb40c82ce", size = 239402, upload-time = "2025-06-13T13:00:43.634Z" }, + { url = "https://files.pythonhosted.org/packages/73/e0/4de7f87192fa65c9c8fbaeb75507e124f82396b71de1797da5602898be32/coverage-7.9.1-cp310-cp310-win32.whl", hash = "sha256:70760b4c5560be6ca70d11f8988ee6542b003f982b32f83d5ac0b72476607b70", size = 214518, upload-time = "2025-06-13T13:00:45.622Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ab/5e4e2fe458907d2a65fab62c773671cfc5ac704f1e7a9ddd91996f66e3c2/coverage-7.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a66e8f628b71f78c0e0342003d53b53101ba4e00ea8dabb799d9dba0abbbcebe", size = 215436, upload-time = "2025-06-13T13:00:47.245Z" }, + { url = "https://files.pythonhosted.org/packages/60/34/fa69372a07d0903a78ac103422ad34db72281c9fc625eba94ac1185da66f/coverage-7.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95c765060e65c692da2d2f51a9499c5e9f5cf5453aeaf1420e3fc847cc060582", size = 212146, upload-time = "2025-06-13T13:00:48.496Z" }, + { url = "https://files.pythonhosted.org/packages/27/f0/da1894915d2767f093f081c42afeba18e760f12fdd7a2f4acbe00564d767/coverage-7.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ba383dc6afd5ec5b7a0d0c23d38895db0e15bcba7fb0fa8901f245267ac30d86", size = 212536, upload-time = "2025-06-13T13:00:51.535Z" }, + { url = "https://files.pythonhosted.org/packages/10/d5/3fc33b06e41e390f88eef111226a24e4504d216ab8e5d1a7089aa5a3c87a/coverage-7.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37ae0383f13cbdcf1e5e7014489b0d71cc0106458878ccde52e8a12ced4298ed", size = 245092, upload-time = "2025-06-13T13:00:52.883Z" }, + { url = "https://files.pythonhosted.org/packages/0a/39/7aa901c14977aba637b78e95800edf77f29f5a380d29768c5b66f258305b/coverage-7.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69aa417a030bf11ec46149636314c24c8d60fadb12fc0ee8f10fda0d918c879d", size = 242806, upload-time = "2025-06-13T13:00:54.571Z" }, + { url = "https://files.pythonhosted.org/packages/43/fc/30e5cfeaf560b1fc1989227adedc11019ce4bb7cce59d65db34fe0c2d963/coverage-7.9.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a4be2a28656afe279b34d4f91c3e26eccf2f85500d4a4ff0b1f8b54bf807338", size = 244610, upload-time = "2025-06-13T13:00:56.932Z" }, + { url = "https://files.pythonhosted.org/packages/bf/15/cca62b13f39650bc87b2b92bb03bce7f0e79dd0bf2c7529e9fc7393e4d60/coverage-7.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:382e7ddd5289f140259b610e5f5c58f713d025cb2f66d0eb17e68d0a94278875", size = 244257, upload-time = "2025-06-13T13:00:58.545Z" }, + { url = "https://files.pythonhosted.org/packages/cd/1a/c0f2abe92c29e1464dbd0ff9d56cb6c88ae2b9e21becdb38bea31fcb2f6c/coverage-7.9.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e5532482344186c543c37bfad0ee6069e8ae4fc38d073b8bc836fc8f03c9e250", size = 242309, upload-time = "2025-06-13T13:00:59.836Z" }, + { url = "https://files.pythonhosted.org/packages/57/8d/c6fd70848bd9bf88fa90df2af5636589a8126d2170f3aade21ed53f2b67a/coverage-7.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a39d18b3f50cc121d0ce3838d32d58bd1d15dab89c910358ebefc3665712256c", size = 242898, upload-time = "2025-06-13T13:01:02.506Z" }, + { url = "https://files.pythonhosted.org/packages/c2/9e/6ca46c7bff4675f09a66fe2797cd1ad6a24f14c9c7c3b3ebe0470a6e30b8/coverage-7.9.1-cp311-cp311-win32.whl", hash = "sha256:dd24bd8d77c98557880def750782df77ab2b6885a18483dc8588792247174b32", size = 214561, upload-time = "2025-06-13T13:01:04.012Z" }, + { url = "https://files.pythonhosted.org/packages/a1/30/166978c6302010742dabcdc425fa0f938fa5a800908e39aff37a7a876a13/coverage-7.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:6b55ad10a35a21b8015eabddc9ba31eb590f54adc9cd39bcf09ff5349fd52125", size = 215493, upload-time = "2025-06-13T13:01:05.702Z" }, + { url = "https://files.pythonhosted.org/packages/60/07/a6d2342cd80a5be9f0eeab115bc5ebb3917b4a64c2953534273cf9bc7ae6/coverage-7.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:6ad935f0016be24c0e97fc8c40c465f9c4b85cbbe6eac48934c0dc4d2568321e", size = 213869, upload-time = "2025-06-13T13:01:09.345Z" }, + { url = "https://files.pythonhosted.org/packages/68/d9/7f66eb0a8f2fce222de7bdc2046ec41cb31fe33fb55a330037833fb88afc/coverage-7.9.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8de12b4b87c20de895f10567639c0797b621b22897b0af3ce4b4e204a743626", size = 212336, upload-time = "2025-06-13T13:01:10.909Z" }, + { url = "https://files.pythonhosted.org/packages/20/20/e07cb920ef3addf20f052ee3d54906e57407b6aeee3227a9c91eea38a665/coverage-7.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5add197315a054e92cee1b5f686a2bcba60c4c3e66ee3de77ace6c867bdee7cb", size = 212571, upload-time = "2025-06-13T13:01:12.518Z" }, + { url = "https://files.pythonhosted.org/packages/78/f8/96f155de7e9e248ca9c8ff1a40a521d944ba48bec65352da9be2463745bf/coverage-7.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:600a1d4106fe66f41e5d0136dfbc68fe7200a5cbe85610ddf094f8f22e1b0300", size = 246377, upload-time = "2025-06-13T13:01:14.87Z" }, + { url = "https://files.pythonhosted.org/packages/3e/cf/1d783bd05b7bca5c10ded5f946068909372e94615a4416afadfe3f63492d/coverage-7.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a876e4c3e5a2a1715a6608906aa5a2e0475b9c0f68343c2ada98110512ab1d8", size = 243394, upload-time = "2025-06-13T13:01:16.23Z" }, + { url = "https://files.pythonhosted.org/packages/02/dd/e7b20afd35b0a1abea09fb3998e1abc9f9bd953bee548f235aebd2b11401/coverage-7.9.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81f34346dd63010453922c8e628a52ea2d2ccd73cb2487f7700ac531b247c8a5", size = 245586, upload-time = "2025-06-13T13:01:17.532Z" }, + { url = "https://files.pythonhosted.org/packages/4e/38/b30b0006fea9d617d1cb8e43b1bc9a96af11eff42b87eb8c716cf4d37469/coverage-7.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:888f8eee13f2377ce86d44f338968eedec3291876b0b8a7289247ba52cb984cd", size = 245396, upload-time = "2025-06-13T13:01:19.164Z" }, + { url = "https://files.pythonhosted.org/packages/31/e4/4d8ec1dc826e16791f3daf1b50943e8e7e1eb70e8efa7abb03936ff48418/coverage-7.9.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9969ef1e69b8c8e1e70d591f91bbc37fc9a3621e447525d1602801a24ceda898", size = 243577, upload-time = "2025-06-13T13:01:22.433Z" }, + { url = "https://files.pythonhosted.org/packages/25/f4/b0e96c5c38e6e40ef465c4bc7f138863e2909c00e54a331da335faf0d81a/coverage-7.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:60c458224331ee3f1a5b472773e4a085cc27a86a0b48205409d364272d67140d", size = 244809, upload-time = "2025-06-13T13:01:24.143Z" }, + { url = "https://files.pythonhosted.org/packages/8a/65/27e0a1fa5e2e5079bdca4521be2f5dabf516f94e29a0defed35ac2382eb2/coverage-7.9.1-cp312-cp312-win32.whl", hash = "sha256:5f646a99a8c2b3ff4c6a6e081f78fad0dde275cd59f8f49dc4eab2e394332e74", size = 214724, upload-time = "2025-06-13T13:01:25.435Z" }, + { url = "https://files.pythonhosted.org/packages/9b/a8/d5b128633fd1a5e0401a4160d02fa15986209a9e47717174f99dc2f7166d/coverage-7.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:30f445f85c353090b83e552dcbbdad3ec84c7967e108c3ae54556ca69955563e", size = 215535, upload-time = "2025-06-13T13:01:27.861Z" }, + { url = "https://files.pythonhosted.org/packages/a3/37/84bba9d2afabc3611f3e4325ee2c6a47cd449b580d4a606b240ce5a6f9bf/coverage-7.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:af41da5dca398d3474129c58cb2b106a5d93bbb196be0d307ac82311ca234342", size = 213904, upload-time = "2025-06-13T13:01:29.202Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a7/a027970c991ca90f24e968999f7d509332daf6b8c3533d68633930aaebac/coverage-7.9.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:31324f18d5969feef7344a932c32428a2d1a3e50b15a6404e97cba1cc9b2c631", size = 212358, upload-time = "2025-06-13T13:01:30.909Z" }, + { url = "https://files.pythonhosted.org/packages/f2/48/6aaed3651ae83b231556750280682528fea8ac7f1232834573472d83e459/coverage-7.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0c804506d624e8a20fb3108764c52e0eef664e29d21692afa375e0dd98dc384f", size = 212620, upload-time = "2025-06-13T13:01:32.256Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2a/f4b613f3b44d8b9f144847c89151992b2b6b79cbc506dee89ad0c35f209d/coverage-7.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef64c27bc40189f36fcc50c3fb8f16ccda73b6a0b80d9bd6e6ce4cffcd810bbd", size = 245788, upload-time = "2025-06-13T13:01:33.948Z" }, + { url = "https://files.pythonhosted.org/packages/04/d2/de4fdc03af5e4e035ef420ed26a703c6ad3d7a07aff2e959eb84e3b19ca8/coverage-7.9.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4fe2348cc6ec372e25adec0219ee2334a68d2f5222e0cba9c0d613394e12d86", size = 243001, upload-time = "2025-06-13T13:01:35.285Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e8/eed18aa5583b0423ab7f04e34659e51101135c41cd1dcb33ac1d7013a6d6/coverage-7.9.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34ed2186fe52fcc24d4561041979a0dec69adae7bce2ae8d1c49eace13e55c43", size = 244985, upload-time = "2025-06-13T13:01:36.712Z" }, + { url = "https://files.pythonhosted.org/packages/17/f8/ae9e5cce8885728c934eaa58ebfa8281d488ef2afa81c3dbc8ee9e6d80db/coverage-7.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:25308bd3d00d5eedd5ae7d4357161f4df743e3c0240fa773ee1b0f75e6c7c0f1", size = 245152, upload-time = "2025-06-13T13:01:39.303Z" }, + { url = "https://files.pythonhosted.org/packages/5a/c8/272c01ae792bb3af9b30fac14d71d63371db227980682836ec388e2c57c0/coverage-7.9.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73e9439310f65d55a5a1e0564b48e34f5369bee943d72c88378f2d576f5a5751", size = 243123, upload-time = "2025-06-13T13:01:40.727Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d0/2819a1e3086143c094ab446e3bdf07138527a7b88cb235c488e78150ba7a/coverage-7.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:37ab6be0859141b53aa89412a82454b482c81cf750de4f29223d52268a86de67", size = 244506, upload-time = "2025-06-13T13:01:42.184Z" }, + { url = "https://files.pythonhosted.org/packages/8b/4e/9f6117b89152df7b6112f65c7a4ed1f2f5ec8e60c4be8f351d91e7acc848/coverage-7.9.1-cp313-cp313-win32.whl", hash = "sha256:64bdd969456e2d02a8b08aa047a92d269c7ac1f47e0c977675d550c9a0863643", size = 214766, upload-time = "2025-06-13T13:01:44.482Z" }, + { url = "https://files.pythonhosted.org/packages/27/0f/4b59f7c93b52c2c4ce7387c5a4e135e49891bb3b7408dcc98fe44033bbe0/coverage-7.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:be9e3f68ca9edb897c2184ad0eee815c635565dbe7a0e7e814dc1f7cbab92c0a", size = 215568, upload-time = "2025-06-13T13:01:45.772Z" }, + { url = "https://files.pythonhosted.org/packages/09/1e/9679826336f8c67b9c39a359352882b24a8a7aee48d4c9cad08d38d7510f/coverage-7.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:1c503289ffef1d5105d91bbb4d62cbe4b14bec4d13ca225f9c73cde9bb46207d", size = 213939, upload-time = "2025-06-13T13:01:47.087Z" }, + { url = "https://files.pythonhosted.org/packages/bb/5b/5c6b4e7a407359a2e3b27bf9c8a7b658127975def62077d441b93a30dbe8/coverage-7.9.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0b3496922cb5f4215bf5caaef4cf12364a26b0be82e9ed6d050f3352cf2d7ef0", size = 213079, upload-time = "2025-06-13T13:01:48.554Z" }, + { url = "https://files.pythonhosted.org/packages/a2/22/1e2e07279fd2fd97ae26c01cc2186e2258850e9ec125ae87184225662e89/coverage-7.9.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9565c3ab1c93310569ec0d86b017f128f027cab0b622b7af288696d7ed43a16d", size = 213299, upload-time = "2025-06-13T13:01:49.997Z" }, + { url = "https://files.pythonhosted.org/packages/14/c0/4c5125a4b69d66b8c85986d3321520f628756cf524af810baab0790c7647/coverage-7.9.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2241ad5dbf79ae1d9c08fe52b36d03ca122fb9ac6bca0f34439e99f8327ac89f", size = 256535, upload-time = "2025-06-13T13:01:51.314Z" }, + { url = "https://files.pythonhosted.org/packages/81/8b/e36a04889dda9960be4263e95e777e7b46f1bb4fc32202612c130a20c4da/coverage-7.9.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bb5838701ca68b10ebc0937dbd0eb81974bac54447c55cd58dea5bca8451029", size = 252756, upload-time = "2025-06-13T13:01:54.403Z" }, + { url = "https://files.pythonhosted.org/packages/98/82/be04eff8083a09a4622ecd0e1f31a2c563dbea3ed848069e7b0445043a70/coverage-7.9.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b30a25f814591a8c0c5372c11ac8967f669b97444c47fd794926e175c4047ece", size = 254912, upload-time = "2025-06-13T13:01:56.769Z" }, + { url = "https://files.pythonhosted.org/packages/0f/25/c26610a2c7f018508a5ab958e5b3202d900422cf7cdca7670b6b8ca4e8df/coverage-7.9.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2d04b16a6062516df97969f1ae7efd0de9c31eb6ebdceaa0d213b21c0ca1a683", size = 256144, upload-time = "2025-06-13T13:01:58.19Z" }, + { url = "https://files.pythonhosted.org/packages/c5/8b/fb9425c4684066c79e863f1e6e7ecebb49e3a64d9f7f7860ef1688c56f4a/coverage-7.9.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7931b9e249edefb07cd6ae10c702788546341d5fe44db5b6108a25da4dca513f", size = 254257, upload-time = "2025-06-13T13:01:59.645Z" }, + { url = "https://files.pythonhosted.org/packages/93/df/27b882f54157fc1131e0e215b0da3b8d608d9b8ef79a045280118a8f98fe/coverage-7.9.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52e92b01041151bf607ee858e5a56c62d4b70f4dac85b8c8cb7fb8a351ab2c10", size = 255094, upload-time = "2025-06-13T13:02:01.37Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/cad1c3dbed8b3ee9e16fa832afe365b4e3eeab1fb6edb65ebbf745eabc92/coverage-7.9.1-cp313-cp313t-win32.whl", hash = "sha256:684e2110ed84fd1ca5f40e89aa44adf1729dc85444004111aa01866507adf363", size = 215437, upload-time = "2025-06-13T13:02:02.905Z" }, + { url = "https://files.pythonhosted.org/packages/99/4d/fad293bf081c0e43331ca745ff63673badc20afea2104b431cdd8c278b4c/coverage-7.9.1-cp313-cp313t-win_amd64.whl", hash = "sha256:437c576979e4db840539674e68c84b3cda82bc824dd138d56bead1435f1cb5d7", size = 216605, upload-time = "2025-06-13T13:02:05.638Z" }, + { url = "https://files.pythonhosted.org/packages/1f/56/4ee027d5965fc7fc126d7ec1187529cc30cc7d740846e1ecb5e92d31b224/coverage-7.9.1-cp313-cp313t-win_arm64.whl", hash = "sha256:18a0912944d70aaf5f399e350445738a1a20b50fbea788f640751c2ed9208b6c", size = 214392, upload-time = "2025-06-13T13:02:07.642Z" }, + { url = "https://files.pythonhosted.org/packages/3e/e5/c723545c3fd3204ebde3b4cc4b927dce709d3b6dc577754bb57f63ca4a4a/coverage-7.9.1-pp39.pp310.pp311-none-any.whl", hash = "sha256:db0f04118d1db74db6c9e1cb1898532c7dcc220f1d2718f058601f7c3f499514", size = 204009, upload-time = "2025-06-13T13:02:25.787Z" }, + { url = "https://files.pythonhosted.org/packages/08/b8/7ddd1e8ba9701dea08ce22029917140e6f66a859427406579fd8d0ca7274/coverage-7.9.1-py3-none-any.whl", hash = "sha256:66b974b145aa189516b6bf2d8423e888b742517d37872f6ee4c5be0073bd9a3c", size = 204000, upload-time = "2025-06-13T13:02:27.173Z" }, ] [package.optional-dependencies] @@ -226,7 +321,7 @@ toml = [ [[package]] name = "datamodel-code-generator" -version = "0.30.1" +version = "0.31.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argcomplete" }, @@ -240,21 +335,18 @@ dependencies = [ { name = "pyyaml" }, { name = "tomli", marker = "python_full_version < '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ef/bc/627a77eafcf7101c9f5710130b2def98593709a8d29676e4a58f09cd2a23/datamodel_code_generator-0.30.1.tar.gz", hash = "sha256:d125012face4cd1eca6c9300297a1f5775a9d5ff8fc3f68d34d0944a7beea105", size = 446630, upload-time = "2025-04-28T13:58:36.438Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/27/7806e87312a5fca6b0f1254b499296f836456db0b67fbbbf281c4d836da9/datamodel_code_generator-0.31.1.tar.gz", hash = "sha256:93672fd62284223203cfa7f5ab35ea0bcad0f627b0cfcbad21d8564b6be76c0b", size = 452638, upload-time = "2025-06-17T15:24:13.398Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/b3/01aab190372914399bbc77f89ac3b24b439c3d97a52a6198f1cd1396ef3a/datamodel_code_generator-0.30.1-py3-none-any.whl", hash = "sha256:9601dfa3da8aa8d8d54e182059f78836b1768a807d5c26df798db12d4054c8f3", size = 118045, upload-time = "2025-04-28T13:58:34.318Z" }, + { url = "https://files.pythonhosted.org/packages/2a/24/1d4caa6d6c4c0f4045c2d5ace4d37efe878e88734f55f2106e695626fefb/datamodel_code_generator-0.31.1-py3-none-any.whl", hash = "sha256:abeaff920500b4242f9cce97108801d726ff5cf3f146a2ce1e0a74a82239f701", size = 119343, upload-time = "2025-06-17T15:24:11.477Z" }, ] [[package]] -name = "deprecated" -version = "1.2.18" +name = "distlib" +version = "0.3.9" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744, upload-time = "2025-01-27T10:46:25.7Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923, upload-time = "2024-10-09T18:35:47.551Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" }, + { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973, upload-time = "2024-10-09T18:35:44.272Z" }, ] [[package]] @@ -283,16 +375,25 @@ wheels = [ [[package]] name = "fastapi" -version = "0.115.12" +version = "0.115.13" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236, upload-time = "2025-03-23T22:55:43.822Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/64/ec0788201b5554e2a87c49af26b77a4d132f807a0fa9675257ac92c6aa0e/fastapi-0.115.13.tar.gz", hash = "sha256:55d1d25c2e1e0a0a50aceb1c8705cd932def273c102bff0b1c1da88b3c6eb307", size = 295680, upload-time = "2025-06-17T11:49:45.575Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164, upload-time = "2025-03-23T22:55:42.101Z" }, + { url = "https://files.pythonhosted.org/packages/59/4a/e17764385382062b0edbb35a26b7cf76d71e27e456546277a42ba6545c6e/fastapi-0.115.13-py3-none-any.whl", hash = "sha256:0a0cab59afa7bab22f5eb347f8c9864b681558c278395e94035a741fc10cd865", size = 95315, upload-time = "2025-06-17T11:49:44.106Z" }, +] + +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, ] [[package]] @@ -304,6 +405,162 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f8/5c/e226de133afd8bb267ec27eead9ae3d784b95b39a287ed404caab39a5f50/genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7", size = 21470, upload-time = "2024-05-15T22:08:47.056Z" }, ] +[[package]] +name = "google-api-core" +version = "2.25.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "googleapis-common-protos" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/21/e9d043e88222317afdbdb567165fdbc3b0aad90064c7e0c9eb0ad9955ad8/google_api_core-2.25.1.tar.gz", hash = "sha256:d2aaa0b13c78c61cb3f4282c464c046e45fbd75755683c9c525e6e8f7ed0a5e8", size = 165443, upload-time = "2025-06-12T20:52:20.439Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/4b/ead00905132820b623732b175d66354e9d3e69fcf2a5dcdab780664e7896/google_api_core-2.25.1-py3-none-any.whl", hash = "sha256:8a2a56c1fef82987a524371f99f3bd0143702fecc670c72e600c1cda6bf8dbb7", size = 160807, upload-time = "2025-06-12T20:52:19.334Z" }, +] + +[[package]] +name = "google-auth" +version = "2.40.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "pyasn1-modules" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/9b/e92ef23b84fa10a64ce4831390b7a4c2e53c0132568d99d4ae61d04c8855/google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77", size = 281029, upload-time = "2025-06-04T18:04:57.577Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/63/b19553b658a1692443c62bd07e5868adaa0ad746a0751ba62c59568cd45b/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca", size = 216137, upload-time = "2025-06-04T18:04:55.573Z" }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.70.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903, upload-time = "2025-04-14T10:17:02.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530, upload-time = "2025-04-14T10:17:01.271Z" }, +] + +[[package]] +name = "grpcio" +version = "1.73.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/7b/ca3f561aeecf0c846d15e1b38921a60dffffd5d4113931198fbf455334ee/grpcio-1.73.0.tar.gz", hash = "sha256:3af4c30918a7f0d39de500d11255f8d9da4f30e94a2033e70fe2a720e184bd8e", size = 12786424, upload-time = "2025-06-09T10:08:23.365Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/44/5ca479c880b9f56c9a9502873ea500c09d1087dc868217a90724c24d83d0/grpcio-1.73.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:d050197eeed50f858ef6c51ab09514856f957dba7b1f7812698260fc9cc417f6", size = 5365135, upload-time = "2025-06-09T10:02:44.243Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b7/78ff355cdb602ab01ea437d316846847e0c1f7d109596e5409402cc13156/grpcio-1.73.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:ebb8d5f4b0200916fb292a964a4d41210de92aba9007e33d8551d85800ea16cb", size = 10609627, upload-time = "2025-06-09T10:02:46.678Z" }, + { url = "https://files.pythonhosted.org/packages/8d/92/5111235062b9da0e3010e5fd2bdceb766113fcf60520f9c23eb651089dd7/grpcio-1.73.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c0811331b469e3f15dda5f90ab71bcd9681189a83944fd6dc908e2c9249041ef", size = 5803418, upload-time = "2025-06-09T10:02:49.047Z" }, + { url = "https://files.pythonhosted.org/packages/76/fa/dbf3fca0b91fa044f1114b11adc3d4ccc18ab1ac278daa69d450fd9aaef2/grpcio-1.73.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12787c791c3993d0ea1cc8bf90393647e9a586066b3b322949365d2772ba965b", size = 6444741, upload-time = "2025-06-09T10:02:51.763Z" }, + { url = "https://files.pythonhosted.org/packages/44/e1/e7c830c1a29abd13f0e7e861c8db57a67db5cb8a1edc6b9d9cd44c26a1e5/grpcio-1.73.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c17771e884fddf152f2a0df12478e8d02853e5b602a10a9a9f1f52fa02b1d32", size = 6040755, upload-time = "2025-06-09T10:02:54.379Z" }, + { url = "https://files.pythonhosted.org/packages/b4/57/2eaccbfdd8298ab6bb4504600a4283260983a9db7378eb79c922fd559883/grpcio-1.73.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:275e23d4c428c26b51857bbd95fcb8e528783597207ec592571e4372b300a29f", size = 6132216, upload-time = "2025-06-09T10:02:56.932Z" }, + { url = "https://files.pythonhosted.org/packages/81/a4/1bd2c59d7426ab640b121f42acb820ff7cd5c561d03e9c9164cb8431128e/grpcio-1.73.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9ffc972b530bf73ef0f948f799482a1bf12d9b6f33406a8e6387c0ca2098a833", size = 6774779, upload-time = "2025-06-09T10:02:59.683Z" }, + { url = "https://files.pythonhosted.org/packages/c6/64/70ee85055b4107acbe1af6a99ef6885e34db89083e53e5c27b8442e3aa38/grpcio-1.73.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d269df64aff092b2cec5e015d8ae09c7e90888b5c35c24fdca719a2c9f35", size = 6304223, upload-time = "2025-06-09T10:03:01.794Z" }, + { url = "https://files.pythonhosted.org/packages/06/02/4b3c373edccf29205205a6d329a267b9337ecbbf658bc70f0a18d63d0a50/grpcio-1.73.0-cp310-cp310-win32.whl", hash = "sha256:072d8154b8f74300ed362c01d54af8b93200c1a9077aeaea79828d48598514f1", size = 3679738, upload-time = "2025-06-09T10:03:03.675Z" }, + { url = "https://files.pythonhosted.org/packages/30/7a/d6dab939cda2129e39a872ad48f61c9951567dcda8ab419b8de446315a68/grpcio-1.73.0-cp310-cp310-win_amd64.whl", hash = "sha256:ce953d9d2100e1078a76a9dc2b7338d5415924dc59c69a15bf6e734db8a0f1ca", size = 4340441, upload-time = "2025-06-09T10:03:05.942Z" }, + { url = "https://files.pythonhosted.org/packages/dd/31/9de81fd12f7b27e6af403531b7249d76f743d58e0654e624b3df26a43ce2/grpcio-1.73.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:51036f641f171eebe5fa7aaca5abbd6150f0c338dab3a58f9111354240fe36ec", size = 5363773, upload-time = "2025-06-09T10:03:08.056Z" }, + { url = "https://files.pythonhosted.org/packages/32/9e/2cb78be357a7f1fc4942b81468ef3c7e5fd3df3ac010540459c10895a57b/grpcio-1.73.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:d12bbb88381ea00bdd92c55aff3da3391fd85bc902c41275c8447b86f036ce0f", size = 10621912, upload-time = "2025-06-09T10:03:10.489Z" }, + { url = "https://files.pythonhosted.org/packages/59/2f/b43954811a2e218a2761c0813800773ac0ca187b94fd2b8494e8ef232dc8/grpcio-1.73.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:483c507c2328ed0e01bc1adb13d1eada05cc737ec301d8e5a8f4a90f387f1790", size = 5807985, upload-time = "2025-06-09T10:03:13.775Z" }, + { url = "https://files.pythonhosted.org/packages/1b/bf/68e9f47e7ee349ffee712dcd907ee66826cf044f0dec7ab517421e56e857/grpcio-1.73.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c201a34aa960c962d0ce23fe5f423f97e9d4b518ad605eae6d0a82171809caaa", size = 6448218, upload-time = "2025-06-09T10:03:16.042Z" }, + { url = "https://files.pythonhosted.org/packages/af/dd/38ae43dd58480d609350cf1411fdac5c2ebb243e2c770f6f7aa3773d5e29/grpcio-1.73.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:859f70c8e435e8e1fa060e04297c6818ffc81ca9ebd4940e180490958229a45a", size = 6044343, upload-time = "2025-06-09T10:03:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/93/44/b6770b55071adb86481f36dae87d332fcad883b7f560bba9a940394ba018/grpcio-1.73.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e2459a27c6886e7e687e4e407778425f3c6a971fa17a16420227bda39574d64b", size = 6135858, upload-time = "2025-06-09T10:03:21.059Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9f/63de49fcef436932fcf0ffb978101a95c83c177058dbfb56dbf30ab81659/grpcio-1.73.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e0084d4559ee3dbdcce9395e1bc90fdd0262529b32c417a39ecbc18da8074ac7", size = 6775806, upload-time = "2025-06-09T10:03:23.876Z" }, + { url = "https://files.pythonhosted.org/packages/4d/67/c11f1953469162e958f09690ec3a9be3fdb29dea7f5661362a664f9d609a/grpcio-1.73.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef5fff73d5f724755693a464d444ee0a448c6cdfd3c1616a9223f736c622617d", size = 6308413, upload-time = "2025-06-09T10:03:26.033Z" }, + { url = "https://files.pythonhosted.org/packages/ba/6a/9dd04426337db07f28bd51a986b7a038ba56912c81b5bb1083c17dd63404/grpcio-1.73.0-cp311-cp311-win32.whl", hash = "sha256:965a16b71a8eeef91fc4df1dc40dc39c344887249174053814f8a8e18449c4c3", size = 3678972, upload-time = "2025-06-09T10:03:28.433Z" }, + { url = "https://files.pythonhosted.org/packages/04/8b/8c0a8a4fdc2e7977d325eafc587c9cf468039693ac23ad707153231d3cb2/grpcio-1.73.0-cp311-cp311-win_amd64.whl", hash = "sha256:b71a7b4483d1f753bbc11089ff0f6fa63b49c97a9cc20552cded3fcad466d23b", size = 4342967, upload-time = "2025-06-09T10:03:31.215Z" }, + { url = "https://files.pythonhosted.org/packages/9d/4d/e938f3a0e51a47f2ce7e55f12f19f316e7074770d56a7c2765e782ec76bc/grpcio-1.73.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fb9d7c27089d9ba3746f18d2109eb530ef2a37452d2ff50f5a6696cd39167d3b", size = 5334911, upload-time = "2025-06-09T10:03:33.494Z" }, + { url = "https://files.pythonhosted.org/packages/13/56/f09c72c43aa8d6f15a71f2c63ebdfac9cf9314363dea2598dc501d8370db/grpcio-1.73.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:128ba2ebdac41e41554d492b82c34586a90ebd0766f8ebd72160c0e3a57b9155", size = 10601460, upload-time = "2025-06-09T10:03:36.613Z" }, + { url = "https://files.pythonhosted.org/packages/20/e3/85496edc81e41b3c44ebefffc7bce133bb531120066877df0f910eabfa19/grpcio-1.73.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:068ecc415f79408d57a7f146f54cdf9f0acb4b301a52a9e563973dc981e82f3d", size = 5759191, upload-time = "2025-06-09T10:03:39.838Z" }, + { url = "https://files.pythonhosted.org/packages/88/cc/fef74270a6d29f35ad744bfd8e6c05183f35074ff34c655a2c80f3b422b2/grpcio-1.73.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ddc1cfb2240f84d35d559ade18f69dcd4257dbaa5ba0de1a565d903aaab2968", size = 6409961, upload-time = "2025-06-09T10:03:42.706Z" }, + { url = "https://files.pythonhosted.org/packages/b0/e6/13cfea15e3b8f79c4ae7b676cb21fab70978b0fde1e1d28bb0e073291290/grpcio-1.73.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53007f70d9783f53b41b4cf38ed39a8e348011437e4c287eee7dd1d39d54b2f", size = 6003948, upload-time = "2025-06-09T10:03:44.96Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ed/b1a36dad4cc0dbf1f83f6d7b58825fefd5cc9ff3a5036e46091335649473/grpcio-1.73.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4dd8d8d092efede7d6f48d695ba2592046acd04ccf421436dd7ed52677a9ad29", size = 6103788, upload-time = "2025-06-09T10:03:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c8/d381433d3d46d10f6858126d2d2245ef329e30f3752ce4514c93b95ca6fc/grpcio-1.73.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:70176093d0a95b44d24baa9c034bb67bfe2b6b5f7ebc2836f4093c97010e17fd", size = 6749508, upload-time = "2025-06-09T10:03:51.185Z" }, + { url = "https://files.pythonhosted.org/packages/87/0a/ff0c31dbd15e63b34320efafac647270aa88c31aa19ff01154a73dc7ce86/grpcio-1.73.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:085ebe876373ca095e24ced95c8f440495ed0b574c491f7f4f714ff794bbcd10", size = 6284342, upload-time = "2025-06-09T10:03:54.467Z" }, + { url = "https://files.pythonhosted.org/packages/fd/73/f762430c0ba867403b9d6e463afe026bf019bd9206eee753785239719273/grpcio-1.73.0-cp312-cp312-win32.whl", hash = "sha256:cfc556c1d6aef02c727ec7d0016827a73bfe67193e47c546f7cadd3ee6bf1a60", size = 3669319, upload-time = "2025-06-09T10:03:56.751Z" }, + { url = "https://files.pythonhosted.org/packages/10/8b/3411609376b2830449cf416f457ad9d2aacb7f562e1b90fdd8bdedf26d63/grpcio-1.73.0-cp312-cp312-win_amd64.whl", hash = "sha256:bbf45d59d090bf69f1e4e1594832aaf40aa84b31659af3c5e2c3f6a35202791a", size = 4335596, upload-time = "2025-06-09T10:03:59.866Z" }, + { url = "https://files.pythonhosted.org/packages/60/da/6f3f7a78e5455c4cbe87c85063cc6da05d65d25264f9d4aed800ece46294/grpcio-1.73.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:da1d677018ef423202aca6d73a8d3b2cb245699eb7f50eb5f74cae15a8e1f724", size = 5335867, upload-time = "2025-06-09T10:04:03.153Z" }, + { url = "https://files.pythonhosted.org/packages/53/14/7d1f2526b98b9658d7be0bb163fd78d681587de6709d8b0c74b4b481b013/grpcio-1.73.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:36bf93f6a657f37c131d9dd2c391b867abf1426a86727c3575393e9e11dadb0d", size = 10595587, upload-time = "2025-06-09T10:04:05.694Z" }, + { url = "https://files.pythonhosted.org/packages/02/24/a293c398ae44e741da1ed4b29638edbb002258797b07a783f65506165b4c/grpcio-1.73.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:d84000367508ade791d90c2bafbd905574b5ced8056397027a77a215d601ba15", size = 5765793, upload-time = "2025-06-09T10:04:09.235Z" }, + { url = "https://files.pythonhosted.org/packages/e1/24/d84dbd0b5bf36fb44922798d525a85cefa2ffee7b7110e61406e9750ed15/grpcio-1.73.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c98ba1d928a178ce33f3425ff823318040a2b7ef875d30a0073565e5ceb058d9", size = 6415494, upload-time = "2025-06-09T10:04:12.377Z" }, + { url = "https://files.pythonhosted.org/packages/5e/85/c80dc65aed8e9dce3d54688864bac45331d9c7600985541f18bd5cb301d4/grpcio-1.73.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a73c72922dfd30b396a5f25bb3a4590195ee45ecde7ee068acb0892d2900cf07", size = 6007279, upload-time = "2025-06-09T10:04:14.878Z" }, + { url = "https://files.pythonhosted.org/packages/37/fc/207c00a4c6fa303d26e2cbd62fbdb0582facdfd08f55500fd83bf6b0f8db/grpcio-1.73.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:10e8edc035724aba0346a432060fd192b42bd03675d083c01553cab071a28da5", size = 6105505, upload-time = "2025-06-09T10:04:17.39Z" }, + { url = "https://files.pythonhosted.org/packages/72/35/8fe69af820667b87ebfcb24214e42a1d53da53cb39edd6b4f84f6b36da86/grpcio-1.73.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f5cdc332b503c33b1643b12ea933582c7b081957c8bc2ea4cc4bc58054a09288", size = 6753792, upload-time = "2025-06-09T10:04:19.989Z" }, + { url = "https://files.pythonhosted.org/packages/e2/d8/738c77c1e821e350da4a048849f695ff88a02b291f8c69db23908867aea6/grpcio-1.73.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:07ad7c57233c2109e4ac999cb9c2710c3b8e3f491a73b058b0ce431f31ed8145", size = 6287593, upload-time = "2025-06-09T10:04:22.878Z" }, + { url = "https://files.pythonhosted.org/packages/09/ec/8498eabc018fa39ae8efe5e47e3f4c1bc9ed6281056713871895dc998807/grpcio-1.73.0-cp313-cp313-win32.whl", hash = "sha256:0eb5df4f41ea10bda99a802b2a292d85be28958ede2a50f2beb8c7fc9a738419", size = 3668637, upload-time = "2025-06-09T10:04:25.787Z" }, + { url = "https://files.pythonhosted.org/packages/d7/35/347db7d2e7674b621afd21b12022e7f48c7b0861b5577134b4e939536141/grpcio-1.73.0-cp313-cp313-win_amd64.whl", hash = "sha256:38cf518cc54cd0c47c9539cefa8888549fcc067db0b0c66a46535ca8032020c4", size = 4335872, upload-time = "2025-06-09T10:04:29.032Z" }, +] + +[[package]] +name = "grpcio-reflection" +version = "1.71.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "grpcio" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/56/af/a693ed8789bc07a3a9b0b1b9cad75aeb899e97173e409d7f08c4fe854e5d/grpcio_reflection-1.71.0.tar.gz", hash = "sha256:51504e977057ffabe66d1ed55557b15e969c42bb3a1f28ee45d730dd5f983bb5", size = 18805, upload-time = "2025-03-10T19:29:00.191Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/32/da7948c387768c609c594b87600d3521a0d9d3f8729c3fade31f8690402f/grpcio_reflection-1.71.0-py3-none-any.whl", hash = "sha256:8c88bdd9c92fcdd4d5df119997be05ecd0d7e10d377ec4a5072db507d2894612", size = 22685, upload-time = "2025-03-10T19:27:03.832Z" }, +] + +[[package]] +name = "grpcio-tools" +version = "1.71.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "grpcio" }, + { name = "protobuf" }, + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/d2/c0866a48c355a6a4daa1f7e27e210c7fa561b1f3b7c0bce2671e89cfa31e/grpcio_tools-1.71.0.tar.gz", hash = "sha256:38dba8e0d5e0fb23a034e09644fdc6ed862be2371887eee54901999e8f6792a8", size = 5326008, upload-time = "2025-03-10T19:29:03.38Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/60/aa7f261eda558d018457e5c8bd8a8079136e5107a0942fd3167477ab50e2/grpcio_tools-1.71.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:f4ad7f0d756546902597053d70b3af2606fbd70d7972876cd75c1e241d22ae00", size = 2385558, upload-time = "2025-03-10T19:27:09.067Z" }, + { url = "https://files.pythonhosted.org/packages/0d/e3/e47b96e93e51398ba3462e027d93a10c0c23fffc31733de9bd4f44a2b867/grpcio_tools-1.71.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:64bdb291df61cf570b5256777ad5fe2b1db6d67bc46e55dc56a0a862722ae329", size = 5930039, upload-time = "2025-03-10T19:27:11.617Z" }, + { url = "https://files.pythonhosted.org/packages/a6/69/5d8920002483b2a65ae3b03329dfe3b668c3592f001d5358e1538f540012/grpcio_tools-1.71.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:8dd9795e982d77a4b496f7278b943c2563d9afde2069cdee78c111a40cc4d675", size = 2351932, upload-time = "2025-03-10T19:27:13.087Z" }, + { url = "https://files.pythonhosted.org/packages/c4/50/8116e307662a2337cdc3f0e1a8b23af197129448b7ff7e0cf1a76c9b0178/grpcio_tools-1.71.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1b5860c41a36b26fec4f52998f1a451d0525a5c9a4fb06b6ea3e9211abdb925", size = 2744962, upload-time = "2025-03-10T19:27:14.518Z" }, + { url = "https://files.pythonhosted.org/packages/e3/4b/d95be4aaf78d7b02dff3bd332c75c228288178e92af0e5228759ac5002a0/grpcio_tools-1.71.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3059c14035e5dc03d462f261e5900b9a077fd1a36976c3865b8507474520bad4", size = 2476716, upload-time = "2025-03-10T19:27:16.199Z" }, + { url = "https://files.pythonhosted.org/packages/37/c2/c784a3705b1a1fd277751a8fc881d5a29325a460b9211e3c6164f594b178/grpcio_tools-1.71.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f360981b215b1d5aff9235b37e7e1826246e35bbac32a53e41d4e990a37b8f4c", size = 2854132, upload-time = "2025-03-10T19:27:17.841Z" }, + { url = "https://files.pythonhosted.org/packages/93/8f/173adbf72ed3996e1962182b55abf30151edc8b53daac0bf15cc3dc4b09e/grpcio_tools-1.71.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bfe3888c3bbe16a5aa39409bc38744a31c0c3d2daa2b0095978c56e106c85b42", size = 3305069, upload-time = "2025-03-10T19:27:19.917Z" }, + { url = "https://files.pythonhosted.org/packages/e4/a8/b1e7df63e7f83336275922f92ded1cd6918964c511280b31c872c54538f4/grpcio_tools-1.71.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:145985c0bf12131f0a1503e65763e0f060473f7f3928ed1ff3fb0e8aad5bc8ac", size = 2916636, upload-time = "2025-03-10T19:27:21.948Z" }, + { url = "https://files.pythonhosted.org/packages/be/a3/53f1e74c6e1c92ad94d7a0127a60fe913276a3e8c864737a053a1574b05c/grpcio_tools-1.71.0-cp310-cp310-win32.whl", hash = "sha256:82c430edd939bb863550ee0fecf067d78feff828908a1b529bbe33cc57f2419c", size = 949576, upload-time = "2025-03-10T19:27:23.391Z" }, + { url = "https://files.pythonhosted.org/packages/97/43/4a3ae830c1405bcb1ba47f2225779dbe9fc009ba341d4a90012919304855/grpcio_tools-1.71.0-cp310-cp310-win_amd64.whl", hash = "sha256:83e90724e3f02415c628e4ead1d6ffe063820aaaa078d9a39176793df958cd5a", size = 1121087, upload-time = "2025-03-10T19:27:25.5Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ec/73b9797ffec80e1faf039ce3e2f0513e26e1a68eedc525ed294ae2a44d03/grpcio_tools-1.71.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:1f19b16b49afa5d21473f49c0966dd430c88d089cd52ac02404d8cef67134efb", size = 2385557, upload-time = "2025-03-10T19:27:27.62Z" }, + { url = "https://files.pythonhosted.org/packages/bf/87/42c6e192b7b09c9610a53e771797f7826aee4f6e769683985ae406a2d862/grpcio_tools-1.71.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:459c8f5e00e390aecd5b89de67deb3ec7188a274bc6cb50e43cef35ab3a3f45d", size = 5954404, upload-time = "2025-03-10T19:27:29.835Z" }, + { url = "https://files.pythonhosted.org/packages/25/30/3fd385a56d32dce34cde09a64dbaf7cf85d395f2bcd86dd41e4b4ee5938f/grpcio_tools-1.71.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:edab7e6518de01196be37f96cb1e138c3819986bf5e2a6c9e1519b4d716b2f5a", size = 2352061, upload-time = "2025-03-10T19:27:31.624Z" }, + { url = "https://files.pythonhosted.org/packages/87/eb/e9971c7693a2d85e7f55760f7906211a95ff74af4d41b05d187849d7fb58/grpcio_tools-1.71.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8b93b9f6adc7491d4c10144c0643409db298e5e63c997106a804f6f0248dbaf4", size = 2745033, upload-time = "2025-03-10T19:27:33.787Z" }, + { url = "https://files.pythonhosted.org/packages/15/72/4e69beae87a1b334f80da9e93c8e2f5c8fe4860c956a781246a092dc4c97/grpcio_tools-1.71.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ae5f2efa9e644c10bf1021600bfc099dfbd8e02b184d2d25dc31fcd6c2bc59e", size = 2476743, upload-time = "2025-03-10T19:27:35.896Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f3/336d2c83f1bfc00a5376bf20dd2273d7aa891b03dd91b11c71ca47392351/grpcio_tools-1.71.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:65aa082f4435571d65d5ce07fc444f23c3eff4f3e34abef599ef8c9e1f6f360f", size = 2853693, upload-time = "2025-03-10T19:27:37.624Z" }, + { url = "https://files.pythonhosted.org/packages/62/ba/cc7ace518c11501a4b8620df5edb8188e81470e5b82dc6829212f3e9b2ff/grpcio_tools-1.71.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1331e726e08b7bdcbf2075fcf4b47dff07842b04845e6e220a08a4663e232d7f", size = 3304474, upload-time = "2025-03-10T19:27:39.351Z" }, + { url = "https://files.pythonhosted.org/packages/00/0d/4b843654af3d5aa2f1a5775df1d583e6e3471e6d569106fd3213ad185a98/grpcio_tools-1.71.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6693a7d3ba138b0e693b3d1f687cdd9db9e68976c3fa2b951c17a072fea8b583", size = 2916147, upload-time = "2025-03-10T19:27:41.022Z" }, + { url = "https://files.pythonhosted.org/packages/e4/14/047e1c817422bc3d434247b9c640c51fd51ca4e047583ff31d927c3dea73/grpcio_tools-1.71.0-cp311-cp311-win32.whl", hash = "sha256:6d11ed3ff7b6023b5c72a8654975324bb98c1092426ba5b481af406ff559df00", size = 949374, upload-time = "2025-03-10T19:27:42.999Z" }, + { url = "https://files.pythonhosted.org/packages/86/cb/739a1b6d517672693796022c0f9061f63eaa243ec70cbbfa59bf881ed9fb/grpcio_tools-1.71.0-cp311-cp311-win_amd64.whl", hash = "sha256:072b2a5805ac97e4623b3aa8f7818275f3fb087f4aa131b0fce00471065f6eaa", size = 1120786, upload-time = "2025-03-10T19:27:44.706Z" }, + { url = "https://files.pythonhosted.org/packages/de/e4/156956b92ad0298290c3d68e6670bc5a6fbefcccfe1ec3997480605e7135/grpcio_tools-1.71.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:61c0409d5bdac57a7bd0ce0ab01c1c916728fe4c8a03d77a25135ad481eb505c", size = 2385480, upload-time = "2025-03-10T19:27:46.425Z" }, + { url = "https://files.pythonhosted.org/packages/c1/08/9930eb4bb38c5214041c9f24f8b35e9864a7938282db986836546c782d52/grpcio_tools-1.71.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:28784f39921d061d2164a9dcda5164a69d07bf29f91f0ea50b505958292312c9", size = 5951891, upload-time = "2025-03-10T19:27:48.219Z" }, + { url = "https://files.pythonhosted.org/packages/73/65/931f29ec9c33719d48e1e30446ecce6f5d2cd4e4934fa73fbe07de41c43b/grpcio_tools-1.71.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:192808cf553cedca73f0479cc61d5684ad61f24db7a5f3c4dfe1500342425866", size = 2351967, upload-time = "2025-03-10T19:27:50.09Z" }, + { url = "https://files.pythonhosted.org/packages/b8/26/2ec8748534406214f20a4809c36efcfa88d1a26246e8312102e3ef8c295d/grpcio_tools-1.71.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:989ee9da61098230d3d4c8f8f8e27c2de796f1ff21b1c90110e636d9acd9432b", size = 2745003, upload-time = "2025-03-10T19:27:52.333Z" }, + { url = "https://files.pythonhosted.org/packages/f1/33/87b4610c86a4e10ee446b543a4d536f94ab04f828bab841f0bc1a083de72/grpcio_tools-1.71.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:541a756276c8a55dec991f6c0106ae20c8c8f5ce8d0bdbfcb01e2338d1a8192b", size = 2476455, upload-time = "2025-03-10T19:27:54.493Z" }, + { url = "https://files.pythonhosted.org/packages/00/7c/f7f0cc36a43be9d45b3ce2a55245f3c7d063a24b7930dd719929e58871a4/grpcio_tools-1.71.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:870c0097700d13c403e5517cb7750ab5b4a791ce3e71791c411a38c5468b64bd", size = 2854333, upload-time = "2025-03-10T19:27:56.693Z" }, + { url = "https://files.pythonhosted.org/packages/07/c4/34b9ea62b173c13fa7accba5f219355b320c05c80c79c3ba70fe52f47b2f/grpcio_tools-1.71.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:abd57f615e88bf93c3c6fd31f923106e3beb12f8cd2df95b0d256fa07a7a0a57", size = 3304297, upload-time = "2025-03-10T19:27:58.437Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ef/9d3449db8a07688dc3de7dcbd2a07048a128610b1a491c5c0cb3e90a00c5/grpcio_tools-1.71.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:753270e2d06d37e6d7af8967d1d059ec635ad215882041a36294f4e2fd502b2e", size = 2916212, upload-time = "2025-03-10T19:28:00.208Z" }, + { url = "https://files.pythonhosted.org/packages/2e/c6/990e8194c934dfe7cf89ef307c319fa4f2bc0b78aeca707addbfa1e502f1/grpcio_tools-1.71.0-cp312-cp312-win32.whl", hash = "sha256:0e647794bd7138b8c215e86277a9711a95cf6a03ff6f9e555d54fdf7378b9f9d", size = 948849, upload-time = "2025-03-10T19:28:01.81Z" }, + { url = "https://files.pythonhosted.org/packages/42/95/3c36d3205e6bd19853cc2420e44b6ef302eb4cfcf56498973c7e85f6c03b/grpcio_tools-1.71.0-cp312-cp312-win_amd64.whl", hash = "sha256:48debc879570972d28bfe98e4970eff25bb26da3f383e0e49829b2d2cd35ad87", size = 1120294, upload-time = "2025-03-10T19:28:03.517Z" }, + { url = "https://files.pythonhosted.org/packages/84/a7/70dc7e9957bcbaccd4dcb6cc11215e0b918f546d55599221522fe0d073e0/grpcio_tools-1.71.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:9a78d07d6c301a25ef5ede962920a522556a1dfee1ccc05795994ceb867f766c", size = 2384758, upload-time = "2025-03-10T19:28:05.327Z" }, + { url = "https://files.pythonhosted.org/packages/65/79/57320b28d0a0c5ec94095fd571a65292f8ed7e1c47e59ae4021e8a48d49b/grpcio_tools-1.71.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:580ac88141c9815557e63c9c04f5b1cdb19b4db8d0cb792b573354bde1ee8b12", size = 5951661, upload-time = "2025-03-10T19:28:07.879Z" }, + { url = "https://files.pythonhosted.org/packages/80/3d/343df5ed7c5dd66fc7a19e4ef3e97ccc4f5d802122b04cd6492f0dcd79f5/grpcio_tools-1.71.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f7c678e68ece0ae908ecae1c4314a0c2c7f83e26e281738b9609860cc2c82d96", size = 2351571, upload-time = "2025-03-10T19:28:09.909Z" }, + { url = "https://files.pythonhosted.org/packages/56/2f/b9736e8c84e880c4237f5b880c6c799b4977c5cde190999bc7ab4b2ec445/grpcio_tools-1.71.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56ecd6cc89b5e5eed1de5eb9cafce86c9c9043ee3840888cc464d16200290b53", size = 2744580, upload-time = "2025-03-10T19:28:11.866Z" }, + { url = "https://files.pythonhosted.org/packages/76/9b/bdb384967353da7bf64bac4232f4cf8ae43f19d0f2f640978d4d4197e667/grpcio_tools-1.71.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e52a041afc20ab2431d756b6295d727bd7adee813b21b06a3483f4a7a15ea15f", size = 2475978, upload-time = "2025-03-10T19:28:14.236Z" }, + { url = "https://files.pythonhosted.org/packages/26/71/1411487fd7862d347b98fda5e3beef611a71b2ac2faac62a965d9e2536b3/grpcio_tools-1.71.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2a1712f12102b60c8d92779b89d0504e0d6f3a59f2b933e5622b8583f5c02992", size = 2853314, upload-time = "2025-03-10T19:28:16.085Z" }, + { url = "https://files.pythonhosted.org/packages/03/06/59d0523eb1ba2f64edc72cb150152fa1b2e77061cae3ef3ecd3ef2a87f51/grpcio_tools-1.71.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:41878cb7a75477e62fdd45e7e9155b3af1b7a5332844021e2511deaf99ac9e6c", size = 3303981, upload-time = "2025-03-10T19:28:18.129Z" }, + { url = "https://files.pythonhosted.org/packages/c2/71/fb9fb49f2b738ec1dfbbc8cdce0b26e5f9c5fc0edef72e453580620d6a36/grpcio_tools-1.71.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:682e958b476049ccc14c71bedf3f979bced01f6e0c04852efc5887841a32ad6b", size = 2915876, upload-time = "2025-03-10T19:28:20.045Z" }, + { url = "https://files.pythonhosted.org/packages/bd/0f/0d49f6fe6fa2d09e9820dd9eeb30437e86002303076be2b6ada0fb52b8f2/grpcio_tools-1.71.0-cp313-cp313-win32.whl", hash = "sha256:0ccfb837152b7b858b9f26bb110b3ae8c46675d56130f6c2f03605c4f129be13", size = 948245, upload-time = "2025-03-10T19:28:21.876Z" }, + { url = "https://files.pythonhosted.org/packages/bb/14/ab131a39187bfea950280b2277a82d2033469fe8c86f73b10b19f53cc5ca/grpcio_tools-1.71.0-cp313-cp313-win_amd64.whl", hash = "sha256:ffff9bc5eacb34dd26b487194f7d44a3e64e752fc2cf049d798021bf25053b87", size = 1119649, upload-time = "2025-03-10T19:28:23.679Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -366,6 +623,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819, upload-time = "2023-12-22T08:01:19.89Z" }, ] +[[package]] +name = "identify" +version = "2.6.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/88/d193a27416618628a5eea64e3223acd800b40749a96ffb322a9b55a49ed1/identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6", size = 99254, upload-time = "2025-05-23T20:37:53.3Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/cd/18f8da995b658420625f7ef13f037be53ae04ec5ad33f9b718240dcfd48c/identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", size = 99145, upload-time = "2025-05-23T20:37:51.495Z" }, +] + [[package]] name = "idna" version = "3.10" @@ -377,14 +643,14 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "8.6.1" +version = "8.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767, upload-time = "2025-01-20T22:21:30.429Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971, upload-time = "2025-01-20T22:21:29.177Z" }, + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, ] [[package]] @@ -499,7 +765,7 @@ wheels = [ [[package]] name = "mypy" -version = "1.16.0" +version = "1.16.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, @@ -507,33 +773,33 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/38/13c2f1abae94d5ea0354e146b95a1be9b2137a0d506728e0da037c4276f6/mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab", size = 3323139, upload-time = "2025-05-29T13:46:12.532Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/5e/a0485f0608a3d67029d3d73cec209278b025e3493a3acfda3ef3a88540fd/mypy-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7909541fef256527e5ee9c0a7e2aeed78b6cda72ba44298d1334fe7881b05c5c", size = 10967416, upload-time = "2025-05-29T13:34:17.783Z" }, - { url = "https://files.pythonhosted.org/packages/4b/53/5837c221f74c0d53a4bfc3003296f8179c3a2a7f336d7de7bbafbe96b688/mypy-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e71d6f0090c2256c713ed3d52711d01859c82608b5d68d4fa01a3fe30df95571", size = 10087654, upload-time = "2025-05-29T13:32:37.878Z" }, - { url = "https://files.pythonhosted.org/packages/29/59/5fd2400352c3093bed4c09017fe671d26bc5bb7e6ef2d4bf85f2a2488104/mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:936ccfdd749af4766be824268bfe22d1db9eb2f34a3ea1d00ffbe5b5265f5491", size = 11875192, upload-time = "2025-05-29T13:34:54.281Z" }, - { url = "https://files.pythonhosted.org/packages/ad/3e/4bfec74663a64c2012f3e278dbc29ffe82b121bc551758590d1b6449ec0c/mypy-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4086883a73166631307fdd330c4a9080ce24913d4f4c5ec596c601b3a4bdd777", size = 12612939, upload-time = "2025-05-29T13:33:14.766Z" }, - { url = "https://files.pythonhosted.org/packages/88/1f/fecbe3dcba4bf2ca34c26ca016383a9676711907f8db4da8354925cbb08f/mypy-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:feec38097f71797da0231997e0de3a58108c51845399669ebc532c815f93866b", size = 12874719, upload-time = "2025-05-29T13:21:52.09Z" }, - { url = "https://files.pythonhosted.org/packages/f3/51/c2d280601cd816c43dfa512a759270d5a5ef638d7ac9bea9134c8305a12f/mypy-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a8da6a0ee9a9770b8ff61b39c0bb07971cda90e7297f4213741b48a0cc8d93", size = 9487053, upload-time = "2025-05-29T13:33:29.797Z" }, - { url = "https://files.pythonhosted.org/packages/24/c4/ff2f79db7075c274fe85b5fff8797d29c6b61b8854c39e3b7feb556aa377/mypy-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f826aaa7ff8443bac6a494cf743f591488ea940dd360e7dd330e30dd772a5ab", size = 10884498, upload-time = "2025-05-29T13:18:54.066Z" }, - { url = "https://files.pythonhosted.org/packages/02/07/12198e83006235f10f6a7808917376b5d6240a2fd5dce740fe5d2ebf3247/mypy-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82d056e6faa508501af333a6af192c700b33e15865bda49611e3d7d8358ebea2", size = 10011755, upload-time = "2025-05-29T13:34:00.851Z" }, - { url = "https://files.pythonhosted.org/packages/f1/9b/5fd5801a72b5d6fb6ec0105ea1d0e01ab2d4971893076e558d4b6d6b5f80/mypy-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089bedc02307c2548eb51f426e085546db1fa7dd87fbb7c9fa561575cf6eb1ff", size = 11800138, upload-time = "2025-05-29T13:32:55.082Z" }, - { url = "https://files.pythonhosted.org/packages/2e/81/a117441ea5dfc3746431e51d78a4aca569c677aa225bca2cc05a7c239b61/mypy-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a2322896003ba66bbd1318c10d3afdfe24e78ef12ea10e2acd985e9d684a666", size = 12533156, upload-time = "2025-05-29T13:19:12.963Z" }, - { url = "https://files.pythonhosted.org/packages/3f/38/88ec57c6c86014d3f06251e00f397b5a7daa6888884d0abf187e4f5f587f/mypy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:021a68568082c5b36e977d54e8f1de978baf401a33884ffcea09bd8e88a98f4c", size = 12742426, upload-time = "2025-05-29T13:20:22.72Z" }, - { url = "https://files.pythonhosted.org/packages/bd/53/7e9d528433d56e6f6f77ccf24af6ce570986c2d98a5839e4c2009ef47283/mypy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:54066fed302d83bf5128632d05b4ec68412e1f03ef2c300434057d66866cea4b", size = 9478319, upload-time = "2025-05-29T13:21:17.582Z" }, - { url = "https://files.pythonhosted.org/packages/70/cf/158e5055e60ca2be23aec54a3010f89dcffd788732634b344fc9cb1e85a0/mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13", size = 11062927, upload-time = "2025-05-29T13:35:52.328Z" }, - { url = "https://files.pythonhosted.org/packages/94/34/cfff7a56be1609f5d10ef386342ce3494158e4d506516890142007e6472c/mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090", size = 10083082, upload-time = "2025-05-29T13:35:33.378Z" }, - { url = "https://files.pythonhosted.org/packages/b3/7f/7242062ec6288c33d8ad89574df87c3903d394870e5e6ba1699317a65075/mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1", size = 11828306, upload-time = "2025-05-29T13:21:02.164Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5f/b392f7b4f659f5b619ce5994c5c43caab3d80df2296ae54fa888b3d17f5a/mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8", size = 12702764, upload-time = "2025-05-29T13:20:42.826Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c0/7646ef3a00fa39ac9bc0938626d9ff29d19d733011be929cfea59d82d136/mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730", size = 12896233, upload-time = "2025-05-29T13:18:37.446Z" }, - { url = "https://files.pythonhosted.org/packages/6d/38/52f4b808b3fef7f0ef840ee8ff6ce5b5d77381e65425758d515cdd4f5bb5/mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec", size = 9565547, upload-time = "2025-05-29T13:20:02.836Z" }, - { url = "https://files.pythonhosted.org/packages/97/9c/ca03bdbefbaa03b264b9318a98950a9c683e06472226b55472f96ebbc53d/mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b", size = 11059753, upload-time = "2025-05-29T13:18:18.167Z" }, - { url = "https://files.pythonhosted.org/packages/36/92/79a969b8302cfe316027c88f7dc6fee70129490a370b3f6eb11d777749d0/mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0", size = 10073338, upload-time = "2025-05-29T13:19:48.079Z" }, - { url = "https://files.pythonhosted.org/packages/14/9b/a943f09319167da0552d5cd722104096a9c99270719b1afeea60d11610aa/mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b", size = 11827764, upload-time = "2025-05-29T13:46:04.47Z" }, - { url = "https://files.pythonhosted.org/packages/ec/64/ff75e71c65a0cb6ee737287c7913ea155845a556c64144c65b811afdb9c7/mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d", size = 12701356, upload-time = "2025-05-29T13:35:13.553Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ad/0e93c18987a1182c350f7a5fab70550852f9fabe30ecb63bfbe51b602074/mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52", size = 12900745, upload-time = "2025-05-29T13:17:24.409Z" }, - { url = "https://files.pythonhosted.org/packages/28/5d/036c278d7a013e97e33f08c047fe5583ab4f1fc47c9a49f985f1cdd2a2d7/mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb", size = 9572200, upload-time = "2025-05-29T13:33:44.92Z" }, - { url = "https://files.pythonhosted.org/packages/99/a3/6ed10530dec8e0fdc890d81361260c9ef1f5e5c217ad8c9b21ecb2b8366b/mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031", size = 2265773, upload-time = "2025-05-29T13:35:18.762Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/81/69/92c7fa98112e4d9eb075a239caa4ef4649ad7d441545ccffbd5e34607cbb/mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab", size = 3324747, upload-time = "2025-06-16T16:51:35.145Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/12/2bf23a80fcef5edb75de9a1e295d778e0f46ea89eb8b115818b663eff42b/mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a", size = 10958644, upload-time = "2025-06-16T16:51:11.649Z" }, + { url = "https://files.pythonhosted.org/packages/08/50/bfe47b3b278eacf348291742fd5e6613bbc4b3434b72ce9361896417cfe5/mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72", size = 10087033, upload-time = "2025-06-16T16:35:30.089Z" }, + { url = "https://files.pythonhosted.org/packages/21/de/40307c12fe25675a0776aaa2cdd2879cf30d99eec91b898de00228dc3ab5/mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea", size = 11875645, upload-time = "2025-06-16T16:35:48.49Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d8/85bdb59e4a98b7a31495bd8f1a4445d8ffc86cde4ab1f8c11d247c11aedc/mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574", size = 12616986, upload-time = "2025-06-16T16:48:39.526Z" }, + { url = "https://files.pythonhosted.org/packages/0e/d0/bb25731158fa8f8ee9e068d3e94fcceb4971fedf1424248496292512afe9/mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d", size = 12878632, upload-time = "2025-06-16T16:36:08.195Z" }, + { url = "https://files.pythonhosted.org/packages/2d/11/822a9beb7a2b825c0cb06132ca0a5183f8327a5e23ef89717c9474ba0bc6/mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6", size = 9484391, upload-time = "2025-06-16T16:37:56.151Z" }, + { url = "https://files.pythonhosted.org/packages/9a/61/ec1245aa1c325cb7a6c0f8570a2eee3bfc40fa90d19b1267f8e50b5c8645/mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc", size = 10890557, upload-time = "2025-06-16T16:37:21.421Z" }, + { url = "https://files.pythonhosted.org/packages/6b/bb/6eccc0ba0aa0c7a87df24e73f0ad34170514abd8162eb0c75fd7128171fb/mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782", size = 10012921, upload-time = "2025-06-16T16:51:28.659Z" }, + { url = "https://files.pythonhosted.org/packages/5f/80/b337a12e2006715f99f529e732c5f6a8c143bb58c92bb142d5ab380963a5/mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507", size = 11802887, upload-time = "2025-06-16T16:50:53.627Z" }, + { url = "https://files.pythonhosted.org/packages/d9/59/f7af072d09793d581a745a25737c7c0a945760036b16aeb620f658a017af/mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca", size = 12531658, upload-time = "2025-06-16T16:33:55.002Z" }, + { url = "https://files.pythonhosted.org/packages/82/c4/607672f2d6c0254b94a646cfc45ad589dd71b04aa1f3d642b840f7cce06c/mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4", size = 12732486, upload-time = "2025-06-16T16:37:03.301Z" }, + { url = "https://files.pythonhosted.org/packages/b6/5e/136555ec1d80df877a707cebf9081bd3a9f397dedc1ab9750518d87489ec/mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6", size = 9479482, upload-time = "2025-06-16T16:47:37.48Z" }, + { url = "https://files.pythonhosted.org/packages/b4/d6/39482e5fcc724c15bf6280ff5806548c7185e0c090712a3736ed4d07e8b7/mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d", size = 11066493, upload-time = "2025-06-16T16:47:01.683Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e5/26c347890efc6b757f4d5bb83f4a0cf5958b8cf49c938ac99b8b72b420a6/mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9", size = 10081687, upload-time = "2025-06-16T16:48:19.367Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/b5cb264c97b86914487d6a24bd8688c0172e37ec0f43e93b9691cae9468b/mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79", size = 11839723, upload-time = "2025-06-16T16:49:20.912Z" }, + { url = "https://files.pythonhosted.org/packages/15/f8/491997a9b8a554204f834ed4816bda813aefda31cf873bb099deee3c9a99/mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15", size = 12722980, upload-time = "2025-06-16T16:37:40.929Z" }, + { url = "https://files.pythonhosted.org/packages/df/f0/2bd41e174b5fd93bc9de9a28e4fb673113633b8a7f3a607fa4a73595e468/mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd", size = 12903328, upload-time = "2025-06-16T16:34:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/61/81/5572108a7bec2c46b8aff7e9b524f371fe6ab5efb534d38d6b37b5490da8/mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b", size = 9562321, upload-time = "2025-06-16T16:48:58.823Z" }, + { url = "https://files.pythonhosted.org/packages/28/e3/96964af4a75a949e67df4b95318fe2b7427ac8189bbc3ef28f92a1c5bc56/mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438", size = 11063480, upload-time = "2025-06-16T16:47:56.205Z" }, + { url = "https://files.pythonhosted.org/packages/f5/4d/cd1a42b8e5be278fab7010fb289d9307a63e07153f0ae1510a3d7b703193/mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536", size = 10090538, upload-time = "2025-06-16T16:46:43.92Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4f/c3c6b4b66374b5f68bab07c8cabd63a049ff69796b844bc759a0ca99bb2a/mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f", size = 11836839, upload-time = "2025-06-16T16:36:28.039Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7e/81ca3b074021ad9775e5cb97ebe0089c0f13684b066a750b7dc208438403/mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359", size = 12715634, upload-time = "2025-06-16T16:50:34.441Z" }, + { url = "https://files.pythonhosted.org/packages/e9/95/bdd40c8be346fa4c70edb4081d727a54d0a05382d84966869738cfa8a497/mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be", size = 12895584, upload-time = "2025-06-16T16:34:54.857Z" }, + { url = "https://files.pythonhosted.org/packages/5a/fd/d486a0827a1c597b3b48b1bdef47228a6e9ee8102ab8c28f944cb83b65dc/mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee", size = 9573886, upload-time = "2025-06-16T16:36:43.589Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d3/53e684e78e07c1a2bf7105715e5edd09ce951fc3f47cf9ed095ec1b7a037/mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37", size = 2265923, upload-time = "2025-06-16T16:48:02.366Z" }, ] [[package]] @@ -545,44 +811,53 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, +] + [[package]] name = "opentelemetry-api" -version = "1.33.1" +version = "1.34.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "deprecated" }, { name = "importlib-metadata" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/8d/1f5a45fbcb9a7d87809d460f09dc3399e3fbd31d7f3e14888345e9d29951/opentelemetry_api-1.33.1.tar.gz", hash = "sha256:1c6055fc0a2d3f23a50c7e17e16ef75ad489345fd3df1f8b8af7c0bbf8a109e8", size = 65002, upload-time = "2025-05-16T18:52:41.146Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4d/5e/94a8cb759e4e409022229418294e098ca7feca00eb3c467bb20cbd329bda/opentelemetry_api-1.34.1.tar.gz", hash = "sha256:64f0bd06d42824843731d05beea88d4d4b6ae59f9fe347ff7dfa2cc14233bbb3", size = 64987, upload-time = "2025-06-10T08:55:19.818Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/44/4c45a34def3506122ae61ad684139f0bbc4e00c39555d4f7e20e0e001c8a/opentelemetry_api-1.33.1-py3-none-any.whl", hash = "sha256:4db83ebcf7ea93e64637ec6ee6fabee45c5cbe4abd9cf3da95c43828ddb50b83", size = 65771, upload-time = "2025-05-16T18:52:17.419Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3a/2ba85557e8dc024c0842ad22c570418dc02c36cbd1ab4b832a93edf071b8/opentelemetry_api-1.34.1-py3-none-any.whl", hash = "sha256:b7df4cb0830d5a6c29ad0c0691dbae874d8daefa934b8b1d642de48323d32a8c", size = 65767, upload-time = "2025-06-10T08:54:56.717Z" }, ] [[package]] name = "opentelemetry-sdk" -version = "1.33.1" +version = "1.34.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/67/12/909b98a7d9b110cce4b28d49b2e311797cffdce180371f35eba13a72dd00/opentelemetry_sdk-1.33.1.tar.gz", hash = "sha256:85b9fcf7c3d23506fbc9692fd210b8b025a1920535feec50bd54ce203d57a531", size = 161885, upload-time = "2025-05-16T18:52:52.832Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/41/fe20f9036433da8e0fcef568984da4c1d1c771fa072ecd1a4d98779dccdd/opentelemetry_sdk-1.34.1.tar.gz", hash = "sha256:8091db0d763fcd6098d4781bbc80ff0971f94e260739aa6afe6fd379cdf3aa4d", size = 159441, upload-time = "2025-06-10T08:55:33.028Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/df/8e/ae2d0742041e0bd7fe0d2dcc5e7cce51dcf7d3961a26072d5b43cc8fa2a7/opentelemetry_sdk-1.33.1-py3-none-any.whl", hash = "sha256:19ea73d9a01be29cacaa5d6c8ce0adc0b7f7b4d58cc52f923e4413609f670112", size = 118950, upload-time = "2025-05-16T18:52:37.297Z" }, + { url = "https://files.pythonhosted.org/packages/07/1b/def4fe6aa73f483cabf4c748f4c25070d5f7604dcc8b52e962983491b29e/opentelemetry_sdk-1.34.1-py3-none-any.whl", hash = "sha256:308effad4059562f1d92163c61c8141df649da24ce361827812c40abb2a1e96e", size = 118477, upload-time = "2025-06-10T08:55:16.02Z" }, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.54b1" +version = "0.55b1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "deprecated" }, { name = "opentelemetry-api" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/2c/d7990fc1ffc82889d466e7cd680788ace44a26789809924813b164344393/opentelemetry_semantic_conventions-0.54b1.tar.gz", hash = "sha256:d1cecedae15d19bdaafca1e56b29a66aa286f50b5d08f036a145c7f3e9ef9cee", size = 118642, upload-time = "2025-05-16T18:52:53.962Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/f0/f33458486da911f47c4aa6db9bda308bb80f3236c111bf848bd870c16b16/opentelemetry_semantic_conventions-0.55b1.tar.gz", hash = "sha256:ef95b1f009159c28d7a7849f5cbc71c4c34c845bb514d66adfdf1b3fff3598b3", size = 119829, upload-time = "2025-06-10T08:55:33.881Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/80/08b1698c52ff76d96ba440bf15edc2f4bc0a279868778928e947c1004bdd/opentelemetry_semantic_conventions-0.54b1-py3-none-any.whl", hash = "sha256:29dab644a7e435b58d3a3918b58c333c92686236b30f7891d5e51f02933ca60d", size = 194938, upload-time = "2025-05-16T18:52:38.796Z" }, + { url = "https://files.pythonhosted.org/packages/1a/89/267b0af1b1d0ba828f0e60642b6a5116ac1fd917cde7fc02821627029bd1/opentelemetry_semantic_conventions-0.55b1-py3-none-any.whl", hash = "sha256:5da81dfdf7d52e3d37f8fe88d5e771e191de924cfff5f550ab0b8f7b2409baed", size = 196223, upload-time = "2025-06-10T08:55:17.638Z" }, ] [[package]] @@ -621,9 +896,72 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] +[[package]] +name = "pre-commit" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424, upload-time = "2025-03-18T21:35:20.987Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" }, +] + +[[package]] +name = "proto-plus" +version = "1.26.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142, upload-time = "2025-03-10T15:54:38.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163, upload-time = "2025-03-10T15:54:37.335Z" }, +] + +[[package]] +name = "protobuf" +version = "5.29.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/29/d09e70352e4e88c9c7a198d5645d7277811448d76c23b00345670f7c8a38/protobuf-5.29.5.tar.gz", hash = "sha256:bc1463bafd4b0929216c35f437a8e28731a2b7fe3d98bb77a600efced5a15c84", size = 425226, upload-time = "2025-05-28T23:51:59.82Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/11/6e40e9fc5bba02988a214c07cf324595789ca7820160bfd1f8be96e48539/protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079", size = 422963, upload-time = "2025-05-28T23:51:41.204Z" }, + { url = "https://files.pythonhosted.org/packages/81/7f/73cefb093e1a2a7c3ffd839e6f9fcafb7a427d300c7f8aef9c64405d8ac6/protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc", size = 434818, upload-time = "2025-05-28T23:51:44.297Z" }, + { url = "https://files.pythonhosted.org/packages/dd/73/10e1661c21f139f2c6ad9b23040ff36fee624310dc28fba20d33fdae124c/protobuf-5.29.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e38c5add5a311f2a6eb0340716ef9b039c1dfa428b28f25a7838ac329204a671", size = 418091, upload-time = "2025-05-28T23:51:45.907Z" }, + { url = "https://files.pythonhosted.org/packages/6c/04/98f6f8cf5b07ab1294c13f34b4e69b3722bb609c5b701d6c169828f9f8aa/protobuf-5.29.5-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:fa18533a299d7ab6c55a238bf8629311439995f2e7eca5caaff08663606e9015", size = 319824, upload-time = "2025-05-28T23:51:47.545Z" }, + { url = "https://files.pythonhosted.org/packages/85/e4/07c80521879c2d15f321465ac24c70efe2381378c00bf5e56a0f4fbac8cd/protobuf-5.29.5-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:63848923da3325e1bf7e9003d680ce6e14b07e55d0473253a690c3a8b8fd6e61", size = 319942, upload-time = "2025-05-28T23:51:49.11Z" }, + { url = "https://files.pythonhosted.org/packages/7e/cc/7e77861000a0691aeea8f4566e5d3aa716f2b1dece4a24439437e41d3d25/protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5", size = 172823, upload-time = "2025-05-28T23:51:58.157Z" }, +] + +[[package]] +name = "pyasn1" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, +] + [[package]] name = "pydantic" -version = "2.11.5" +version = "2.11.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -631,9 +969,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, ] [[package]] @@ -723,9 +1061,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, ] +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, +] + [[package]] name = "pytest" -version = "8.3.5" +version = "8.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -733,11 +1080,12 @@ dependencies = [ { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, + { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/aa/405082ce2749be5398045152251ac69c0f3578c7077efc53431303af97ce/pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6", size = 1515232, upload-time = "2025-06-02T17:36:30.03Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" }, + { url = "https://files.pythonhosted.org/packages/2f/de/afa024cbe022b1b318a3d224125aa24939e99b4ff6f22e0ba639a2eaee47/pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e", size = 363797, upload-time = "2025-06-02T17:36:27.859Z" }, ] [[package]] @@ -754,15 +1102,16 @@ wheels = [ [[package]] name = "pytest-cov" -version = "6.1.1" +version = "6.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857, upload-time = "2025-04-05T14:07:51.592Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841, upload-time = "2025-04-05T14:07:49.641Z" }, + { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, ] [[package]] @@ -821,29 +1170,77 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, ] +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, +] + +[[package]] +name = "respx" +version = "0.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/7c/96bd0bc759cf009675ad1ee1f96535edcb11e9666b985717eb8c87192a95/respx-0.22.0.tar.gz", hash = "sha256:3c8924caa2a50bd71aefc07aa812f2466ff489f1848c96e954a5362d17095d91", size = 28439, upload-time = "2024-12-19T22:33:59.374Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/67/afbb0978d5399bc9ea200f1d4489a23c9a1dad4eee6376242b8182389c79/respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0", size = 25127, upload-time = "2024-12-19T22:33:57.837Z" }, +] + +[[package]] +name = "rsa" +version = "4.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, +] + [[package]] name = "ruff" -version = "0.11.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/0a/92416b159ec00cdf11e5882a9d80d29bf84bba3dbebc51c4898bfbca1da6/ruff-0.11.12.tar.gz", hash = "sha256:43cf7f69c7d7c7d7513b9d59c5d8cafd704e05944f978614aa9faff6ac202603", size = 4202289, upload-time = "2025-05-29T13:31:40.037Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/cc/53eb79f012d15e136d40a8e8fc519ba8f55a057f60b29c2df34efd47c6e3/ruff-0.11.12-py3-none-linux_armv6l.whl", hash = "sha256:c7680aa2f0d4c4f43353d1e72123955c7a2159b8646cd43402de6d4a3a25d7cc", size = 10285597, upload-time = "2025-05-29T13:30:57.539Z" }, - { url = "https://files.pythonhosted.org/packages/e7/d7/73386e9fb0232b015a23f62fea7503f96e29c29e6c45461d4a73bac74df9/ruff-0.11.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2cad64843da9f134565c20bcc430642de897b8ea02e2e79e6e02a76b8dcad7c3", size = 11053154, upload-time = "2025-05-29T13:31:00.865Z" }, - { url = "https://files.pythonhosted.org/packages/4e/eb/3eae144c5114e92deb65a0cb2c72326c8469e14991e9bc3ec0349da1331c/ruff-0.11.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9b6886b524a1c659cee1758140138455d3c029783d1b9e643f3624a5ee0cb0aa", size = 10403048, upload-time = "2025-05-29T13:31:03.413Z" }, - { url = "https://files.pythonhosted.org/packages/29/64/20c54b20e58b1058db6689e94731f2a22e9f7abab74e1a758dfba058b6ca/ruff-0.11.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc3a3690aad6e86c1958d3ec3c38c4594b6ecec75c1f531e84160bd827b2012", size = 10597062, upload-time = "2025-05-29T13:31:05.539Z" }, - { url = "https://files.pythonhosted.org/packages/29/3a/79fa6a9a39422a400564ca7233a689a151f1039110f0bbbabcb38106883a/ruff-0.11.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f97fdbc2549f456c65b3b0048560d44ddd540db1f27c778a938371424b49fe4a", size = 10155152, upload-time = "2025-05-29T13:31:07.986Z" }, - { url = "https://files.pythonhosted.org/packages/e5/a4/22c2c97b2340aa968af3a39bc38045e78d36abd4ed3fa2bde91c31e712e3/ruff-0.11.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74adf84960236961090e2d1348c1a67d940fd12e811a33fb3d107df61eef8fc7", size = 11723067, upload-time = "2025-05-29T13:31:10.57Z" }, - { url = "https://files.pythonhosted.org/packages/bc/cf/3e452fbd9597bcd8058856ecd42b22751749d07935793a1856d988154151/ruff-0.11.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b56697e5b8bcf1d61293ccfe63873aba08fdbcbbba839fc046ec5926bdb25a3a", size = 12460807, upload-time = "2025-05-29T13:31:12.88Z" }, - { url = "https://files.pythonhosted.org/packages/2f/ec/8f170381a15e1eb7d93cb4feef8d17334d5a1eb33fee273aee5d1f8241a3/ruff-0.11.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d47afa45e7b0eaf5e5969c6b39cbd108be83910b5c74626247e366fd7a36a13", size = 12063261, upload-time = "2025-05-29T13:31:15.236Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/57208f8c0a8153a14652a85f4116c0002148e83770d7a41f2e90b52d2b4e/ruff-0.11.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bf9603fe1bf949de8b09a2da896f05c01ed7a187f4a386cdba6760e7f61be", size = 11329601, upload-time = "2025-05-29T13:31:18.68Z" }, - { url = "https://files.pythonhosted.org/packages/c3/56/edf942f7fdac5888094d9ffa303f12096f1a93eb46570bcf5f14c0c70880/ruff-0.11.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08033320e979df3b20dba567c62f69c45e01df708b0f9c83912d7abd3e0801cd", size = 11522186, upload-time = "2025-05-29T13:31:21.216Z" }, - { url = "https://files.pythonhosted.org/packages/ed/63/79ffef65246911ed7e2290aeece48739d9603b3a35f9529fec0fc6c26400/ruff-0.11.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:929b7706584f5bfd61d67d5070f399057d07c70585fa8c4491d78ada452d3bef", size = 10449032, upload-time = "2025-05-29T13:31:23.417Z" }, - { url = "https://files.pythonhosted.org/packages/88/19/8c9d4d8a1c2a3f5a1ea45a64b42593d50e28b8e038f1aafd65d6b43647f3/ruff-0.11.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7de4a73205dc5756b8e09ee3ed67c38312dce1aa28972b93150f5751199981b5", size = 10129370, upload-time = "2025-05-29T13:31:25.777Z" }, - { url = "https://files.pythonhosted.org/packages/bc/0f/2d15533eaa18f460530a857e1778900cd867ded67f16c85723569d54e410/ruff-0.11.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2635c2a90ac1b8ca9e93b70af59dfd1dd2026a40e2d6eebaa3efb0465dd9cf02", size = 11123529, upload-time = "2025-05-29T13:31:28.396Z" }, - { url = "https://files.pythonhosted.org/packages/4f/e2/4c2ac669534bdded835356813f48ea33cfb3a947dc47f270038364587088/ruff-0.11.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d05d6a78a89166f03f03a198ecc9d18779076ad0eec476819467acb401028c0c", size = 11577642, upload-time = "2025-05-29T13:31:30.647Z" }, - { url = "https://files.pythonhosted.org/packages/a7/9b/c9ddf7f924d5617a1c94a93ba595f4b24cb5bc50e98b94433ab3f7ad27e5/ruff-0.11.12-py3-none-win32.whl", hash = "sha256:f5a07f49767c4be4772d161bfc049c1f242db0cfe1bd976e0f0886732a4765d6", size = 10475511, upload-time = "2025-05-29T13:31:32.917Z" }, - { url = "https://files.pythonhosted.org/packages/fd/d6/74fb6d3470c1aada019ffff33c0f9210af746cca0a4de19a1f10ce54968a/ruff-0.11.12-py3-none-win_amd64.whl", hash = "sha256:5a4d9f8030d8c3a45df201d7fb3ed38d0219bccd7955268e863ee4a115fa0832", size = 11523573, upload-time = "2025-05-29T13:31:35.782Z" }, - { url = "https://files.pythonhosted.org/packages/44/42/d58086ec20f52d2b0140752ae54b355ea2be2ed46f914231136dd1effcc7/ruff-0.11.12-py3-none-win_arm64.whl", hash = "sha256:65194e37853158d368e333ba282217941029a28ea90913c67e558c611d04daa5", size = 10697770, upload-time = "2025-05-29T13:31:38.009Z" }, +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/90/5255432602c0b196a0da6720f6f76b93eb50baef46d3c9b0025e2f9acbf3/ruff-0.12.0.tar.gz", hash = "sha256:4d047db3662418d4a848a3fdbfaf17488b34b62f527ed6f10cb8afd78135bc5c", size = 4376101, upload-time = "2025-06-17T15:19:26.217Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/fd/b46bb20e14b11ff49dbc74c61de352e0dc07fb650189513631f6fb5fc69f/ruff-0.12.0-py3-none-linux_armv6l.whl", hash = "sha256:5652a9ecdb308a1754d96a68827755f28d5dfb416b06f60fd9e13f26191a8848", size = 10311554, upload-time = "2025-06-17T15:18:45.792Z" }, + { url = "https://files.pythonhosted.org/packages/e7/d3/021dde5a988fa3e25d2468d1dadeea0ae89dc4bc67d0140c6e68818a12a1/ruff-0.12.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:05ed0c914fabc602fc1f3b42c53aa219e5736cb030cdd85640c32dbc73da74a6", size = 11118435, upload-time = "2025-06-17T15:18:49.064Z" }, + { url = "https://files.pythonhosted.org/packages/07/a2/01a5acf495265c667686ec418f19fd5c32bcc326d4c79ac28824aecd6a32/ruff-0.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:07a7aa9b69ac3fcfda3c507916d5d1bca10821fe3797d46bad10f2c6de1edda0", size = 10466010, upload-time = "2025-06-17T15:18:51.341Z" }, + { url = "https://files.pythonhosted.org/packages/4c/57/7caf31dd947d72e7aa06c60ecb19c135cad871a0a8a251723088132ce801/ruff-0.12.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7731c3eec50af71597243bace7ec6104616ca56dda2b99c89935fe926bdcd48", size = 10661366, upload-time = "2025-06-17T15:18:53.29Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ba/aa393b972a782b4bc9ea121e0e358a18981980856190d7d2b6187f63e03a/ruff-0.12.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:952d0630eae628250ab1c70a7fffb641b03e6b4a2d3f3ec6c1d19b4ab6c6c807", size = 10173492, upload-time = "2025-06-17T15:18:55.262Z" }, + { url = "https://files.pythonhosted.org/packages/d7/50/9349ee777614bc3062fc6b038503a59b2034d09dd259daf8192f56c06720/ruff-0.12.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c021f04ea06966b02614d442e94071781c424ab8e02ec7af2f037b4c1e01cc82", size = 11761739, upload-time = "2025-06-17T15:18:58.906Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/ad459de67c70ec112e2ba7206841c8f4eb340a03ee6a5cabc159fe558b8e/ruff-0.12.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d235618283718ee2fe14db07f954f9b2423700919dc688eacf3f8797a11315c", size = 12537098, upload-time = "2025-06-17T15:19:01.316Z" }, + { url = "https://files.pythonhosted.org/packages/ed/50/15ad9c80ebd3c4819f5bd8883e57329f538704ed57bac680d95cb6627527/ruff-0.12.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0758038f81beec8cc52ca22de9685b8ae7f7cc18c013ec2050012862cc9165", size = 12154122, upload-time = "2025-06-17T15:19:03.727Z" }, + { url = "https://files.pythonhosted.org/packages/76/e6/79b91e41bc8cc3e78ee95c87093c6cacfa275c786e53c9b11b9358026b3d/ruff-0.12.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:139b3d28027987b78fc8d6cfb61165447bdf3740e650b7c480744873688808c2", size = 11363374, upload-time = "2025-06-17T15:19:05.875Z" }, + { url = "https://files.pythonhosted.org/packages/db/c3/82b292ff8a561850934549aa9dc39e2c4e783ab3c21debe55a495ddf7827/ruff-0.12.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68853e8517b17bba004152aebd9dd77d5213e503a5f2789395b25f26acac0da4", size = 11587647, upload-time = "2025-06-17T15:19:08.246Z" }, + { url = "https://files.pythonhosted.org/packages/2b/42/d5760d742669f285909de1bbf50289baccb647b53e99b8a3b4f7ce1b2001/ruff-0.12.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3a9512af224b9ac4757f7010843771da6b2b0935a9e5e76bb407caa901a1a514", size = 10527284, upload-time = "2025-06-17T15:19:10.37Z" }, + { url = "https://files.pythonhosted.org/packages/19/f6/fcee9935f25a8a8bba4adbae62495c39ef281256693962c2159e8b284c5f/ruff-0.12.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b08df3d96db798e5beb488d4df03011874aff919a97dcc2dd8539bb2be5d6a88", size = 10158609, upload-time = "2025-06-17T15:19:12.286Z" }, + { url = "https://files.pythonhosted.org/packages/37/fb/057febf0eea07b9384787bfe197e8b3384aa05faa0d6bd844b94ceb29945/ruff-0.12.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6a315992297a7435a66259073681bb0d8647a826b7a6de45c6934b2ca3a9ed51", size = 11141462, upload-time = "2025-06-17T15:19:15.195Z" }, + { url = "https://files.pythonhosted.org/packages/10/7c/1be8571011585914b9d23c95b15d07eec2d2303e94a03df58294bc9274d4/ruff-0.12.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1e55e44e770e061f55a7dbc6e9aed47feea07731d809a3710feda2262d2d4d8a", size = 11641616, upload-time = "2025-06-17T15:19:17.6Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ef/b960ab4818f90ff59e571d03c3f992828d4683561095e80f9ef31f3d58b7/ruff-0.12.0-py3-none-win32.whl", hash = "sha256:7162a4c816f8d1555eb195c46ae0bd819834d2a3f18f98cc63819a7b46f474fb", size = 10525289, upload-time = "2025-06-17T15:19:19.688Z" }, + { url = "https://files.pythonhosted.org/packages/34/93/8b16034d493ef958a500f17cda3496c63a537ce9d5a6479feec9558f1695/ruff-0.12.0-py3-none-win_amd64.whl", hash = "sha256:d00b7a157b8fb6d3827b49d3324da34a1e3f93492c1f97b08e222ad7e9b291e0", size = 11598311, upload-time = "2025-06-17T15:19:21.785Z" }, + { url = "https://files.pythonhosted.org/packages/d0/33/4d3e79e4a84533d6cd526bfb42c020a23256ae5e4265d858bd1287831f7d/ruff-0.12.0-py3-none-win_arm64.whl", hash = "sha256:8cd24580405ad8c1cc64d61725bca091d6b6da7eb3d36f72cc605467069d7e8b", size = 10724946, upload-time = "2025-06-17T15:19:23.952Z" }, +] + +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, ] [[package]] @@ -920,11 +1317,11 @@ wheels = [ [[package]] name = "tomlkit" -version = "0.13.2" +version = "0.13.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885, upload-time = "2024-08-14T08:19:41.488Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size = 185207, upload-time = "2025-06-05T07:13:44.947Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955, upload-time = "2024-08-14T08:19:40.05Z" }, + { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" }, ] [[package]] @@ -938,14 +1335,35 @@ wheels = [ [[package]] name = "typeguard" -version = "4.4.2" +version = "4.4.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/60/8cd6a3d78d00ceeb2193c02b7ed08f063d5341ccdfb24df88e61f383048e/typeguard-4.4.2.tar.gz", hash = "sha256:a6f1065813e32ef365bc3b3f503af8a96f9dd4e0033a02c28c4a4983de8c6c49", size = 75746, upload-time = "2025-02-16T16:28:26.205Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/53/f701077a29ddf65ed4556119961ef517d767c07f15f6cdf0717ad985426b/typeguard-4.4.3.tar.gz", hash = "sha256:be72b9c85f322c20459b29060c5c099cd733d5886c4ee14297795e62b0c0d59b", size = 75072, upload-time = "2025-06-04T21:47:07.733Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/18/662e2a14fcdbbc9e7842ad801a7f9292fcd6cf7df43af94e59ac9c0da9af/typeguard-4.4.3-py3-none-any.whl", hash = "sha256:7d8b4a3d280257fd1aa29023f22de64e29334bda0b172ff1040f05682223795e", size = 34855, upload-time = "2025-06-04T21:47:03.683Z" }, +] + +[[package]] +name = "types-protobuf" +version = "6.30.2.20250516" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/6c/5cf088aaa3927d1cc39910f60f220f5ff573ab1a6485b2836e8b26beb58c/types_protobuf-6.30.2.20250516.tar.gz", hash = "sha256:aecd1881770a9bb225ede66872ef7f0da4505edd0b193108edd9892e48d49a41", size = 62254, upload-time = "2025-05-16T03:06:50.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/66/06a9c161f5dd5deb4f5c016ba29106a8f1903eb9a1ba77d407dd6588fecb/types_protobuf-6.30.2.20250516-py3-none-any.whl", hash = "sha256:8c226d05b5e8b2623111765fa32d6e648bbc24832b4c2fddf0fa340ba5d5b722", size = 76480, upload-time = "2025-05-16T03:06:49.444Z" }, +] + +[[package]] +name = "types-requests" +version = "2.32.4.20250611" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/7f/73b3a04a53b0fd2a911d4ec517940ecd6600630b559e4505cc7b68beb5a0/types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826", size = 23118, upload-time = "2025-06-11T03:11:41.272Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/4b/9a77dc721aa0b7f74440a42e4ef6f9a4fae7324e17f64f88b96f4c25cc05/typeguard-4.4.2-py3-none-any.whl", hash = "sha256:77a78f11f09777aeae7fa08585f33b5f4ef0e7335af40005b0c422ed398ff48c", size = 35801, upload-time = "2025-02-16T16:28:24.793Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ea/0be9258c5a4fa1ba2300111aa5a0767ee6d18eb3fd20e91616c12082284d/types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072", size = 20643, upload-time = "2025-06-11T03:11:40.186Z" }, ] [[package]] @@ -969,6 +1387,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, ] +[[package]] +name = "urllib3" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, +] + [[package]] name = "uv-dynamic-versioning" version = "0.8.2" @@ -986,74 +1413,24 @@ wheels = [ ] [[package]] -name = "wrapt" -version = "1.17.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531, upload-time = "2025-01-14T10:35:45.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/d1/1daec934997e8b160040c78d7b31789f19b122110a75eca3d4e8da0049e1/wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984", size = 53307, upload-time = "2025-01-14T10:33:13.616Z" }, - { url = "https://files.pythonhosted.org/packages/1b/7b/13369d42651b809389c1a7153baa01d9700430576c81a2f5c5e460df0ed9/wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22", size = 38486, upload-time = "2025-01-14T10:33:15.947Z" }, - { url = "https://files.pythonhosted.org/packages/62/bf/e0105016f907c30b4bd9e377867c48c34dc9c6c0c104556c9c9126bd89ed/wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7", size = 38777, upload-time = "2025-01-14T10:33:17.462Z" }, - { url = "https://files.pythonhosted.org/packages/27/70/0f6e0679845cbf8b165e027d43402a55494779295c4b08414097b258ac87/wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c", size = 83314, upload-time = "2025-01-14T10:33:21.282Z" }, - { url = "https://files.pythonhosted.org/packages/0f/77/0576d841bf84af8579124a93d216f55d6f74374e4445264cb378a6ed33eb/wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72", size = 74947, upload-time = "2025-01-14T10:33:24.414Z" }, - { url = "https://files.pythonhosted.org/packages/90/ec/00759565518f268ed707dcc40f7eeec38637d46b098a1f5143bff488fe97/wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061", size = 82778, upload-time = "2025-01-14T10:33:26.152Z" }, - { url = "https://files.pythonhosted.org/packages/f8/5a/7cffd26b1c607b0b0c8a9ca9d75757ad7620c9c0a9b4a25d3f8a1480fafc/wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2", size = 81716, upload-time = "2025-01-14T10:33:27.372Z" }, - { url = "https://files.pythonhosted.org/packages/7e/09/dccf68fa98e862df7e6a60a61d43d644b7d095a5fc36dbb591bbd4a1c7b2/wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c", size = 74548, upload-time = "2025-01-14T10:33:28.52Z" }, - { url = "https://files.pythonhosted.org/packages/b7/8e/067021fa3c8814952c5e228d916963c1115b983e21393289de15128e867e/wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62", size = 81334, upload-time = "2025-01-14T10:33:29.643Z" }, - { url = "https://files.pythonhosted.org/packages/4b/0d/9d4b5219ae4393f718699ca1c05f5ebc0c40d076f7e65fd48f5f693294fb/wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563", size = 36427, upload-time = "2025-01-14T10:33:30.832Z" }, - { url = "https://files.pythonhosted.org/packages/72/6a/c5a83e8f61aec1e1aeef939807602fb880e5872371e95df2137142f5c58e/wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f", size = 38774, upload-time = "2025-01-14T10:33:32.897Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f7/a2aab2cbc7a665efab072344a8949a71081eed1d2f451f7f7d2b966594a2/wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58", size = 53308, upload-time = "2025-01-14T10:33:33.992Z" }, - { url = "https://files.pythonhosted.org/packages/50/ff/149aba8365fdacef52b31a258c4dc1c57c79759c335eff0b3316a2664a64/wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", size = 38488, upload-time = "2025-01-14T10:33:35.264Z" }, - { url = "https://files.pythonhosted.org/packages/65/46/5a917ce85b5c3b490d35c02bf71aedaa9f2f63f2d15d9949cc4ba56e8ba9/wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", size = 38776, upload-time = "2025-01-14T10:33:38.28Z" }, - { url = "https://files.pythonhosted.org/packages/ca/74/336c918d2915a4943501c77566db41d1bd6e9f4dbc317f356b9a244dfe83/wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", size = 83776, upload-time = "2025-01-14T10:33:40.678Z" }, - { url = "https://files.pythonhosted.org/packages/09/99/c0c844a5ccde0fe5761d4305485297f91d67cf2a1a824c5f282e661ec7ff/wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", size = 75420, upload-time = "2025-01-14T10:33:41.868Z" }, - { url = "https://files.pythonhosted.org/packages/b4/b0/9fc566b0fe08b282c850063591a756057c3247b2362b9286429ec5bf1721/wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", size = 83199, upload-time = "2025-01-14T10:33:43.598Z" }, - { url = "https://files.pythonhosted.org/packages/9d/4b/71996e62d543b0a0bd95dda485219856def3347e3e9380cc0d6cf10cfb2f/wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", size = 82307, upload-time = "2025-01-14T10:33:48.499Z" }, - { url = "https://files.pythonhosted.org/packages/39/35/0282c0d8789c0dc9bcc738911776c762a701f95cfe113fb8f0b40e45c2b9/wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", size = 75025, upload-time = "2025-01-14T10:33:51.191Z" }, - { url = "https://files.pythonhosted.org/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", size = 81879, upload-time = "2025-01-14T10:33:52.328Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", size = 36419, upload-time = "2025-01-14T10:33:53.551Z" }, - { url = "https://files.pythonhosted.org/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", size = 38773, upload-time = "2025-01-14T10:33:56.323Z" }, - { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799, upload-time = "2025-01-14T10:33:57.4Z" }, - { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821, upload-time = "2025-01-14T10:33:59.334Z" }, - { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919, upload-time = "2025-01-14T10:34:04.093Z" }, - { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721, upload-time = "2025-01-14T10:34:07.163Z" }, - { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899, upload-time = "2025-01-14T10:34:09.82Z" }, - { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222, upload-time = "2025-01-14T10:34:11.258Z" }, - { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707, upload-time = "2025-01-14T10:34:12.49Z" }, - { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685, upload-time = "2025-01-14T10:34:15.043Z" }, - { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567, upload-time = "2025-01-14T10:34:16.563Z" }, - { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672, upload-time = "2025-01-14T10:34:17.727Z" }, - { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865, upload-time = "2025-01-14T10:34:19.577Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800, upload-time = "2025-01-14T10:34:21.571Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824, upload-time = "2025-01-14T10:34:22.999Z" }, - { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920, upload-time = "2025-01-14T10:34:25.386Z" }, - { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690, upload-time = "2025-01-14T10:34:28.058Z" }, - { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861, upload-time = "2025-01-14T10:34:29.167Z" }, - { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174, upload-time = "2025-01-14T10:34:31.702Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721, upload-time = "2025-01-14T10:34:32.91Z" }, - { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763, upload-time = "2025-01-14T10:34:34.903Z" }, - { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585, upload-time = "2025-01-14T10:34:36.13Z" }, - { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676, upload-time = "2025-01-14T10:34:37.962Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871, upload-time = "2025-01-14T10:34:39.13Z" }, - { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312, upload-time = "2025-01-14T10:34:40.604Z" }, - { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062, upload-time = "2025-01-14T10:34:45.011Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155, upload-time = "2025-01-14T10:34:47.25Z" }, - { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471, upload-time = "2025-01-14T10:34:50.934Z" }, - { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208, upload-time = "2025-01-14T10:34:52.297Z" }, - { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339, upload-time = "2025-01-14T10:34:53.489Z" }, - { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232, upload-time = "2025-01-14T10:34:55.327Z" }, - { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476, upload-time = "2025-01-14T10:34:58.055Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377, upload-time = "2025-01-14T10:34:59.3Z" }, - { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986, upload-time = "2025-01-14T10:35:00.498Z" }, - { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750, upload-time = "2025-01-14T10:35:03.378Z" }, - { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594, upload-time = "2025-01-14T10:35:44.018Z" }, +name = "virtualenv" +version = "20.31.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/56/2c/444f465fb2c65f40c3a104fd0c495184c4f2336d65baf398e3c75d72ea94/virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af", size = 6076316, upload-time = "2025-05-08T17:58:23.811Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/40/b1c265d4b2b62b58576588510fc4d1fe60a86319c8de99fd8e9fec617d2c/virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11", size = 6057982, upload-time = "2025-05-08T17:58:21.15Z" }, ] [[package]] name = "zipp" -version = "3.22.0" +version = "3.23.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/b6/7b3d16792fdf94f146bed92be90b4eb4563569eca91513c8609aebf0c167/zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5", size = 25257, upload-time = "2025-05-26T14:46:32.217Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/da/f64669af4cae46f17b90798a827519ce3737d31dbafad65d391e49643dc4/zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343", size = 9796, upload-time = "2025-05-26T14:46:30.775Z" }, + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, ]