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://pypi.org/project/a2a-sdk/)

+[](https://pypistats.org/packages/a2a-sdk)
-
+
- 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" },
]