diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 145eb42c4..7a5c2ab46 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -10,13 +10,17 @@ # other than the SDK team. For each one, we add the owning team, # as well as @temporalio/sdk, so the SDK team can continue to # manage repo-wide concerns. +/temporalio/contrib/deepagents/ @temporalio/ai-sdk @temporalio/sdk /temporalio/contrib/google_adk_agents/ @temporalio/ai-sdk @temporalio/sdk +/temporalio/contrib/google_genai/ @temporalio/ai-sdk @temporalio/sdk /temporalio/contrib/langgraph/ @temporalio/ai-sdk @temporalio/sdk /temporalio/contrib/langsmith/ @temporalio/ai-sdk @temporalio/sdk /temporalio/contrib/openai_agents/ @temporalio/ai-sdk @temporalio/sdk /temporalio/contrib/strands/ @temporalio/ai-sdk @temporalio/sdk /temporalio/contrib/workflow_streams/ @temporalio/ai-sdk @temporalio/sdk +/tests/contrib/deepagents/ @temporalio/ai-sdk @temporalio/sdk /tests/contrib/google_adk_agents/ @temporalio/ai-sdk @temporalio/sdk +/tests/contrib/google_genai/ @temporalio/ai-sdk @temporalio/sdk /tests/contrib/langgraph/ @temporalio/ai-sdk @temporalio/sdk /tests/contrib/langsmith/ @temporalio/ai-sdk @temporalio/sdk /tests/contrib/openai_agents/ @temporalio/ai-sdk @temporalio/sdk diff --git a/.github/actions/alpine-package-smoke/action.yml b/.github/actions/alpine-package-smoke/action.yml new file mode 100644 index 000000000..ad72f2eb9 --- /dev/null +++ b/.github/actions/alpine-package-smoke/action.yml @@ -0,0 +1,72 @@ +name: Alpine package smoke test +description: Install a local or published temporalio package in Alpine and run a minimal SDK workflow. +inputs: + wheel-dir: + description: "Directory containing local wheel files to install" + required: false + default: "" + version: + description: "Published package version to install and verify" + required: false + default: "" + index-url: + description: "Primary package index URL for published package installs" + required: false + default: "" + dependency-index-url: + description: "Optional dependency package index URL for published package installs" + required: false + default: "" + python-image: + description: "Alpine Python Docker image" + required: false + default: "python:3.10-alpine" +runs: + using: composite + steps: + - name: Install package and run SDK smoke test + shell: bash + env: + WHEEL_DIR: ${{ inputs.wheel-dir }} + VERSION: ${{ inputs.version }} + INDEX_URL: ${{ inputs.index-url }} + DEPENDENCY_INDEX_URL: ${{ inputs.dependency-index-url }} + PYTHON_IMAGE: ${{ inputs.python-image }} + run: | + set -euo pipefail + + if [[ -z "$WHEEL_DIR" && ( -z "$VERSION" || -z "$INDEX_URL" ) ]]; then + echo "Either wheel-dir or both version and index-url must be provided" >&2 + exit 1 + fi + + docker run --rm -v "$(pwd):/workspace" -w /tmp \ + -e WHEEL_DIR="$WHEEL_DIR" \ + -e VERSION="$VERSION" \ + -e INDEX_URL="$INDEX_URL" \ + -e DEPENDENCY_INDEX_URL="$DEPENDENCY_INDEX_URL" \ + "$PYTHON_IMAGE" sh -c ' + set -eu + python -m venv /tmp/alpine-smoke + /tmp/alpine-smoke/bin/python -m pip install --upgrade pip + + if [ -n "$WHEEL_DIR" ]; then + /tmp/alpine-smoke/bin/python -m pip install --prefer-binary /workspace/$WHEEL_DIR/*.whl + elif [ -n "$DEPENDENCY_INDEX_URL" ]; then + /tmp/alpine-smoke/bin/python /workspace/.github/scripts/install_release_package.py \ + --version "$VERSION" \ + --index-url "$INDEX_URL" \ + --dependency-index-url "$DEPENDENCY_INDEX_URL" + else + /tmp/alpine-smoke/bin/python /workspace/.github/scripts/install_release_package.py \ + --version "$VERSION" \ + --index-url "$INDEX_URL" + fi + + if [ -z "$VERSION" ]; then + VERSION=$(/tmp/alpine-smoke/bin/python -c "import importlib.metadata; print(importlib.metadata.version(\"temporalio\"))") + export VERSION + fi + + /tmp/alpine-smoke/bin/python /workspace/.github/scripts/release_smoke_package.py + ' diff --git a/.github/scripts/cloud_namespace.py b/.github/scripts/cloud_namespace.py new file mode 100644 index 000000000..c13d1e65e --- /dev/null +++ b/.github/scripts/cloud_namespace.py @@ -0,0 +1,110 @@ +"""Create and delete an isolated Temporal Cloud namespace for CI.""" + +import asyncio +import os +import sys +import time +from pathlib import Path + +from temporalio.api.cloud.cloudservice.v1 import ( + CreateNamespaceRequest, + DeleteNamespaceRequest, + GetAsyncOperationRequest, + GetNamespaceRequest, +) +from temporalio.api.cloud.namespace.v1 import MtlsAuthSpec, NamespaceSpec +from temporalio.api.cloud.operation.v1 import AsyncOperation +from temporalio.client import CloudOperationsClient + + +async def wait_for_operation( + client: CloudOperationsClient, operation: AsyncOperation +) -> None: + deadline = time.monotonic() + 10 * 60 + while True: + operation = ( + await client.cloud_service.get_async_operation( + GetAsyncOperationRequest(async_operation_id=operation.id) + ) + ).async_operation + if operation.state == AsyncOperation.STATE_FULFILLED: + return + if operation.state in { + AsyncOperation.STATE_FAILED, + AsyncOperation.STATE_CANCELLED, + AsyncOperation.STATE_REJECTED, + }: + raise RuntimeError( + "Cloud operation " + f"{operation.id} {AsyncOperation.State.Name(operation.state).lower()}: " + f"{operation.failure_reason}" + ) + if time.monotonic() >= deadline: + raise TimeoutError(f"Timed out waiting for Cloud operation {operation.id}") + delay = max( + operation.check_duration.seconds + + operation.check_duration.nanos / 1_000_000_000, + 1, + ) + await asyncio.sleep(min(delay, deadline - time.monotonic())) + + +async def create() -> None: + client = await cloud_client() + namespace_name = "sdk-python-ci-{}-{}".format( + os.environ["GITHUB_RUN_ID"], os.environ["GITHUB_RUN_ATTEMPT"] + ) + result = await client.cloud_service.create_namespace( + CreateNamespaceRequest( + spec=NamespaceSpec( + name=namespace_name, + regions=["aws-ca-central-1"], + retention_days=1, + mtls_auth=MtlsAuthSpec( + accepted_client_ca=Path( + os.environ["TEMPORAL_CLOUD_CLIENT_CA_PATH"] + ).read_bytes(), + enabled=True, + ), + ) + ) + ) + # Make cleanup possible even if provisioning fails after Cloud accepts the request. + with open(os.environ["GITHUB_OUTPUT"], "a") as output: + output.write(f"namespace={result.namespace}\n") + await wait_for_operation(client, result.async_operation) + + +async def delete(namespace: str) -> None: + client = await cloud_client() + existing = await client.cloud_service.get_namespace( + GetNamespaceRequest(namespace=namespace) + ) + result = await client.cloud_service.delete_namespace( + DeleteNamespaceRequest( + namespace=namespace, + resource_version=existing.namespace.resource_version, + ) + ) + await wait_for_operation(client, result.async_operation) + + +async def cloud_client() -> CloudOperationsClient: + return await CloudOperationsClient.connect( + api_key=os.environ["TEMPORAL_CLIENT_CLOUD_API_KEY"], + version=os.environ["TEMPORAL_CLIENT_CLOUD_API_VERSION"], + ) + + +async def main() -> None: + match sys.argv[1:]: + case ["create"]: + await create() + case ["delete", namespace]: + await delete(namespace) + case _: + raise ValueError("Usage: cloud_namespace.py create|delete ") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/.github/scripts/release_verify.py b/.github/scripts/release_verify.py index 390252854..23df9d5e3 100644 --- a/.github/scripts/release_verify.py +++ b/.github/scripts/release_verify.py @@ -4,6 +4,8 @@ import argparse import ast +import dataclasses +import difflib import pathlib import re import subprocess @@ -75,9 +77,9 @@ def verify_dist(args: argparse.Namespace) -> None: expected_sdist = f"temporalio-{args.version}.tar.gz" if sdists != [expected_sdist]: raise RuntimeError(f"Expected only sdist {expected_sdist!r}, found {sdists!r}") - if len(wheels) != 5: + if len(wheels) != 7: raise RuntimeError( - f"Expected 5 platform wheels, found {len(wheels)}: {wheels!r}" + f"Expected 7 platform wheels, found {len(wheels)}: {wheels!r}" ) for name in files: @@ -90,6 +92,8 @@ def verify_dist(args: argparse.Namespace) -> None: expected_platforms = { "linux-x86_64": lambda name: "manylinux" in name and "x86_64" in name, "linux-aarch64": lambda name: "manylinux" in name and "aarch64" in name, + "linux-musl-x86_64": lambda name: "musllinux" in name and "x86_64" in name, + "linux-musl-aarch64": lambda name: "musllinux" in name and "aarch64" in name, "macos-x86_64": lambda name: "macosx" in name and "x86_64" in name, "macos-arm64": lambda name: "macosx" in name and "arm64" in name, "windows-amd64": lambda name: "win_amd64" in name, @@ -163,6 +167,126 @@ def _link_sdk_core_prs(subject: str) -> str: ) +def _changelog_entries(text: str) -> dict[str, list[list[str]]]: + entries: dict[str, list[list[str]]] = {} + header: str | None = None + entry: list[str] | None = None + + for line in text.splitlines(): + if line.startswith("### "): + if entry is not None: + entries.setdefault(header or "Other", []).append(entry) + entry = None + header = line.removeprefix("### ").strip() + elif line.startswith(("* ", "- ")): + if entry is not None: + entries.setdefault(header or "Other", []).append(entry) + entry = [line] + elif entry is not None: + if line.strip(): + entry.append(line) + else: + entries.setdefault(header or "Other", []).append(entry) + entry = None + + if entry is not None: + entries.setdefault(header or "Other", []).append(entry) + return entries + + +@dataclasses.dataclass +class _ChangelogEntry: + lines: list[str] + introduced_header: str | None = None + + +def _updated_changelog_entries( + previous_entries: dict[str, list[_ChangelogEntry]], + current_entries: dict[str, list[list[str]]], +) -> dict[str, list[_ChangelogEntry]]: + current = [ + (header, entry) + for header, header_entries in current_entries.items() + for entry in header_entries + ] + previous = [ + entry + for category_entries in previous_entries.values() + for entry in category_entries + ] + exact_matches: dict[tuple[str, ...], list[_ChangelogEntry]] = {} + for entry in previous: + exact_matches.setdefault(tuple(entry.lines), []).append(entry) + + matches: dict[int, _ChangelogEntry] = {} + matched_previous: set[int] = set() + for current_index, (_, entry) in enumerate(current): + exact = exact_matches.get(tuple(entry)) + if exact: + previous_entry = exact.pop(0) + matches[current_index] = previous_entry + matched_previous.add(id(previous_entry)) + + candidates: list[tuple[float, int, _ChangelogEntry]] = [] + for current_index, (_, current_entry) in enumerate(current): + if current_index in matches: + continue + for previous_entry in previous: + if id(previous_entry) in matched_previous: + continue + similarity = difflib.SequenceMatcher( + a="\n".join(previous_entry.lines), + b="\n".join(current_entry), + autojunk=False, + ).ratio() + if similarity >= 0.6: + candidates.append((similarity, current_index, previous_entry)) + for _, current_index, previous_entry in sorted( + candidates, key=lambda candidate: candidate[0], reverse=True + ): + if current_index not in matches and id(previous_entry) not in matched_previous: + matches[current_index] = previous_entry + matched_previous.add(id(previous_entry)) + + updated: dict[str, list[_ChangelogEntry]] = {} + for current_index, (header, entry) in enumerate(current): + previous_entry = matches.get(current_index) + updated.setdefault(header, []).append( + _ChangelogEntry( + entry, + previous_entry.introduced_header if previous_entry else header, + ) + ) + return updated + + +def _sdk_core_changelog_entries( + previous_commit: str, + current_commit: str, + path: pathlib.Path, +) -> list[str]: + output = subprocess.check_output( + [ + "cargo", + "run", + "--quiet", + "-p", + "temporalio-sdk-core", + "--bin", + "changelog-release-notes", + "--", + "--from", + previous_commit, + "--to", + current_commit, + ], + cwd=path, + encoding="utf-8", + stderr=subprocess.STDOUT, + ).strip() + return output.splitlines() if output else [] + + def _sdk_core_release_notes(version: str, path: str) -> list[str]: previous_tag = _previous_release_tag(version) previous_commit = _gitlink(previous_tag, path) @@ -176,29 +300,23 @@ def _sdk_core_release_notes(version: str, path: str) -> list[str]: f"Submodule {path!r} is not initialized; checkout with submodules" ) - log_args = [ - "log", - "--format=%H%x00%h%x00%s", - "--reverse", - f"{previous_commit}..{current_commit}", - ] try: - log_output = _git(log_args, cwd=submodule_path) + notes = _sdk_core_changelog_entries( + previous_commit, + current_commit, + submodule_path, + ) except subprocess.CalledProcessError: _git(["fetch", "--quiet", "origin", "main"], cwd=submodule_path) - log_output = _git(log_args, cwd=submodule_path) - if not log_output: + notes = _sdk_core_changelog_entries( + previous_commit, + current_commit, + submodule_path, + ) + if not notes: return [] - lines = ["### SDK Core", ""] - for line in log_output.splitlines(): - full_hash, short_hash, subject = line.split("\0", 2) - subject = _link_sdk_core_prs(_clean_commit_subject(subject)) - lines.append( - f"- [`{short_hash}`](https://github.com/temporalio/sdk-rust/commit/" - f"{full_hash}) {subject}" - ) - return lines + return ["### SDK Core", "", *notes] def changelog_notes(args: argparse.Namespace) -> None: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55eb7eb56..a53035a7d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,6 +100,118 @@ jobs: npx doctoc README.md [[ -z $(git status --porcelain README.md) ]] || (git diff README.md; echo "README changed"; exit 1) + # Verify the optional FIPS build: the Rust core must link aws-lc-fips-sys + # (aws-lc-rs FIPS mode) and must NOT link `ring` (the cargo-tree guard, ported + # from sdk-ruby PR #466's `fips_tree` guard); then run the test suite against the + # FIPS binary and build the release wheel. + fips-build: + timeout-minutes: 45 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + submodules: recursive + - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: "3.10" + - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 + with: + workspaces: temporalio/bridge -> target + key: fips-${{ env.pythonLocation }} + # aws-lc-fips-sys builds the validated AWS-LC module from source, which + # needs Go, CMake, Perl and a C compiler (see the FIPS Compliance section + # in the README). + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 + with: + go-version: "1.24" + - name: Verify FIPS linkage (aws-lc-fips-sys present, ring absent) + working-directory: temporalio/bridge + run: | + set -uo pipefail + # Resolve the FIPS build's dependency graph once and query it for a + # single crate. `cargo tree -i ` ("invert") prints the chain of + # packages that pull in , or nothing if is not in the + # graph at all. We capture that output and decide PRESENT/ABSENT by + # whether the string is empty -- NOT by the exit code, because + # `cargo tree -i` exits 0 either way (an absent crate just prints + # "nothing to print" to stderr). Ported from sdk-ruby PR #466. + links_in_fips_build() { + cargo tree -p temporal-sdk-bridge --no-default-features --features fips -i "$1" 2>/dev/null + } + echo "== aws-lc-fips-sys must be PRESENT ==" + aws_lc_fips="$(links_in_fips_build aws-lc-fips-sys)" + if [ -z "$aws_lc_fips" ]; then + echo "ERROR: aws-lc-fips-sys is absent from the FIPS dependency tree" >&2 + exit 1 + fi + echo "$aws_lc_fips" + echo "== ring must be ABSENT ==" + ring="$(links_in_fips_build ring)" + if [ -n "$ring" ]; then + echo "ERROR: 'ring' is still linked in the FIPS build" >&2 + echo "$ring" >&2 + exit 1 + fi + echo "FIPS linkage verified: aws-lc-fips-sys linked, ring absent." + - uses: arduino/setup-protoc@c65c819552d16ad3c9b72d9dfd5ba5237b9c906b # v3 + with: + version: "23.x" + repo-token: ${{ secrets.GITHUB_TOKEN }} + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8 + - run: uv tool install poethepoet + - run: uv sync --all-extras + # Develop build so the FIPS extension is importable, then run the suite + # against it to confirm the aws-lc-rs stack works end to end (not just links). + - run: poe build-develop-fips + - name: Confirm the FIPS build is loaded + run: uv run python -c "from temporalio.bridge import temporal_sdk_bridge; assert temporal_sdk_bridge.FIPS, 'not a FIPS build'" + - run: mkdir junit-xml + - name: Run tests against the FIPS build + run: | + poe test --reruns 3 --only-rerun "RuntimeError: Failed validating workflow" -s --junit-xml=junit-xml/fips.xml + timeout-minutes: 15 + - name: "Upload junit-xml artifacts" + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + if: always() + with: + name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--fips + path: junit-xml + retention-days: 14 + - name: Build FIPS wheel (proves the release feature set compiles) + run: uv run maturin build --release --no-default-features --features fips + env: + TEMPORALIO_FIPS: "1" + + alpine-package-test: + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + - cibw-build: cp310-musllinux_x86_64 + runsOn: ubuntu-latest + - cibw-build: cp310-musllinux_aarch64 + runsOn: ubuntu-24.04-arm64-2-core + runs-on: ${{ matrix.runsOn }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + submodules: recursive + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: "3.14" + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8 + - run: uv sync --all-extras + - name: Build Alpine wheel + run: uv run cibuildwheel --output-dir dist + env: + CIBW_BUILD: ${{ matrix.cibw-build }} + - name: Test Alpine wheel + uses: ./.github/actions/alpine-package-smoke + with: + wheel-dir: dist + check-protos: timeout-minutes: 30 runs-on: ubuntu-latest @@ -130,7 +242,7 @@ jobs: - run: poe gen-protos - name: Check generation unchanged run: | - [[ -z $(git status --porcelain temporalio) ]] || (git diff temporalio; echo "Protos changed"; exit 1) + [[ -z $(git status --porcelain temporalio tests) ]] || (git diff temporalio tests; echo "Protos changed"; exit 1) - name: Test with protobuf 3.x run: poe test -s --ignore=tests/contrib/google_adk_agents/ env: @@ -175,10 +287,10 @@ jobs: path: junit-xml retention-days: 14 - # Run tests against Temporal Cloud (skipped on forks) + # Run the test suite against Temporal Cloud (skipped on forks) cloud-test: if: ${{ github.event.pull_request.head.repo.full_name == '' || github.event.pull_request.head.repo.full_name == 'temporalio/sdk-python' }} - timeout-minutes: 15 + timeout-minutes: 30 runs-on: ubuntu-latest steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -201,16 +313,46 @@ jobs: - run: uv tool install poethepoet - run: uv sync --all-extras - run: poe build-develop - - run: poe test -s tests/test_cloud.py --junit-xml=junit-xml/cloud.xml - timeout-minutes: 10 + - name: Generate Cloud test certificates + run: | + cert_dir="$RUNNER_TEMP/cloud-test-certs" + mkdir "$cert_dir" + openssl req -x509 -newkey rsa:2048 -nodes -days 1 \ + -keyout "$cert_dir/ca.key" -out "$cert_dir/ca.pem" \ + -subj '/CN=Temporal Python SDK Cloud CI CA' + openssl req -newkey rsa:2048 -nodes \ + -keyout "$cert_dir/client.key" -out "$cert_dir/client.csr" \ + -subj '/CN=Temporal Python SDK Cloud CI' + openssl x509 -req -days 1 -in "$cert_dir/client.csr" \ + -CA "$cert_dir/ca.pem" -CAkey "$cert_dir/ca.key" -CAcreateserial \ + -out "$cert_dir/client.pem" -extfile <(printf 'extendedKeyUsage=clientAuth') + { + echo "TEMPORAL_CLOUD_CLIENT_CA_PATH=$cert_dir/ca.pem" + echo "TEMPORAL_TLS_CLIENT_CERT_PATH=$cert_dir/client.pem" + echo "TEMPORAL_TLS_CLIENT_KEY_PATH=$cert_dir/client.key" + } >> "$GITHUB_ENV" + - name: Create Cloud namespace + id: create-cloud-namespace + run: uv run python .github/scripts/cloud_namespace.py create env: + TEMPORAL_CLIENT_CLOUD_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }} + TEMPORAL_CLIENT_CLOUD_API_VERSION: v0.19.1 + - run: mkdir junit-xml + - run: poe test -s --workflow-environment envconfig --junit-xml=junit-xml/cloud.xml + timeout-minutes: 15 + env: + TEMPORAL_ADDRESS: ${{ steps.create-cloud-namespace.outputs.namespace }}.tmprl.cloud:7233 + TEMPORAL_NAMESPACE: ${{ steps.create-cloud-namespace.outputs.namespace }} TEMPORAL_IS_CLOUD_TESTS: true TEMPORAL_CLIENT_CLOUD_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }} - TEMPORAL_CLIENT_CLOUD_API_VERSION: 2024-05-13-00 - TEMPORAL_CLIENT_CLOUD_NAMESPACE: sdk-ci.a2dd6 - TEMPORAL_CLIENT_CLOUD_TARGET: sdk-ci.a2dd6.tmprl.cloud:7233 - TEMPORAL_CLIENT_CERT: ${{ secrets.TEMPORAL_CLIENT_CERT }} - TEMPORAL_CLIENT_KEY: ${{ secrets.TEMPORAL_CLIENT_KEY }} + TEMPORAL_CLIENT_CLOUD_API_VERSION: v0.19.1 + TEMPORAL_CLIENT_CLOUD_NAMESPACE: ${{ steps.create-cloud-namespace.outputs.namespace }} + - name: Delete Cloud namespace + if: ${{ always() && steps.create-cloud-namespace.outputs.namespace != '' }} + run: uv run python .github/scripts/cloud_namespace.py delete "${{ steps.create-cloud-namespace.outputs.namespace }}" + env: + TEMPORAL_CLIENT_CLOUD_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }} + TEMPORAL_CLIENT_CLOUD_API_VERSION: v0.19.1 - name: "Upload junit-xml artifacts" uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 if: always() diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 3be7ad849..d745804a8 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -20,17 +20,29 @@ jobs: include: - os: ubuntu-latest package-suffix: linux-amd64 + cibw-build: cp310-manylinux_x86_64 - os: ubuntu-arm package-suffix: linux-aarch64 + cibw-build: cp310-manylinux_aarch64 + runsOn: ubuntu-24.04-arm64-2-core + - os: ubuntu-latest + package-suffix: linux-musl-amd64 + cibw-build: cp310-musllinux_x86_64 + - os: ubuntu-arm + package-suffix: linux-musl-aarch64 + cibw-build: cp310-musllinux_aarch64 runsOn: ubuntu-24.04-arm64-2-core - os: macos-intel package-suffix: macos-amd64 + cibw-build: cp310-macosx_x86_64 runsOn: macos-15-intel - os: macos-arm package-suffix: macos-aarch64 + cibw-build: cp310-macosx_arm64 runsOn: macos-14 - os: windows-latest package-suffix: windows-amd64 + cibw-build: cp310-win_amd64 runs-on: ${{ matrix.runsOn || matrix.os }} permissions: contents: read @@ -63,9 +75,12 @@ jobs: # Build the wheel - run: uv run cibuildwheel --output-dir dist + env: + CIBW_BUILD: ${{ matrix.cibw-build }} # Install the wheel in a new venv and run a test - name: Test wheel + if: ${{ !contains(matrix.package-suffix, 'musl') }} shell: bash run: | mkdir __test_wheel__ @@ -80,6 +95,12 @@ jobs: ./.venv/$bindir/pip install --prefer-binary ../dist/*.whl ./.venv/$bindir/python -m pytest -s tests/worker/test_workflow.py -k test_workflow_hello + - name: Test Alpine wheel + if: ${{ contains(matrix.package-suffix, 'musl') }} + uses: ./.github/actions/alpine-package-smoke + with: + wheel-dir: dist + # Upload dist - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: @@ -200,11 +221,36 @@ jobs: index-url: https://test.pypi.org/simple/ dependency-index-url: https://pypi.org/simple/ + smoke_testpypi_alpine: + name: Smoke test TestPyPI package on Alpine (${{ matrix.arch }}) + needs: + - verify_artifacts + - publish_testpypi + strategy: + fail-fast: false + matrix: + include: + - arch: x64 + runsOn: ubuntu-latest + - arch: arm64 + runsOn: ubuntu-24.04-arm64-2-core + runs-on: ${{ matrix.runsOn }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - name: Install and load package + uses: ./.github/actions/alpine-package-smoke + with: + version: ${{ needs.verify_artifacts.outputs.version }} + index-url: https://test.pypi.org/simple/ + dependency-index-url: https://pypi.org/simple/ + publish_pypi: name: Publish to PyPI needs: - verify_artifacts - smoke_testpypi + - smoke_testpypi_alpine runs-on: ubuntu-latest timeout-minutes: 10 environment: pypi @@ -247,11 +293,35 @@ jobs: version: ${{ needs.verify_artifacts.outputs.version }} index-url: https://pypi.org/simple/ + smoke_pypi_alpine: + name: Smoke test PyPI package on Alpine (${{ matrix.arch }}) + needs: + - verify_artifacts + - publish_pypi + strategy: + fail-fast: false + matrix: + include: + - arch: x64 + runsOn: ubuntu-latest + - arch: arm64 + runsOn: ubuntu-24.04-arm64-2-core + runs-on: ${{ matrix.runsOn }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - name: Install and load package + uses: ./.github/actions/alpine-package-smoke + with: + version: ${{ needs.verify_artifacts.outputs.version }} + index-url: https://pypi.org/simple/ + create_draft_release: name: Create draft GitHub Release needs: - verify_artifacts - smoke_pypi + - smoke_pypi_alpine runs-on: ubuntu-latest timeout-minutes: 5 permissions: diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..7ecbb40d5 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,117 @@ +# Contributor Guidance for `sdk-python` + +This repository provides the Temporal Python SDK, including Python packages, +tests, optional integrations, and the Rust bridge used by the SDK. Use this +document as a quick reference when submitting pull requests. + +## Requirements for coding agents + +* Prefer the repo's Poe tasks over invoking underlying tools directly. Use + `poe test`, `poe lint`, `poe format`, `poe build-develop`, and + `poe bridge-lint` unless there is a specific reason to run a lower-level + command. +* If you are about to run tests, you do not need to run a build separately first + unless Rust bridge changes need a fresh editable extension. For bridge changes, + run `poe build-develop` before Python tests that import `temporalio`. +* Use targeted tests while iterating. `poe test -s -k ` is preferred for + a small behavioral change; run broader tests only when the change affects + shared behavior. +* Do not use `--log-cli-level` by default. The pytest configuration shows logs + for failed tests at the end without streaming all logs for passing tests. +* Tests that use the workflow environment may start a local Temporal dev server + and may download a test server binary on first run. Unit tests that do not use + the workflow environment do not start a server. +* Time-skipping tests are run with `poe test -s --workflow-environment + time-skipping`. Time-skipping does not work on Linux ARM or Windows ARM. +* It is extremely important that comments explain why something is necessary, + not what the code already says. Avoid comments unless they clarify nonobvious + behavior. +* Avoid broad refactors, style churn, or unrelated cleanups in behavior changes. +* Avoid unqualified imports from `temporalio` packages except `temporalio.types`. + Relative imports are acceptable for private packages. +* Do not commit `uv.lock` or `pyproject.toml` changes created only for temporary + protobuf downgrade workflows. Prefer to use poe gen-protos-docker when possible. + +## Repo Specific Utilities + +* Poe tasks are defined in `pyproject.toml`: + * `poe build-develop` - build the Rust extension in editable debug mode. + * `poe test` - run pytest in parallel with the default workflow environment. + * `poe lint` - run import checks, formatting checks, type checks, and + docstyle. + * `poe lint-types` - run pyright, mypy, and basedpyright. + * `poe bridge-lint` - run clippy for the Rust bridge. + * `poe format` - run Ruff import sorting, Ruff formatting, and `cargo fmt` for + the bridge. + * `poe gen-protos-docker` - regenerate protobuf-related files using Docker. + * `poe gen-protos` - regenerate protobuf-related files without Docker, with + the Python/protobuf constraints documented in `README.md`. + +## Building and Testing + +The common local commands are: + +```bash +uv sync --all-extras +poe build-develop +poe lint +poe test +poe test -s --workflow-environment time-skipping +``` + +For focused iteration, prefer: + +```bash +poe test -s -k +uv run pytest tests/path/test_file.py::test_name +``` + +For release artifacts, use `uv build`. Documentation can be generated with +`poe gen-docs`. + +## Expectations for Pull Requests + +* Format and lint code before submitting. +* Include tests for behavior changes. +* Update public API documentation or doc comments for public behavior changes. +* Add a high-level changelog entry for user-facing changes according to the + existing `CHANGELOG.md` convention. +* Keep commit messages short and in the imperative mood. +* Provide a clear PR description outlining what changed, why it changed, and + what validation was run. + +## Review Checklist + +Reviewers will look for: + +* CI passing, including build, lint, type checks, unit tests, and workflow + environment tests. +* Tests covering behavior changes. +* Clear and concise code following existing style. +* Public API documentation updates when behavior changes. +* No unrelated generated files, lockfile churn, or broad rewrites. + +## Where Things Are + +* `temporalio/` - Python SDK source. + * `temporalio/worker/` - worker implementation. + * `temporalio/converter/` - payload and failure conversion. + * `temporalio/testing/` - testing utilities. + * `temporalio/nexus/` - Nexus support. + * `temporalio/contrib/` - optional integrations. + * `temporalio/bridge/` - Rust bridge and generated bridge bindings. +* `tests/` - pytest suites mirroring SDK areas. +* `scripts/` - generation, documentation, and helper scripts. +* `build/apidocs/` - generated API documentation. +* `dist/` - built wheels and source distributions. +* `temporalio/bridge/target/` - Rust build output. You should not need to inspect + this directory. + +## Notes + +* The SDK supports Python 3.10 and newer. +* Generated protobuf and bridge files have specific regeneration workflows; see + `README.md` before changing them. +* The Rust bridge uses SDK Core from `temporalio/bridge/sdk-core`. +* `__pycache__`, `build`, `dist`, and Rust `target` outputs are generated + artifacts and should not be reviewed as source changes. diff --git a/CHANGELOG.md b/CHANGELOG.md index 32527c303..f5e22ce98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ to include examples, links to docs, or any other relevant information. ### Added — new features ### Changed — changes in existing functionality ### Deprecated — soon-to-be-removed features -### Breaking Changes — removed or backwards-incompatible features +### :boom: Breaking Changes — removed or backwards-incompatible features ### Fixed — notable bug fixes ### Security — notable security fixes --> @@ -20,6 +20,108 @@ to include examples, links to docs, or any other relevant information. ### Added +- Added the `Runtime(disable_environment_info=...)` option to control whether + runtime, hosting, and platform information is included in worker heartbeats. + +- `temporalio.workflow.uuid7()` generates a determinism-safe, time-sortable + UUIDv7 (RFC 9562) from workflow time and the workflow's deterministic random + generator, complementing the existing `workflow.uuid4()` + ([#1450](https://github.com/temporalio/sdk-python/issues/1450)). The + workflow sandbox now also restricts the non-deterministic `uuid.uuid7()` + added to the standard library in Python 3.14, matching the existing + `uuid.uuid1()`/`uuid.uuid4()` restrictions. +- **Experimental**: `TemporalOperationHandler` can now use Standalone Activities as asynchronous + Nexus Operation backing executions through `TemporalNexusClient.start_activity`. + +### Changed + +- `temporalio.contrib.pydantic` converters now reuse Pydantic type adapters + for repeated type hints instead of rebuilding their schemas for every + payload, greatly speeding up decode of non-model hints such as discriminated + unions ([#1695](https://github.com/temporalio/sdk-python/issues/1695)). Up + to 1024 type adapters are cached per converter instance by default, with + least-recently-used eviction. To change the bound, pass + ``max_cached_type_adapters`` to ``PydanticPayloadConverter`` (or + ``PydanticJSONPlainPayloadConverter``) from a nullary subclass used as the + ``DataConverter.payload_converter_class``; ``None`` makes the cache + unbounded and zero disables caching. + +### Deprecated + +### :boom: Breaking Changes + +### Fixed + +- The `google-adk` extra now depends on `mcp`, so fresh installs of + `temporalio[google-adk]` can import `temporalio.contrib.google_adk_agents` + without separately installing `mcp`. Previously the import failed with an + `ImportError` because `google.adk.tools.mcp_tool` only exports `McpToolset` + when `mcp` is installed. +- `temporalio.contrib.openai_agents` no longer crashes when a plain `dict` + is passed for `run_config`. (openai-agents >= 0.19.0 accepts `dict` run + configs at its public runner API) + +### Security + +## [1.31.0] - 2026-07-29 + +### Added + +- Added the `Worker` `max_eager_activity_reservations_per_workflow_task` option for configuring + the number of activity slots reserved for eager execution per workflow task. Configured values + must be positive; use `disable_eager_activity_execution` to disable eager activity execution. +- Added experimental SDK payload converter support for values and type hints + decorated with `@transfer_type_convertible(...)` using a `TransferTypeConverter` class. + This lets types with transfer type converters delegate their wire representation to the + configured payload converter, preserving SDK behavior such as serialization + contexts. +- Added `TLSConfig.verification_server_name` to verify the server certificate against a fixed name + instead of the connection's server name. Unlike `domain`, it does not change the TLS SNI or + HTTP/2 authority values, which keep following the connected host, so it can be used when the + server's certificate does not carry the dialed name but on-path infrastructure (e.g. an + SNI-inspecting egress proxy) needs the SNI to remain resolvable. Requires + `server_root_ca_cert`. + +- Added the experimental `Worker` `patch_activation_callback` option, allowing workers + to decide whether a first non-replay `workflow.patched` call should activate a patch + during rolling deployments. +- Added external storage support to Nexus task handling. + +### Changed + +- Prepared replay-safe workflow activation scheduling that prevents cancellation + from being lost when another event becomes ready in the same workflow task. The + behavior is guarded by internal workflow logic flag 2 and remains disabled by + default during its compatibility rollout. + **Maintainer reminder:** keep flag 2 default-disabled for the first two published + SDK releases that recognize it; enable it in the third release, remove the explicit + overrides for this flag from `tests/worker/test_workflow.py`, and replace this rollout + note with a `Fixed` entry announcing the behavior change. + +### :boom: Breaking Changes + +- Custom workflow runners that construct `WorkflowInstanceDetails` must now pass + `payload_converter_factory` instead of `payload_converter_class`. The factory + returns the already wrapped payload converter that workflow instances should + use. +- System Nexus payload converter helpers added for generated bindings are now + private implementation details, and the remaining public `temporalio.nexus.system` + APIs are marked experimental and subject to change. +- Payload size limits have moved from `DataConverter` to `Client.connect`. Pass + `payload_limits=PayloadLimitsConfig(...)` (now exported from + `temporalio.client`) instead of setting `payload_limits` on `DataConverter`. + Config fields were renamed to `payloads_warn_size` and `memo_warn_size`, and + the deprecated `PayloadSizeWarning` was removed. + +### Fixed + +- Marked system Nexus envelope payloads so nested payloads can be detected and + visited after the envelope is already stored as a payload. + +## [1.30.0] - 2026-07-01 + +### Added + - Nexus operation link propagation for signals. When a Nexus operation handler signals a workflow (including signal-with-start), the inbound Nexus request links are now forwarded onto the signaled workflow so its history events link back to the caller, and the link the server returns for the @@ -36,19 +138,13 @@ to include examples, links to docs, or any other relevant information. with the selected optional dependencies. - Standalone Nexus operation links are now forwarded on start workflow and signal requests. -### Deprecated - -### Breaking Changes +### :boom: Breaking Changes - AWS Lambda worker `configure` parameter has been changed to be invoked per-invocation of the worker instead of only at startup. It is advised that any shared, heavy-weight operations are performed outside of the callback before `run_worker` is invoked. -### Fixed - -### Security - ## [1.29.0] - 2026-06-17 ### Added @@ -65,7 +161,7 @@ to include examples, links to docs, or any other relevant information. Pass `grpc_compression=GrpcCompression.NONE` to `Client.connect` or `CloudOperationsClient.connect` to disable it. -### Breaking Changes +### :boom: Breaking Changes - `StartWorkflowUpdateWithStartInput` now owns the authoritative `rpc_metadata` and `rpc_timeout` fields for diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6096c5b2b..057901ff5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,21 +1,128 @@ -# Contributing to the Temporal Python SDK +# Contributing to Temporal SDKs -Thanks for your interest in contributing! +Thanks for your interest in contributing to Temporal SDKs. -All contributors must complete the Temporal Contributor License Agreement (CLA) before changes -can be merged. A link to the CLA will be posted in the PR. +This guide describes expectations that apply across Temporal SDK repositories. Each +repository may have additional local conventions, but the guidance below should help +you open issues and pull requests that maintainers can evaluate efficiently. -See the [README](README.md) for build and development instructions. +## Before You Open an Issue -## Changelog +Search the existing issues first. If you find an issue that describes the same bug, +feature request, or design topic, add any relevant details there instead of opening a +duplicate. Use an upvote on the issue to show that it affects you too. -User-facing changes are recorded in [`CHANGELOG.md`](CHANGELOG.md), loosely following the -[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format. +Issues are assigned to people when they are actively working on them. Before taking +on an issue, check whether it is already assigned so you do not duplicate someone +else's work. -If your PR includes a user-facing change (new feature, behavior change, deprecation, breaking -change, notable bug fix, or security fix), add a short, high-level entry to the `## [Unreleased]` -section at the top of `CHANGELOG.md` under the appropriate heading: -Added, Changed, Deprecated, Breaking Changes, Fixed, or Security. +Use GitHub issues for actionable bugs and feature work. For usage questions, help +debugging an application, or general discussion, join the relevant +language-specific channel in the +[Temporal community Slack](https://temporal.io/slack) or use the support channel +available to you. -Keep entries high-level and written for users. The full commit log is appended at release time, -so internal-only changes (refactors, tests, CI, docs) don't need an entry. +## Bug Reports + +When reporting a bug, include enough detail for someone else to reproduce or +understand the problem: + +* A short summary of the problem. +* A minimal reproduction, preferably as code that can be copied into a small + project or test. +* What you expected to happen and what actually happened. +* The SDK version. +* The language runtime version. +* The operating system and architecture. +* Temporal Server or Temporal Cloud details, if the issue depends on service + behavior. +* Logs, stack traces, workflow histories, or other diagnostics that show the + failure. +* Whether the behavior is a regression, and the last version where it worked if + known. + +## Feature Requests and Design Changes + +Open or join a GitHub issue before starting substantial feature work, behavior +changes, or API design changes. This gives maintainers and other SDK users a chance +to discuss the approach before you invest in a larger implementation. + +The relevant language-specific channel in Temporal community Slack is also a good +place for early discussion, but important decisions should still be captured in a +GitHub issue so they are visible and searchable. + +Small bug fixes, documentation fixes, and narrowly scoped maintenance changes can go +straight to a pull request. + +## Pull Requests + +Good pull requests are focused and easy to review: + +* Keep each pull request scoped to one logical change. +* Include tests for behavior changes. +* Update public API documentation or doc comments when public behavior changes. +* Add a high-level changelog entry for user-facing changes according to the + repository's local changelog convention. +* Describe what changed, why it changed, and what validation you ran. + +Run the relevant local checks when practical. CI must pass before a pull request can +be merged. + +## Things to Avoid + +Avoid changes that make review harder without improving the contribution: + +* Unrelated refactors mixed into a behavior change. +* Style-only churn. +* Large feature pull requests that were not discussed first. +* License, copyright, or other legal changes without maintainer discussion. + +## AI-Generated Contributions + +Using AI tools while contributing is acceptable. You are responsible for the +correctness, quality, and maintainability of everything you submit. + +Contributors must fully understand the issue they are fixing and be able to explain +the proposed change. We expect that human understanding to be evident in pull +request responses and design discussions. If a contribution's interaction appears +entirely AI-driven, maintainers may close it: it does not provide a benefit over +maintainers using AI tooling themselves. + +Thoroughly self-review AI-generated code and documentation before opening a pull +request. Make sure it is correct, tested where appropriate, and consistent with the +style and patterns of the codebase. + +Keep AI-assisted changes concise and scoped. Avoid verbose generated prose, +unnecessary comments, or broad rewrites that make the change harder to review. + +## Contributor License Agreement + +All contributors must complete the Temporal Contributor License Agreement (CLA) +before changes can be merged. A link to the CLA will be posted in the pull request. + +## Security Issues + +Do not open public GitHub issues for suspected security vulnerabilities. Report them +to security@temporal.io instead. + +## Review and CI + +Maintainers review pull requests for correctness, compatibility, test coverage, +documentation, and long-term maintainability. Review may require changes before a +pull request can be merged, and it may take maintainers some time to review a +contribution. + +CI is the final validation gate. If CI fails, update the pull request or ask for help +if the failure appears unrelated to your change. Some CI gates may wait for a +maintainer to approve or run them. + +## Inactive Pull Requests + +Maintainers may close inactive pull requests after follow-up if they are no longer +moving forward. If that happens, you are welcome to reopen the pull request or open a +new one when you are ready to continue. + +## Community Conduct + +Keep discussions respectful, constructive, and focused on the work. Clear context, +specific examples, and patience with review feedback help everyone move faster. diff --git a/README.md b/README.md index 03ed87d51..7a1caedd9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![Temporal Python SDK](https://assets.temporal.io/w/py.png) -[![Python 3.9+](https://img.shields.io/pypi/pyversions/temporalio.svg?style=for-the-badge)](https://pypi.org/project/temporalio) +[![Python 3.10+](https://img.shields.io/pypi/pyversions/temporalio.svg?style=for-the-badge)](https://pypi.org/project/temporalio) [![PyPI](https://img.shields.io/pypi/v/temporalio.svg?style=for-the-badge)](https://pypi.org/project/temporalio) [![MIT](https://img.shields.io/pypi/l/temporalio.svg?style=for-the-badge)](LICENSE) @@ -121,6 +121,7 @@ informal introduction to the features and their implementation. - [Prepare](#prepare) - [Build](#build) - [Use](#use) + - [FIPS Compliance (Experimental)](#fips-compliance-experimental) - [Local SDK development environment](#local-sdk-development-environment) - [Testing](#testing-2) - [Proto Generation and Testing](#proto-generation-and-testing) @@ -895,9 +896,12 @@ protect tasks against cancellation. The following tasks, when cancelled, perform a Temporal cancellation: -* Activities - when the task executing an activity is cancelled, a cancellation request is sent to the activity -* Child workflows - when the task starting or executing a child workflow is cancelled, a cancellation request is sent to - cancel the child workflow +* Activities - when the task executing an activity is cancelled, a cancellation request may be sent to the activity + depending on cancellation type +* Child workflows - when the task starting or executing a child workflow is cancelled, a cancellation request may be + sent to cancel the child workflow depending on cancellation type +* Nexus operations - when the task starting or executing a Nexus operation is cancelled, a cancellation request may be + sent to cancel the Nexus operation depending on cancellation type * Timers - when the task executing a timer is cancelled (whether started via sleep or timeout), the timer is cancelled When the workflow itself is requested to cancel, `Task.cancel` is called on the main workflow task. Therefore, @@ -2011,7 +2015,7 @@ users are encouraged to not use gevent in asyncio applications (including Tempor # Development -The Python SDK is built to work with Python 3.9 and newer. It is built using +The Python SDK is built to work with Python 3.10 and newer. It is built using [SDK Core](https://github.com/temporalio/sdk-rust/) which is written in Rust. ### Building @@ -2109,6 +2113,50 @@ It should output: Result: Hello, Temporal! +#### FIPS Compliance (Experimental) + +> **NOTE**: FIPS support is **experimental**. It is opt-in, source-build only, and currently exercised +> on Linux only. This build wires the TLS/gRPC cryptography through a FIPS-validated module; it is +> **not** a claim that the SDK has passed a FIPS compliance audit or certification. + +FIPS 140-3 compliant cryptography is available as an **opt-in source build**. The published wheels are +**not** FIPS compliant — they use the [`ring`](https://github.com/briansmith/ring) backend, which is not +FIPS-validated. Because the crypto backend is chosen at compile time, FIPS cannot be enabled on a +precompiled wheel: you must build the native extension yourself with `TEMPORALIO_FIPS=1`. When set, the +build selects [`aws-lc-rs`](https://github.com/aws/aws-lc-rs) in FIPS mode (AWS-LC's FIPS 140-3 module) +for the gRPC client (and the OTLP metric exporter, when enabled), in place of `ring`. + +Building `aws-lc-rs` in FIPS mode compiles AWS-LC from source, so in addition to the +[Prepare](#prepare) prerequisites it requires **Go**, **CMake**, **Perl**, and a **C compiler**. + +To produce an installable FIPS wheel: + +```bash +TEMPORALIO_FIPS=1 uv run maturin build --release --no-default-features --features fips +``` + +or, equivalently, the provided task: + +```bash +poe build-wheel-fips +``` + +For a local develop build, use `poe build-develop-fips`. You can confirm at runtime that a FIPS build is +loaded: + +```python +from temporalio.bridge import temporal_sdk_bridge +assert temporal_sdk_bridge.FIPS +``` + +> **NOTE**: When a `Worker` or `Replayer` is created without a `build_id` (or `deployment_config`), the +> SDK derives a default build ID by hashing loaded module bytecode with MD5 (via +> `hashlib.md5(usedforsecurity=False)`). Although md5 is among Python's +> [guaranteed hash algorithms](https://docs.python.org/3/library/hashlib.html#hashlib.algorithms_guaranteed), +> some vendors ship "FIPS" Python builds that remove it entirely — on such an interpreter this call +> raises. If you run on one, pass an explicit `build_id` (directly or inside `deployment_config`) so the +> default MD5-based path is not used. + ### Local SDK development environment For local development, it is quicker to use a debug build. diff --git a/pyproject.toml b/pyproject.toml index 6b1e9b736..d5de4c077 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "temporalio" -version = "1.29.0" +version = "1.31.0" description = "Temporal.io Python SDK" authors = [{ name = "Temporal Technologies Inc", email = "sdk@temporal.io" }] requires-python = ">=3.10" @@ -29,9 +29,14 @@ grpc = ["grpcio>=1.48.2,<2"] opentelemetry = ["opentelemetry-api>=1.11.1,<2", "opentelemetry-sdk>=1.11.1,<2"] pydantic = ["pydantic>=2.0.0,<3"] openai-agents = ["openai-agents>=0.17.5", "mcp>=1.9.4, <2"] -google-adk = ["google-adk>=1.27.0,<2"] +google-adk = ["google-adk>=2.2.0,<3", "mcp>=1.24,<2"] langgraph = ["langgraph>=1.1.0"] langsmith = ["langsmith>=0.7.34,<0.9"] +deepagents = [ + "deepagents>=0.6.12,<0.7; python_version >= '3.11'", + "langchain>=1.3.11,<2; python_version >= '3.11'", + "langchain-core>=1.4.8,<2; python_version >= '3.11'", +] lambda-worker-otel = [ "opentelemetry-api>=1.11.1,<2", "opentelemetry-sdk>=1.11.1,<2", @@ -40,6 +45,7 @@ lambda-worker-otel = [ "opentelemetry-sdk-extension-aws>=2.0.0,<3", ] aioboto3 = ["aioboto3>=10.4.0", "types-aioboto3[s3]>=10.4.0"] +google-genai = ["google-genai>=2.10.0,<3.0.0"] strands-agents = ["strands-agents>=1.39.0"] [project.urls] @@ -52,6 +58,7 @@ Documentation = "https://docs.temporal.io/docs/python" dev = [ "basedpyright==1.34.0", "cibuildwheel>=2.22.0,<3", + "cryptography>=46", "grpcio-tools>=1.48.2,<2", "mypy==1.18.2", "mypy-protobuf>=3.3.0,<4", @@ -80,6 +87,10 @@ dev = [ "moto[s3,server]>=5", "langgraph>=1.1.0", "langsmith>=0.7.34,<0.9", + "deepagents>=0.6.12,<0.7; python_version >= '3.11'", + "langchain>=1.3.11,<2; python_version >= '3.11'", + "langchain-core>=1.4.8,<2; python_version >= '3.11'", + "langchain-anthropic>=1.4.7; python_version >= '3.11'", "setuptools<82", "opentelemetry-exporter-otlp-proto-grpc>=1.11.1,<2", "opentelemetry-semantic-conventions>=0.40b0,<1", @@ -88,11 +99,15 @@ dev = [ "async-timeout>=4.0,<6; python_version < '3.11'", "strands-agents>=1.39.0", "strands-agents-tools>=0.5.2", + "mcp>=1.9.4,<2", ] [tool.poe.tasks] build-develop = "uv run maturin develop --uv" build-develop-with-release = { cmd = "uv run maturin develop --release --uv" } +# FIPS builds: swap the rustls stack onto aws-lc-rs FIPS mode (aws-lc-fips-sys), eliminating `ring`. +build-develop-fips = { cmd = "uv run maturin develop --uv --no-default-features --features fips", env = { TEMPORALIO_FIPS = "1" } } +build-wheel-fips = { cmd = "uv run maturin build --release --no-default-features --features fips", env = { TEMPORALIO_FIPS = "1" } } format = [ { cmd = "uv run ruff check --select I --fix" }, { cmd = "uv run ruff format" }, @@ -151,17 +166,21 @@ filterwarnings = [ [tool.cibuildwheel] before-all = "pip install protoc-wheel-0" -build = "cp310-win_amd64 cp310-manylinux_x86_64 cp310-manylinux_aarch64 cp310-macosx_x86_64 cp310-macosx_arm64" +build = "cp310-win_amd64 cp310-manylinux_x86_64 cp310-manylinux_aarch64 cp310-musllinux_x86_64 cp310-musllinux_aarch64 cp310-macosx_x86_64 cp310-macosx_arm64" build-verbosity = 1 [tool.cibuildwheel.macos] environment = { MACOSX_DEPLOYMENT_TARGET = "10.12" } [tool.cibuildwheel.linux] -before-all = "curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain stable -y && yum install -y openssl-devel" -before-build = "pip install protoc-wheel-0" +before-all = "sh scripts/cibuildwheel_before_all_linux.sh" +before-build = "sh scripts/cibuildwheel_before_build_linux.sh" environment = { PATH = "$PATH:$HOME/.cargo/bin", CARGO_NET_GIT_FETCH_WITH_CLI = "true" } +[[tool.cibuildwheel.overrides]] +select = "*musllinux*" +environment = { PATH = "$PATH:$HOME/.cargo/bin", CARGO_NET_GIT_FETCH_WITH_CLI = "true", RUSTFLAGS = "-C target-feature=-crt-static" } + [tool.mypy] ignore_missing_imports = true exclude = [ @@ -254,10 +273,9 @@ manifest-path = "temporalio/bridge/Cargo.toml" module-name = "temporalio.bridge.temporal_sdk_bridge" python-packages = ["temporalio"] include = ["LICENSE"] -exclude = ["temporalio/bridge/target/**/*"] +exclude = ["temporalio/bridge/target/**/*", "temporalio/bridge/sdk-core/.git"] [tool.uv] # Prevent uv commands from building the package by default package = false exclude-newer = "2 weeks" -exclude-newer-package = { openai-agents = false } diff --git a/scripts/cibuildwheel_before_all_linux.sh b/scripts/cibuildwheel_before_all_linux.sh new file mode 100644 index 000000000..be9b08986 --- /dev/null +++ b/scripts/cibuildwheel_before_all_linux.sh @@ -0,0 +1,13 @@ +#!/bin/sh +set -eu + +if command -v apk >/dev/null 2>&1; then + apk add --no-cache build-base curl openssl-dev protobuf-dev +elif command -v yum >/dev/null 2>&1; then + yum install -y openssl-devel +else + echo "Unsupported Linux image: expected apk or yum" >&2 + exit 1 +fi + +curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain stable -y diff --git a/scripts/cibuildwheel_before_build_linux.sh b/scripts/cibuildwheel_before_build_linux.sh new file mode 100644 index 000000000..838c7dd72 --- /dev/null +++ b/scripts/cibuildwheel_before_build_linux.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -eu + +if command -v protoc >/dev/null 2>&1; then + protoc --version +else + pip install protoc-wheel-0 +fi diff --git a/scripts/gen_payload_visitor.py b/scripts/gen_payload_visitor.py index efe9c0df2..0001659f7 100644 --- a/scripts/gen_payload_visitor.py +++ b/scripts/gen_payload_visitor.py @@ -12,6 +12,7 @@ sys.path.insert(0, str(base_dir)) from temporalio.api.common.v1.message_pb2 import Payload, Payloads, SearchAttributes +from temporalio.bridge.proto.nexus import NexusTaskCompletion from temporalio.bridge.proto.workflow_activation.workflow_activation_pb2 import ( WorkflowActivation, ) @@ -188,22 +189,9 @@ async def visit( async def _visit_nexus_operation_input_payload( self, fs: VisitorFunctions, - endpoint: str, payload: Payload, ) -> None: - new_payload = await temporalio.nexus.system.maybe_visit_payload( - endpoint, - payload, - fs, - self.skip_search_attributes, - ) - if new_payload is None: - await self._visit_temporal_api_common_v1_Payload(fs, payload) - return - - if new_payload is not payload: - payload.CopyFrom(new_payload) - await fs.visit_system_nexus_envelope(payload) + await self._visit_temporal_api_common_v1_Payload(fs, payload) """ @@ -218,8 +206,21 @@ def __init__(self): self.in_progress: set[str] = set() self.methods: list[str] = [ """\ - async def _visit_temporal_api_common_v1_Payload(self, fs: VisitorFunctions, o: Payload): - await fs.visit_payload(o) + async def _visit_temporal_api_common_v1_Payload( + self, fs: VisitorFunctions, payload: Payload + ) -> None: + new_payload = await temporalio.nexus.system.maybe_visit_payload( + payload, + fs, + self.skip_search_attributes, + ) + if new_payload is None: + await fs.visit_payload(payload) + return + + if new_payload is not payload: + payload.CopyFrom(new_payload) + await fs.visit_system_nexus_envelope(payload) """, """\ async def _visit_temporal_api_common_v1_Payloads(self, fs: VisitorFunctions, o: Any): @@ -403,11 +404,11 @@ def walk(self, desc: Descriptor) -> bool: ) ) elif item[0] == "system_nexus": - _, field_name, endpoint_expr, payload_expr = item + _, field_name, _endpoint_expr, payload_expr = item lines.append( f' if o.HasField("{field_name}"):\n' " await self._visit_nexus_operation_input_payload(\n" - f" fs, {endpoint_expr}, {payload_expr}\n" + f" fs, {payload_expr}\n" " )" ) else: # oneof_group @@ -426,27 +427,20 @@ def write_bridge_visitors() -> None: out_path = base_dir / "temporalio" / "bridge" / "_visitor.py" # Build root descriptors: WorkflowActivation, WorkflowActivationCompletion, - # and all messages from selected API modules + # NexusTaskCompletion, and the system Nexus operation roots. roots: list[Descriptor] = [ WorkflowActivation.DESCRIPTOR, WorkflowActivationCompletion.DESCRIPTOR, - ] + NexusTaskCompletion.DESCRIPTOR, + ] + discover_system_nexus_roots() code = VisitorGenerator().generate(roots) out_path.write_text(code) -def write_system_nexus_payload_visitors() -> None: - out_path = base_dir / "temporalio" / "nexus" / "system" / "_payload_visitor.py" - code = VisitorGenerator().generate(discover_system_nexus_roots()) - out_path.write_text(code) - - if __name__ == "__main__": print("Generating temporalio/bridge/_visitor.py...", file=sys.stderr) write_bridge_visitors() - print("Generating temporalio/nexus/system/_payload_visitor.py...", file=sys.stderr) - write_system_nexus_payload_visitors() subprocess.run( [ "uv", @@ -457,7 +451,6 @@ def write_system_nexus_payload_visitors() -> None: "I", "--fix", "temporalio/bridge/_visitor.py", - "temporalio/nexus/system/_payload_visitor.py", ], cwd=base_dir, check=True, @@ -469,7 +462,6 @@ def write_system_nexus_payload_visitors() -> None: "ruff", "format", "temporalio/bridge/_visitor.py", - "temporalio/nexus/system/_payload_visitor.py", ], cwd=base_dir, check=True, diff --git a/scripts/gen_protos_docker.py b/scripts/gen_protos_docker.py index 500fb0cbd..6735bdf90 100644 --- a/scripts/gen_protos_docker.py +++ b/scripts/gen_protos_docker.py @@ -11,7 +11,7 @@ os.path.join("scripts", "_proto", "Dockerfile"), ".", ], - capture_output=True, + stdout=subprocess.PIPE, text=True, check=True, ) diff --git a/scripts/prepare_release.py b/scripts/prepare_release.py index 68f2ba39f..51dc34496 100644 --- a/scripts/prepare_release.py +++ b/scripts/prepare_release.py @@ -17,13 +17,20 @@ "Added", "Changed", "Deprecated", - "Breaking Changes", + ":boom: Breaking Changes", "Fixed", "Security", ) VERSION_RE = re.compile(r"[0-9]+(?:\.[0-9]+)+(?:[a-zA-Z0-9_.+-]+)?") _CHANGELOG_HEADING_RE = re.compile(r"^## \[(?P[^\]]+)\](?:\s+-\s+.*)?\s*$") _CHANGELOG_SUBHEADING_RE = re.compile(r"^### (?P
.+?)\s*$") +_RELEASE_FILES = ( + "CHANGELOG.md", + "pyproject.toml", + "temporalio/service.py", + "uv.lock", +) +_RELEASE_FILE_SET = frozenset(_RELEASE_FILES) def validate_version(version: str) -> str: @@ -78,7 +85,7 @@ def finalize_changelog_release( def replace_project_version(text: str, version: str) -> str: return _replace_once( - r'(?m)^version = "[^"]+"\s*$', + r'(?m)^version = "[^"]+"[^\S\r\n]*$', f'version = "{validate_version(version)}"', text, description="project version", @@ -87,13 +94,89 @@ def replace_project_version(text: str, version: str) -> str: def replace_service_version(text: str, version: str) -> str: return _replace_once( - r'(?m)^__version__ = "[^"]+"\s*$', + r'(?m)^__version__ = "[^"]+"[^\S\r\n]*$', f'__version__ = "{validate_version(version)}"', text, description="service version", ) +def create_release_branch(repo_root: pathlib.Path, version: str) -> None: + subprocess.run(["git", "fetch", "origin", "main"], cwd=repo_root, check=True) + subprocess.run( + ["git", "switch", "--create", f"chore/release-{version}", "origin/main"], + cwd=repo_root, + check=True, + ) + + +def changed_files(repo_root: pathlib.Path) -> set[str]: + result = subprocess.run( + ["git", "status", "--porcelain"], + cwd=repo_root, + check=True, + capture_output=True, + text=True, + ) + return {line[3:] for line in result.stdout.splitlines()} + + +def ensure_clean_worktree(repo_root: pathlib.Path) -> None: + changes = changed_files(repo_root) + if changes: + raise RuntimeError( + "Release preparation requires a clean worktree; found changes in " + + ", ".join(sorted(changes)) + ) + + +def ensure_only_release_changes(repo_root: pathlib.Path) -> None: + unexpected_files = changed_files(repo_root) - _RELEASE_FILE_SET + if unexpected_files: + raise RuntimeError( + "Release preparation changed unexpected files: " + + ", ".join(sorted(unexpected_files)) + ) + + +def commit_release_changes(repo_root: pathlib.Path, version: str) -> None: + subprocess.run( + ["git", "commit", "-m", f"Prepare release {version}", "--", *_RELEASE_FILES], + cwd=repo_root, + check=True, + ) + + +def push_release_branch(repo_root: pathlib.Path, version: str) -> None: + branch = f"chore/release-{version}" + subprocess.run( + ["git", "push", "--set-upstream", "origin", branch], + cwd=repo_root, + check=True, + ) + + +def create_release_pr(repo_root: pathlib.Path, version: str) -> None: + branch = f"chore/release-{version}" + subprocess.run( + [ + "gh", + "pr", + "create", + "--base", + "main", + "--head", + branch, + "--title", + f"Prepare release {version}", + "--body", + f"Prepare release {version}.", + ], + cwd=repo_root, + check=True, + ) + + def _seeded_unreleased_lines() -> list[str]: lines = ["## [Unreleased]", ""] for header in CHANGELOG_HEADERS: @@ -197,6 +280,8 @@ def main(argv: Sequence[str] | None = None) -> None: repo_root = pathlib.Path(__file__).resolve().parents[1] version = validate_version(args.version) release_date = parse_date(args.date) + ensure_clean_worktree(repo_root) + create_release_branch(repo_root, version) changelog_path = repo_root / "CHANGELOG.md" pyproject_path = repo_root / "pyproject.toml" service_path = repo_root / "temporalio" / "service.py" @@ -228,7 +313,14 @@ def main(argv: Sequence[str] | None = None) -> None: if not args.skip_lock: subprocess.run(["uv", "lock"], cwd=repo_root, check=True) - print(f"Prepared release {version} dated {release_date.isoformat()}") + ensure_only_release_changes(repo_root) + commit_release_changes(repo_root, version) + push_release_branch(repo_root, version) + create_release_pr(repo_root, version) + + print( + f"Prepared release {version} dated {release_date.isoformat()} and opened a PR" + ) if __name__ == "__main__": diff --git a/temporalio/activity.py b/temporalio/activity.py index 417195d59..3f69bc17f 100644 --- a/temporalio/activity.py +++ b/temporalio/activity.py @@ -29,6 +29,9 @@ import temporalio.bridge.proto.activity_task import temporalio.common import temporalio.converter +from temporalio.converter._payload_converter import ( + _TemporalTransferTypePayloadConverter, +) from .types import CallableType @@ -238,9 +241,13 @@ def payload_converter(self) -> temporalio.converter.PayloadConverter: self.payload_converter_class_or_instance, temporalio.converter.PayloadConverter, ): - self._payload_converter = self.payload_converter_class_or_instance + self._payload_converter = _TemporalTransferTypePayloadConverter.wrap( + self.payload_converter_class_or_instance + ) else: - self._payload_converter = self.payload_converter_class_or_instance() + self._payload_converter = _TemporalTransferTypePayloadConverter.wrap( + self.payload_converter_class_or_instance() + ) return self._payload_converter @property @@ -513,15 +520,14 @@ def process( if context: if self.activity_info_on_message: msg = f"{msg} ({context.logger_details})" - if self.activity_info_on_extra: - # Extra can be absent or None, this handles both - extra = kwargs.get("extra", None) or {} - extra["temporal_activity"] = context.logger_details - kwargs["extra"] = extra - if self.full_activity_info_on_extra: - # Extra can be absent or None, this handles both - extra = kwargs.get("extra", None) or {} - extra["activity_info"] = context.info() + if self.activity_info_on_extra or self.full_activity_info_on_extra: + # Copy any caller-provided extra rather than mutating it in + # place (see https://github.com/temporalio/sdk-python/issues/503). + extra = dict(kwargs.get("extra") or {}) + if self.activity_info_on_extra: + extra["temporal_activity"] = context.logger_details + if self.full_activity_info_on_extra: + extra["activity_info"] = context.info() kwargs["extra"] = extra return (msg, kwargs) diff --git a/temporalio/api/activity/v1/message_pb2.py b/temporalio/api/activity/v1/message_pb2.py index 4f0a4b164..59f79a3c5 100644 --- a/temporalio/api/activity/v1/message_pb2.py +++ b/temporalio/api/activity/v1/message_pb2.py @@ -43,7 +43,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n&temporal/api/activity/v1/message.proto\x12\x18temporal.api.activity.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a$temporal/api/common/v1/message.proto\x1a(temporal/api/deployment/v1/message.proto\x1a$temporal/api/enums/v1/activity.proto\x1a&temporal/api/callback/v1/message.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a%temporal/api/failure/v1/message.proto\x1a\'temporal/api/taskqueue/v1/message.proto\x1a\'temporal/api/sdk/v1/user_metadata.proto"\x8c\x01\n\x18\x41\x63tivityExecutionOutcome\x12\x32\n\x06result\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.PayloadsH\x00\x12\x33\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.FailureH\x00\x42\x07\n\x05value"\xa7\x03\n\x0f\x41\x63tivityOptions\x12\x38\n\ntask_queue\x18\x01 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12<\n\x19schedule_to_close_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x06 \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x32\n\x08priority\x18\x07 \x01(\x0b\x32 .temporal.api.common.v1.Priority"\x82\x0e\n\x15\x41\x63tivityExecutionInfo\x12\x13\n\x0b\x61\x63tivity_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12;\n\ractivity_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12>\n\x06status\x18\x04 \x01(\x0e\x32..temporal.api.enums.v1.ActivityExecutionStatus\x12>\n\trun_state\x18\x05 \x01(\x0e\x32+.temporal.api.enums.v1.PendingActivityState\x12\x12\n\ntask_queue\x18\x06 \x01(\t\x12<\n\x19schedule_to_close_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x0b \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12;\n\x11heartbeat_details\x18\x0c \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x37\n\x13last_heartbeat_time\x18\r \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x35\n\x11last_started_time\x18\x0e \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07\x61ttempt\x18\x0f \x01(\x05\x12\x35\n\x12\x65xecution_duration\x18\x10 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x31\n\rschedule_time\x18\x11 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x33\n\x0f\x65xpiration_time\x18\x12 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nclose_time\x18\x13 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x36\n\x0clast_failure\x18\x14 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x1c\n\x14last_worker_identity\x18\x15 \x01(\t\x12\x39\n\x16\x63urrent_retry_interval\x18\x16 \x01(\x0b\x32\x19.google.protobuf.Duration\x12>\n\x1alast_attempt_complete_time\x18\x17 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x1anext_attempt_schedule_time\x18\x18 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12T\n\x17last_deployment_version\x18\x19 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x32\n\x08priority\x18\x1a \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12\x1e\n\x16state_transition_count\x18\x1b \x01(\x03\x12\x18\n\x10state_size_bytes\x18\x1c \x01(\x03\x12\x43\n\x11search_attributes\x18\x1d \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12.\n\x06header\x18\x1e \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x38\n\ruser_metadata\x18\x1f \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12\x17\n\x0f\x63\x61nceled_reason\x18 \x01(\t\x12+\n\x05links\x18! \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x12\x1d\n\x15total_heartbeat_count\x18" \x01(\x03\x12\x10\n\x08sdk_name\x18# \x01(\t\x12\x13\n\x0bsdk_version\x18$ \x01(\t\x12.\n\x0bstart_delay\x18% \x01(\x0b\x32\x19.google.protobuf.Duration"\xea\x03\n\x19\x41\x63tivityExecutionListInfo\x12\x13\n\x0b\x61\x63tivity_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12;\n\ractivity_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12\x31\n\rschedule_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nclose_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x06status\x18\x06 \x01(\x0e\x32..temporal.api.enums.v1.ActivityExecutionStatus\x12\x43\n\x11search_attributes\x18\x07 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x12\n\ntask_queue\x18\x08 \x01(\t\x12\x1e\n\x16state_transition_count\x18\t \x01(\x03\x12\x18\n\x10state_size_bytes\x18\n \x01(\x03\x12\x35\n\x12\x65xecution_duration\x18\x0b \x01(\x0b\x32\x19.google.protobuf.Duration"\xff\x01\n\x0c\x43\x61llbackInfo\x12?\n\x07trigger\x18\x01 \x01(\x0b\x32..temporal.api.activity.v1.CallbackInfo.Trigger\x12\x34\n\x04info\x18\x02 \x01(\x0b\x32&.temporal.api.callback.v1.CallbackInfo\x1a\x10\n\x0e\x41\x63tivityClosed\x1a\x66\n\x07Trigger\x12P\n\x0f\x61\x63tivity_closed\x18\x01 \x01(\x0b\x32\x35.temporal.api.activity.v1.CallbackInfo.ActivityClosedH\x00\x42\t\n\x07variantB\x93\x01\n\x1bio.temporal.api.activity.v1B\x0cMessageProtoP\x01Z\'go.temporal.io/api/activity/v1;activity\xaa\x02\x1aTemporalio.Api.Activity.V1\xea\x02\x1dTemporalio::Api::Activity::V1b\x06proto3' + b'\n&temporal/api/activity/v1/message.proto\x12\x18temporal.api.activity.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a$temporal/api/common/v1/message.proto\x1a(temporal/api/deployment/v1/message.proto\x1a$temporal/api/enums/v1/activity.proto\x1a&temporal/api/callback/v1/message.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a%temporal/api/failure/v1/message.proto\x1a\'temporal/api/taskqueue/v1/message.proto\x1a\'temporal/api/sdk/v1/user_metadata.proto"\xc4\x01\n\x18\x41\x63tivityExecutionOutcome\x12\x32\n\x06result\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.PayloadsH\x00\x12\x33\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.FailureH\x00\x12\x36\n\x0bretry_state\x18\x03 \x01(\x0e\x32!.temporal.api.enums.v1.RetryStateB\x07\n\x05value"\xd7\x03\n\x0f\x41\x63tivityOptions\x12\x38\n\ntask_queue\x18\x01 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12<\n\x19schedule_to_close_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x06 \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x32\n\x08priority\x18\x07 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12.\n\x0bstart_delay\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration"\xb6\x0e\n\x15\x41\x63tivityExecutionInfo\x12\x13\n\x0b\x61\x63tivity_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12;\n\ractivity_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12>\n\x06status\x18\x04 \x01(\x0e\x32..temporal.api.enums.v1.ActivityExecutionStatus\x12>\n\trun_state\x18\x05 \x01(\x0e\x32+.temporal.api.enums.v1.PendingActivityState\x12\x12\n\ntask_queue\x18\x06 \x01(\t\x12<\n\x19schedule_to_close_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x0b \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12;\n\x11heartbeat_details\x18\x0c \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x37\n\x13last_heartbeat_time\x18\r \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x35\n\x11last_started_time\x18\x0e \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07\x61ttempt\x18\x0f \x01(\x05\x12\x35\n\x12\x65xecution_duration\x18\x10 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x31\n\rschedule_time\x18\x11 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x33\n\x0f\x65xpiration_time\x18\x12 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nclose_time\x18\x13 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x36\n\x0clast_failure\x18\x14 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x1c\n\x14last_worker_identity\x18\x15 \x01(\t\x12\x39\n\x16\x63urrent_retry_interval\x18\x16 \x01(\x0b\x32\x19.google.protobuf.Duration\x12>\n\x1alast_attempt_complete_time\x18\x17 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x1anext_attempt_schedule_time\x18\x18 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12T\n\x17last_deployment_version\x18\x19 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x32\n\x08priority\x18\x1a \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12\x1e\n\x16state_transition_count\x18\x1b \x01(\x03\x12\x18\n\x10state_size_bytes\x18\x1c \x01(\x03\x12\x43\n\x11search_attributes\x18\x1d \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12.\n\x06header\x18\x1e \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x38\n\ruser_metadata\x18\x1f \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12\x17\n\x0f\x63\x61nceled_reason\x18 \x01(\t\x12+\n\x05links\x18! \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x12\x1d\n\x15total_heartbeat_count\x18" \x01(\x03\x12\x10\n\x08sdk_name\x18# \x01(\t\x12\x13\n\x0bsdk_version\x18$ \x01(\t\x12.\n\x0bstart_delay\x18% \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x32\n\x0e\x65xecution_time\x18& \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\x9e\x04\n\x19\x41\x63tivityExecutionListInfo\x12\x13\n\x0b\x61\x63tivity_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12;\n\ractivity_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12\x31\n\rschedule_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nclose_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x06status\x18\x06 \x01(\x0e\x32..temporal.api.enums.v1.ActivityExecutionStatus\x12\x43\n\x11search_attributes\x18\x07 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x12\n\ntask_queue\x18\x08 \x01(\t\x12\x1e\n\x16state_transition_count\x18\t \x01(\x03\x12\x18\n\x10state_size_bytes\x18\n \x01(\x03\x12\x35\n\x12\x65xecution_duration\x18\x0b \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x32\n\x0e\x65xecution_time\x18\x0c \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xff\x01\n\x0c\x43\x61llbackInfo\x12?\n\x07trigger\x18\x01 \x01(\x0b\x32..temporal.api.activity.v1.CallbackInfo.Trigger\x12\x34\n\x04info\x18\x02 \x01(\x0b\x32&.temporal.api.callback.v1.CallbackInfo\x1a\x10\n\x0e\x41\x63tivityClosed\x1a\x66\n\x07Trigger\x12P\n\x0f\x61\x63tivity_closed\x18\x01 \x01(\x0b\x32\x35.temporal.api.activity.v1.CallbackInfo.ActivityClosedH\x00\x42\t\n\x07variantB\x93\x01\n\x1bio.temporal.api.activity.v1B\x0cMessageProtoP\x01Z\'go.temporal.io/api/activity/v1;activity\xaa\x02\x1aTemporalio.Api.Activity.V1\xea\x02\x1dTemporalio::Api::Activity::V1b\x06proto3' ) @@ -135,17 +135,17 @@ DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b"\n\033io.temporal.api.activity.v1B\014MessageProtoP\001Z'go.temporal.io/api/activity/v1;activity\252\002\032Temporalio.Api.Activity.V1\352\002\035Temporalio::Api::Activity::V1" _ACTIVITYEXECUTIONOUTCOME._serialized_start = 451 - _ACTIVITYEXECUTIONOUTCOME._serialized_end = 591 - _ACTIVITYOPTIONS._serialized_start = 594 - _ACTIVITYOPTIONS._serialized_end = 1017 - _ACTIVITYEXECUTIONINFO._serialized_start = 1020 - _ACTIVITYEXECUTIONINFO._serialized_end = 2814 - _ACTIVITYEXECUTIONLISTINFO._serialized_start = 2817 - _ACTIVITYEXECUTIONLISTINFO._serialized_end = 3307 - _CALLBACKINFO._serialized_start = 3310 - _CALLBACKINFO._serialized_end = 3565 - _CALLBACKINFO_ACTIVITYCLOSED._serialized_start = 3445 - _CALLBACKINFO_ACTIVITYCLOSED._serialized_end = 3461 - _CALLBACKINFO_TRIGGER._serialized_start = 3463 - _CALLBACKINFO_TRIGGER._serialized_end = 3565 + _ACTIVITYEXECUTIONOUTCOME._serialized_end = 647 + _ACTIVITYOPTIONS._serialized_start = 650 + _ACTIVITYOPTIONS._serialized_end = 1121 + _ACTIVITYEXECUTIONINFO._serialized_start = 1124 + _ACTIVITYEXECUTIONINFO._serialized_end = 2970 + _ACTIVITYEXECUTIONLISTINFO._serialized_start = 2973 + _ACTIVITYEXECUTIONLISTINFO._serialized_end = 3515 + _CALLBACKINFO._serialized_start = 3518 + _CALLBACKINFO._serialized_end = 3773 + _CALLBACKINFO_ACTIVITYCLOSED._serialized_start = 3653 + _CALLBACKINFO_ACTIVITYCLOSED._serialized_end = 3669 + _CALLBACKINFO_TRIGGER._serialized_start = 3671 + _CALLBACKINFO_TRIGGER._serialized_end = 3773 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/activity/v1/message_pb2.pyi b/temporalio/api/activity/v1/message_pb2.pyi index e57f3860c..684233f95 100644 --- a/temporalio/api/activity/v1/message_pb2.pyi +++ b/temporalio/api/activity/v1/message_pb2.pyi @@ -36,17 +36,23 @@ class ActivityExecutionOutcome(google.protobuf.message.Message): RESULT_FIELD_NUMBER: builtins.int FAILURE_FIELD_NUMBER: builtins.int + RETRY_STATE_FIELD_NUMBER: builtins.int @property def result(self) -> temporalio.api.common.v1.message_pb2.Payloads: """The result if the activity completed successfully.""" @property def failure(self) -> temporalio.api.failure.v1.message_pb2.Failure: """The failure if the activity completed unsuccessfully.""" + retry_state: temporalio.api.enums.v1.workflow_pb2.RetryState.ValueType + """The retry state associated with an unsuccessful activity execution. + This field is only meaningful when `failure` is set. + """ def __init__( self, *, result: temporalio.api.common.v1.message_pb2.Payloads | None = ..., failure: temporalio.api.failure.v1.message_pb2.Failure | None = ..., + retry_state: temporalio.api.enums.v1.workflow_pb2.RetryState.ValueType = ..., ) -> None: ... def HasField( self, @@ -57,7 +63,14 @@ class ActivityExecutionOutcome(google.protobuf.message.Message): def ClearField( self, field_name: typing_extensions.Literal[ - "failure", b"failure", "result", b"result", "value", b"value" + "failure", + b"failure", + "result", + b"result", + "retry_state", + b"retry_state", + "value", + b"value", ], ) -> None: ... def WhichOneof( @@ -76,6 +89,7 @@ class ActivityOptions(google.protobuf.message.Message): HEARTBEAT_TIMEOUT_FIELD_NUMBER: builtins.int RETRY_POLICY_FIELD_NUMBER: builtins.int PRIORITY_FIELD_NUMBER: builtins.int + START_DELAY_FIELD_NUMBER: builtins.int @property def task_queue(self) -> temporalio.api.taskqueue.v1.message_pb2.TaskQueue: ... @property @@ -116,6 +130,12 @@ class ActivityOptions(google.protobuf.message.Message): """Priority metadata. If this message is not present, or any fields are not present, they inherit the values from the workflow. """ + @property + def start_delay(self) -> google.protobuf.duration_pb2.Duration: + """Time to wait before making the first activity task available for dispatch. This delay is not applied to retry attempts. + When updated, the time is added to the original `schedule_time`, not to the current time. + If the resulting time is in the past, the task is made available for dispatch immediately. + """ def __init__( self, *, @@ -126,6 +146,7 @@ class ActivityOptions(google.protobuf.message.Message): heartbeat_timeout: google.protobuf.duration_pb2.Duration | None = ..., retry_policy: temporalio.api.common.v1.message_pb2.RetryPolicy | None = ..., priority: temporalio.api.common.v1.message_pb2.Priority | None = ..., + start_delay: google.protobuf.duration_pb2.Duration | None = ..., ) -> None: ... def HasField( self, @@ -140,6 +161,8 @@ class ActivityOptions(google.protobuf.message.Message): b"schedule_to_close_timeout", "schedule_to_start_timeout", b"schedule_to_start_timeout", + "start_delay", + b"start_delay", "start_to_close_timeout", b"start_to_close_timeout", "task_queue", @@ -159,6 +182,8 @@ class ActivityOptions(google.protobuf.message.Message): b"schedule_to_close_timeout", "schedule_to_start_timeout", b"schedule_to_start_timeout", + "start_delay", + b"start_delay", "start_to_close_timeout", b"start_to_close_timeout", "task_queue", @@ -210,6 +235,7 @@ class ActivityExecutionInfo(google.protobuf.message.Message): SDK_NAME_FIELD_NUMBER: builtins.int SDK_VERSION_FIELD_NUMBER: builtins.int START_DELAY_FIELD_NUMBER: builtins.int + EXECUTION_TIME_FIELD_NUMBER: builtins.int activity_id: builtins.str """Unique identifier of this activity within its namespace along with run ID (below).""" run_id: builtins.str @@ -273,7 +299,9 @@ class ActivityExecutionInfo(google.protobuf.message.Message): """Time the activity was originally scheduled via a StartActivityExecution request.""" @property def expiration_time(self) -> google.protobuf.timestamp_pb2.Timestamp: - """Scheduled time + schedule to close timeout.""" + """The time at which the activity's Schedule-to-Close timeout expires. + Calculated as `schedule_time` + `start_delay` + `schedule_to_close_timeout`. + """ @property def close_time(self) -> google.protobuf.timestamp_pb2.Timestamp: """Time when the activity transitioned to a closed state.""" @@ -344,7 +372,12 @@ class ActivityExecutionInfo(google.protobuf.message.Message): """ @property def start_delay(self) -> google.protobuf.duration_pb2.Duration: - """Time to wait before dispatching the first activity task. This delay is not applied to retry attempts.""" + """Time to wait before making the first activity task available for dispatch. This delay is not applied to retry attempts.""" + @property + def execution_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """The time at which the first activity task is made available for dispatch, computed as + `schedule_time + start_delay`. Same as `schedule_time` if `start_delay` is not set. + """ def __init__( self, *, @@ -391,6 +424,7 @@ class ActivityExecutionInfo(google.protobuf.message.Message): sdk_name: builtins.str = ..., sdk_version: builtins.str = ..., start_delay: google.protobuf.duration_pb2.Duration | None = ..., + execution_time: google.protobuf.timestamp_pb2.Timestamp | None = ..., ) -> None: ... def HasField( self, @@ -403,6 +437,8 @@ class ActivityExecutionInfo(google.protobuf.message.Message): b"current_retry_interval", "execution_duration", b"execution_duration", + "execution_time", + b"execution_time", "expiration_time", b"expiration_time", "header", @@ -460,6 +496,8 @@ class ActivityExecutionInfo(google.protobuf.message.Message): b"current_retry_interval", "execution_duration", b"execution_duration", + "execution_time", + b"execution_time", "expiration_time", b"expiration_time", "header", @@ -544,6 +582,7 @@ class ActivityExecutionListInfo(google.protobuf.message.Message): STATE_TRANSITION_COUNT_FIELD_NUMBER: builtins.int STATE_SIZE_BYTES_FIELD_NUMBER: builtins.int EXECUTION_DURATION_FIELD_NUMBER: builtins.int + EXECUTION_TIME_FIELD_NUMBER: builtins.int activity_id: builtins.str """A unique identifier of this activity within its namespace along with run ID (below).""" run_id: builtins.str @@ -577,6 +616,11 @@ class ActivityExecutionListInfo(google.protobuf.message.Message): """The difference between close time and scheduled time. This field is only populated if the activity is closed. """ + @property + def execution_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """The time at which the first activity task is made available for dispatch, computed as + `schedule_time + start_delay`. Same as `schedule_time` if `start_delay` is not set. + """ def __init__( self, *, @@ -592,6 +636,7 @@ class ActivityExecutionListInfo(google.protobuf.message.Message): state_transition_count: builtins.int = ..., state_size_bytes: builtins.int = ..., execution_duration: google.protobuf.duration_pb2.Duration | None = ..., + execution_time: google.protobuf.timestamp_pb2.Timestamp | None = ..., ) -> None: ... def HasField( self, @@ -602,6 +647,8 @@ class ActivityExecutionListInfo(google.protobuf.message.Message): b"close_time", "execution_duration", b"execution_duration", + "execution_time", + b"execution_time", "schedule_time", b"schedule_time", "search_attributes", @@ -619,6 +666,8 @@ class ActivityExecutionListInfo(google.protobuf.message.Message): b"close_time", "execution_duration", b"execution_duration", + "execution_time", + b"execution_time", "run_id", b"run_id", "schedule_time", diff --git a/temporalio/api/batch/v1/__init__.py b/temporalio/api/batch/v1/__init__.py index 5ec8ad63a..fea70b41f 100644 --- a/temporalio/api/batch/v1/__init__.py +++ b/temporalio/api/batch/v1/__init__.py @@ -1,10 +1,13 @@ from .message_pb2 import ( + BatchOperationCancelActivities, BatchOperationCancellation, + BatchOperationDeleteActivities, BatchOperationDeletion, BatchOperationInfo, BatchOperationReset, BatchOperationResetActivities, BatchOperationSignal, + BatchOperationTerminateActivities, BatchOperationTermination, BatchOperationTriggerWorkflowRule, BatchOperationUnpauseActivities, @@ -13,12 +16,15 @@ ) __all__ = [ + "BatchOperationCancelActivities", "BatchOperationCancellation", + "BatchOperationDeleteActivities", "BatchOperationDeletion", "BatchOperationInfo", "BatchOperationReset", "BatchOperationResetActivities", "BatchOperationSignal", + "BatchOperationTerminateActivities", "BatchOperationTermination", "BatchOperationTriggerWorkflowRule", "BatchOperationUnpauseActivities", diff --git a/temporalio/api/batch/v1/message_pb2.py b/temporalio/api/batch/v1/message_pb2.py index cb1e39b73..292b2c182 100644 --- a/temporalio/api/batch/v1/message_pb2.py +++ b/temporalio/api/batch/v1/message_pb2.py @@ -38,7 +38,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n#temporal/api/batch/v1/message.proto\x12\x15temporal.api.batch.v1\x1a\x1egoogle/protobuf/duration.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a&temporal/api/activity/v1/message.proto\x1a$temporal/api/common/v1/message.proto\x1a+temporal/api/enums/v1/batch_operation.proto\x1a!temporal/api/enums/v1/reset.proto\x1a#temporal/api/rules/v1/message.proto\x1a&temporal/api/workflow/v1/message.proto"\xbf\x01\n\x12\x42\x61tchOperationInfo\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\x39\n\x05state\x18\x02 \x01(\x0e\x32*.temporal.api.enums.v1.BatchOperationState\x12.\n\nstart_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nclose_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"`\n\x19\x42\x61tchOperationTermination\x12\x31\n\x07\x64\x65tails\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x02 \x01(\t"\x99\x01\n\x14\x42\x61tchOperationSignal\x12\x0e\n\x06signal\x18\x01 \x01(\t\x12/\n\x05input\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12.\n\x06header\x18\x03 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x10\n\x08identity\x18\x04 \x01(\t".\n\x1a\x42\x61tchOperationCancellation\x12\x10\n\x08identity\x18\x01 \x01(\t"*\n\x16\x42\x61tchOperationDeletion\x12\x10\n\x08identity\x18\x01 \x01(\t"\xae\x02\n\x13\x42\x61tchOperationReset\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x35\n\x07options\x18\x04 \x01(\x0b\x32$.temporal.api.common.v1.ResetOptions\x12\x38\n\nreset_type\x18\x01 \x01(\x0e\x32 .temporal.api.enums.v1.ResetTypeB\x02\x18\x01\x12G\n\x12reset_reapply_type\x18\x02 \x01(\x0e\x32\'.temporal.api.enums.v1.ResetReapplyTypeB\x02\x18\x01\x12K\n\x15post_reset_operations\x18\x05 \x03(\x0b\x32,.temporal.api.workflow.v1.PostResetOperation"\xc9\x01\n,BatchOperationUpdateWorkflowExecutionOptions\x12\x10\n\x08identity\x18\x01 \x01(\t\x12V\n\x1aworkflow_execution_options\x18\x02 \x01(\x0b\x32\x32.temporal.api.workflow.v1.WorkflowExecutionOptions\x12/\n\x0bupdate_mask\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.FieldMask"\xc0\x01\n\x1f\x42\x61tchOperationUnpauseActivities\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x04type\x18\x02 \x01(\tH\x00\x12\x13\n\tmatch_all\x18\x03 \x01(\x08H\x00\x12\x16\n\x0ereset_attempts\x18\x04 \x01(\x08\x12\x17\n\x0freset_heartbeat\x18\x05 \x01(\x08\x12)\n\x06jitter\x18\x06 \x01(\x0b\x32\x19.google.protobuf.DurationB\n\n\x08\x61\x63tivity"\x84\x01\n!BatchOperationTriggerWorkflowRule\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0c\n\x02id\x18\x02 \x01(\tH\x00\x12\x37\n\x04spec\x18\x03 \x01(\x0b\x32\'.temporal.api.rules.v1.WorkflowRuleSpecH\x00\x42\x06\n\x04rule"\xf5\x01\n\x1d\x42\x61tchOperationResetActivities\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x04type\x18\x02 \x01(\tH\x00\x12\x13\n\tmatch_all\x18\x03 \x01(\x08H\x00\x12\x16\n\x0ereset_attempts\x18\x04 \x01(\x08\x12\x17\n\x0freset_heartbeat\x18\x05 \x01(\x08\x12\x13\n\x0bkeep_paused\x18\x06 \x01(\x08\x12)\n\x06jitter\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12 \n\x18restore_original_options\x18\x08 \x01(\x08\x42\n\n\x08\x61\x63tivity"\xf8\x01\n#BatchOperationUpdateActivityOptions\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x04type\x18\x02 \x01(\tH\x00\x12\x13\n\tmatch_all\x18\x03 \x01(\x08H\x00\x12\x43\n\x10\x61\x63tivity_options\x18\x04 \x01(\x0b\x32).temporal.api.activity.v1.ActivityOptions\x12/\n\x0bupdate_mask\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.FieldMask\x12\x18\n\x10restore_original\x18\x06 \x01(\x08\x42\n\n\x08\x61\x63tivityB\x84\x01\n\x18io.temporal.api.batch.v1B\x0cMessageProtoP\x01Z!go.temporal.io/api/batch/v1;batch\xaa\x02\x17Temporalio.Api.Batch.V1\xea\x02\x1aTemporalio::Api::Batch::V1b\x06proto3' + b'\n#temporal/api/batch/v1/message.proto\x12\x15temporal.api.batch.v1\x1a\x1egoogle/protobuf/duration.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a&temporal/api/activity/v1/message.proto\x1a$temporal/api/common/v1/message.proto\x1a+temporal/api/enums/v1/batch_operation.proto\x1a!temporal/api/enums/v1/reset.proto\x1a#temporal/api/rules/v1/message.proto\x1a&temporal/api/workflow/v1/message.proto"\x82\x02\n\x12\x42\x61tchOperationInfo\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\x39\n\x05state\x18\x02 \x01(\x0e\x32*.temporal.api.enums.v1.BatchOperationState\x12.\n\nstart_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nclose_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x41\n\x0eoperation_type\x18\x05 \x01(\x0e\x32).temporal.api.enums.v1.BatchOperationType"`\n\x19\x42\x61tchOperationTermination\x12\x31\n\x07\x64\x65tails\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x02 \x01(\t"E\n!BatchOperationTerminateActivities\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t"\x99\x01\n\x14\x42\x61tchOperationSignal\x12\x0e\n\x06signal\x18\x01 \x01(\t\x12/\n\x05input\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12.\n\x06header\x18\x03 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x10\n\x08identity\x18\x04 \x01(\t".\n\x1a\x42\x61tchOperationCancellation\x12\x10\n\x08identity\x18\x01 \x01(\t"B\n\x1e\x42\x61tchOperationCancelActivities\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t"*\n\x16\x42\x61tchOperationDeletion\x12\x10\n\x08identity\x18\x01 \x01(\t" \n\x1e\x42\x61tchOperationDeleteActivities"\xae\x02\n\x13\x42\x61tchOperationReset\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x35\n\x07options\x18\x04 \x01(\x0b\x32$.temporal.api.common.v1.ResetOptions\x12\x38\n\nreset_type\x18\x01 \x01(\x0e\x32 .temporal.api.enums.v1.ResetTypeB\x02\x18\x01\x12G\n\x12reset_reapply_type\x18\x02 \x01(\x0e\x32\'.temporal.api.enums.v1.ResetReapplyTypeB\x02\x18\x01\x12K\n\x15post_reset_operations\x18\x05 \x03(\x0b\x32,.temporal.api.workflow.v1.PostResetOperation"\xc9\x01\n,BatchOperationUpdateWorkflowExecutionOptions\x12\x10\n\x08identity\x18\x01 \x01(\t\x12V\n\x1aworkflow_execution_options\x18\x02 \x01(\x0b\x32\x32.temporal.api.workflow.v1.WorkflowExecutionOptions\x12/\n\x0bupdate_mask\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.FieldMask"\xc0\x01\n\x1f\x42\x61tchOperationUnpauseActivities\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x04type\x18\x02 \x01(\tH\x00\x12\x13\n\tmatch_all\x18\x03 \x01(\x08H\x00\x12\x16\n\x0ereset_attempts\x18\x04 \x01(\x08\x12\x17\n\x0freset_heartbeat\x18\x05 \x01(\x08\x12)\n\x06jitter\x18\x06 \x01(\x0b\x32\x19.google.protobuf.DurationB\n\n\x08\x61\x63tivity"\x84\x01\n!BatchOperationTriggerWorkflowRule\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0c\n\x02id\x18\x02 \x01(\tH\x00\x12\x37\n\x04spec\x18\x03 \x01(\x0b\x32\'.temporal.api.rules.v1.WorkflowRuleSpecH\x00\x42\x06\n\x04rule"\xf5\x01\n\x1d\x42\x61tchOperationResetActivities\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x04type\x18\x02 \x01(\tH\x00\x12\x13\n\tmatch_all\x18\x03 \x01(\x08H\x00\x12\x16\n\x0ereset_attempts\x18\x04 \x01(\x08\x12\x17\n\x0freset_heartbeat\x18\x05 \x01(\x08\x12\x13\n\x0bkeep_paused\x18\x06 \x01(\x08\x12)\n\x06jitter\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12 \n\x18restore_original_options\x18\x08 \x01(\x08\x42\n\n\x08\x61\x63tivity"\xf8\x01\n#BatchOperationUpdateActivityOptions\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x04type\x18\x02 \x01(\tH\x00\x12\x13\n\tmatch_all\x18\x03 \x01(\x08H\x00\x12\x43\n\x10\x61\x63tivity_options\x18\x04 \x01(\x0b\x32).temporal.api.activity.v1.ActivityOptions\x12/\n\x0bupdate_mask\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.FieldMask\x12\x18\n\x10restore_original\x18\x06 \x01(\x08\x42\n\n\x08\x61\x63tivityB\x84\x01\n\x18io.temporal.api.batch.v1B\x0cMessageProtoP\x01Z!go.temporal.io/api/batch/v1;batch\xaa\x02\x17Temporalio.Api.Batch.V1\xea\x02\x1aTemporalio::Api::Batch::V1b\x06proto3' ) @@ -46,11 +46,20 @@ _BATCHOPERATIONTERMINATION = DESCRIPTOR.message_types_by_name[ "BatchOperationTermination" ] +_BATCHOPERATIONTERMINATEACTIVITIES = DESCRIPTOR.message_types_by_name[ + "BatchOperationTerminateActivities" +] _BATCHOPERATIONSIGNAL = DESCRIPTOR.message_types_by_name["BatchOperationSignal"] _BATCHOPERATIONCANCELLATION = DESCRIPTOR.message_types_by_name[ "BatchOperationCancellation" ] +_BATCHOPERATIONCANCELACTIVITIES = DESCRIPTOR.message_types_by_name[ + "BatchOperationCancelActivities" +] _BATCHOPERATIONDELETION = DESCRIPTOR.message_types_by_name["BatchOperationDeletion"] +_BATCHOPERATIONDELETEACTIVITIES = DESCRIPTOR.message_types_by_name[ + "BatchOperationDeleteActivities" +] _BATCHOPERATIONRESET = DESCRIPTOR.message_types_by_name["BatchOperationReset"] _BATCHOPERATIONUPDATEWORKFLOWEXECUTIONOPTIONS = DESCRIPTOR.message_types_by_name[ "BatchOperationUpdateWorkflowExecutionOptions" @@ -89,6 +98,17 @@ ) _sym_db.RegisterMessage(BatchOperationTermination) +BatchOperationTerminateActivities = _reflection.GeneratedProtocolMessageType( + "BatchOperationTerminateActivities", + (_message.Message,), + { + "DESCRIPTOR": _BATCHOPERATIONTERMINATEACTIVITIES, + "__module__": "temporalio.api.batch.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.batch.v1.BatchOperationTerminateActivities) + }, +) +_sym_db.RegisterMessage(BatchOperationTerminateActivities) + BatchOperationSignal = _reflection.GeneratedProtocolMessageType( "BatchOperationSignal", (_message.Message,), @@ -111,6 +131,17 @@ ) _sym_db.RegisterMessage(BatchOperationCancellation) +BatchOperationCancelActivities = _reflection.GeneratedProtocolMessageType( + "BatchOperationCancelActivities", + (_message.Message,), + { + "DESCRIPTOR": _BATCHOPERATIONCANCELACTIVITIES, + "__module__": "temporalio.api.batch.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.batch.v1.BatchOperationCancelActivities) + }, +) +_sym_db.RegisterMessage(BatchOperationCancelActivities) + BatchOperationDeletion = _reflection.GeneratedProtocolMessageType( "BatchOperationDeletion", (_message.Message,), @@ -122,6 +153,17 @@ ) _sym_db.RegisterMessage(BatchOperationDeletion) +BatchOperationDeleteActivities = _reflection.GeneratedProtocolMessageType( + "BatchOperationDeleteActivities", + (_message.Message,), + { + "DESCRIPTOR": _BATCHOPERATIONDELETEACTIVITIES, + "__module__": "temporalio.api.batch.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.batch.v1.BatchOperationDeleteActivities) + }, +) +_sym_db.RegisterMessage(BatchOperationDeleteActivities) + BatchOperationReset = _reflection.GeneratedProtocolMessageType( "BatchOperationReset", (_message.Message,), @@ -198,25 +240,31 @@ "reset_reapply_type" ]._serialized_options = b"\030\001" _BATCHOPERATIONINFO._serialized_start = 397 - _BATCHOPERATIONINFO._serialized_end = 588 - _BATCHOPERATIONTERMINATION._serialized_start = 590 - _BATCHOPERATIONTERMINATION._serialized_end = 686 - _BATCHOPERATIONSIGNAL._serialized_start = 689 - _BATCHOPERATIONSIGNAL._serialized_end = 842 - _BATCHOPERATIONCANCELLATION._serialized_start = 844 - _BATCHOPERATIONCANCELLATION._serialized_end = 890 - _BATCHOPERATIONDELETION._serialized_start = 892 - _BATCHOPERATIONDELETION._serialized_end = 934 - _BATCHOPERATIONRESET._serialized_start = 937 - _BATCHOPERATIONRESET._serialized_end = 1239 - _BATCHOPERATIONUPDATEWORKFLOWEXECUTIONOPTIONS._serialized_start = 1242 - _BATCHOPERATIONUPDATEWORKFLOWEXECUTIONOPTIONS._serialized_end = 1443 - _BATCHOPERATIONUNPAUSEACTIVITIES._serialized_start = 1446 - _BATCHOPERATIONUNPAUSEACTIVITIES._serialized_end = 1638 - _BATCHOPERATIONTRIGGERWORKFLOWRULE._serialized_start = 1641 - _BATCHOPERATIONTRIGGERWORKFLOWRULE._serialized_end = 1773 - _BATCHOPERATIONRESETACTIVITIES._serialized_start = 1776 - _BATCHOPERATIONRESETACTIVITIES._serialized_end = 2021 - _BATCHOPERATIONUPDATEACTIVITYOPTIONS._serialized_start = 2024 - _BATCHOPERATIONUPDATEACTIVITYOPTIONS._serialized_end = 2272 + _BATCHOPERATIONINFO._serialized_end = 655 + _BATCHOPERATIONTERMINATION._serialized_start = 657 + _BATCHOPERATIONTERMINATION._serialized_end = 753 + _BATCHOPERATIONTERMINATEACTIVITIES._serialized_start = 755 + _BATCHOPERATIONTERMINATEACTIVITIES._serialized_end = 824 + _BATCHOPERATIONSIGNAL._serialized_start = 827 + _BATCHOPERATIONSIGNAL._serialized_end = 980 + _BATCHOPERATIONCANCELLATION._serialized_start = 982 + _BATCHOPERATIONCANCELLATION._serialized_end = 1028 + _BATCHOPERATIONCANCELACTIVITIES._serialized_start = 1030 + _BATCHOPERATIONCANCELACTIVITIES._serialized_end = 1096 + _BATCHOPERATIONDELETION._serialized_start = 1098 + _BATCHOPERATIONDELETION._serialized_end = 1140 + _BATCHOPERATIONDELETEACTIVITIES._serialized_start = 1142 + _BATCHOPERATIONDELETEACTIVITIES._serialized_end = 1174 + _BATCHOPERATIONRESET._serialized_start = 1177 + _BATCHOPERATIONRESET._serialized_end = 1479 + _BATCHOPERATIONUPDATEWORKFLOWEXECUTIONOPTIONS._serialized_start = 1482 + _BATCHOPERATIONUPDATEWORKFLOWEXECUTIONOPTIONS._serialized_end = 1683 + _BATCHOPERATIONUNPAUSEACTIVITIES._serialized_start = 1686 + _BATCHOPERATIONUNPAUSEACTIVITIES._serialized_end = 1878 + _BATCHOPERATIONTRIGGERWORKFLOWRULE._serialized_start = 1881 + _BATCHOPERATIONTRIGGERWORKFLOWRULE._serialized_end = 2013 + _BATCHOPERATIONRESETACTIVITIES._serialized_start = 2016 + _BATCHOPERATIONRESETACTIVITIES._serialized_end = 2261 + _BATCHOPERATIONUPDATEACTIVITYOPTIONS._serialized_start = 2264 + _BATCHOPERATIONUPDATEACTIVITYOPTIONS._serialized_end = 2512 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/batch/v1/message_pb2.pyi b/temporalio/api/batch/v1/message_pb2.pyi index 0f7e87da0..106b351ef 100644 --- a/temporalio/api/batch/v1/message_pb2.pyi +++ b/temporalio/api/batch/v1/message_pb2.pyi @@ -35,6 +35,7 @@ class BatchOperationInfo(google.protobuf.message.Message): STATE_FIELD_NUMBER: builtins.int START_TIME_FIELD_NUMBER: builtins.int CLOSE_TIME_FIELD_NUMBER: builtins.int + OPERATION_TYPE_FIELD_NUMBER: builtins.int job_id: builtins.str """Batch job ID""" state: temporalio.api.enums.v1.batch_operation_pb2.BatchOperationState.ValueType @@ -45,6 +46,10 @@ class BatchOperationInfo(google.protobuf.message.Message): @property def close_time(self) -> google.protobuf.timestamp_pb2.Timestamp: """Batch operation close time""" + operation_type: ( + temporalio.api.enums.v1.batch_operation_pb2.BatchOperationType.ValueType + ) + """Operation type""" def __init__( self, *, @@ -52,6 +57,7 @@ class BatchOperationInfo(google.protobuf.message.Message): state: temporalio.api.enums.v1.batch_operation_pb2.BatchOperationState.ValueType = ..., start_time: google.protobuf.timestamp_pb2.Timestamp | None = ..., close_time: google.protobuf.timestamp_pb2.Timestamp | None = ..., + operation_type: temporalio.api.enums.v1.batch_operation_pb2.BatchOperationType.ValueType = ..., ) -> None: ... def HasField( self, @@ -66,6 +72,8 @@ class BatchOperationInfo(google.protobuf.message.Message): b"close_time", "job_id", b"job_id", + "operation_type", + b"operation_type", "start_time", b"start_time", "state", @@ -108,6 +116,36 @@ class BatchOperationTermination(google.protobuf.message.Message): global___BatchOperationTermination = BatchOperationTermination +class BatchOperationTerminateActivities(google.protobuf.message.Message): + """BatchOperationTerminateActivities sends terminate requests to a batch of activities. + Keep the parameter in sync with temporalio.api.workflowservice.v1.TerminateActivityExecutionRequest. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + IDENTITY_FIELD_NUMBER: builtins.int + REASON_FIELD_NUMBER: builtins.int + identity: builtins.str + """The identity of the worker/client""" + reason: builtins.str + """Reason for requesting the termination, recorded and available via the PollActivityExecution API. + Not propagated to a worker if an activity attempt is currently running. + """ + def __init__( + self, + *, + identity: builtins.str = ..., + reason: builtins.str = ..., + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "identity", b"identity", "reason", b"reason" + ], + ) -> None: ... + +global___BatchOperationTerminateActivities = BatchOperationTerminateActivities + class BatchOperationSignal(google.protobuf.message.Message): """BatchOperationSignal sends signals to batch workflows. Keep the parameter in sync with temporalio.api.workflowservice.v1.SignalWorkflowExecutionRequest. @@ -181,6 +219,36 @@ class BatchOperationCancellation(google.protobuf.message.Message): global___BatchOperationCancellation = BatchOperationCancellation +class BatchOperationCancelActivities(google.protobuf.message.Message): + """BatchOperationCancelActivities sends cancel requests to a batch of activities. + Keep the parameter in sync with temporalio.api.workflowservice.v1.RequestCancelActivityExecutionRequest. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + IDENTITY_FIELD_NUMBER: builtins.int + REASON_FIELD_NUMBER: builtins.int + identity: builtins.str + """The identity of the worker/client""" + reason: builtins.str + """Reason for requesting the cancellation, recorded and available via the PollActivityExecution API. + Not propagated to a worker if an activity attempt is currently running. + """ + def __init__( + self, + *, + identity: builtins.str = ..., + reason: builtins.str = ..., + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "identity", b"identity", "reason", b"reason" + ], + ) -> None: ... + +global___BatchOperationCancelActivities = BatchOperationCancelActivities + class BatchOperationDeletion(google.protobuf.message.Message): """BatchOperationDeletion sends deletion requests to batch workflows. Keep the parameter in sync with temporalio.api.workflowservice.v1.DeleteWorkflowExecutionRequest. @@ -202,6 +270,19 @@ class BatchOperationDeletion(google.protobuf.message.Message): global___BatchOperationDeletion = BatchOperationDeletion +class BatchOperationDeleteActivities(google.protobuf.message.Message): + """BatchOperationDeleteActivities sends deletion requests to a batch of activities. + Keep the parameter in sync with temporalio.api.workflowservice.v1.DeleteActivityExecutionRequest. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___BatchOperationDeleteActivities = BatchOperationDeleteActivities + class BatchOperationReset(google.protobuf.message.Message): """BatchOperationReset sends reset requests to batch workflows. Keep the parameter in sync with temporalio.api.workflowservice.v1.ResetWorkflowExecutionRequest. diff --git a/temporalio/api/command/v1/message_pb2.py b/temporalio/api/command/v1/message_pb2.py index fe94363f5..ad004ae03 100644 --- a/temporalio/api/command/v1/message_pb2.py +++ b/temporalio/api/command/v1/message_pb2.py @@ -28,15 +28,21 @@ from temporalio.api.failure.v1 import ( message_pb2 as temporal_dot_api_dot_failure_dot_v1_dot_message__pb2, ) +from temporalio.api.sdk.v1 import ( + event_group_marker_pb2 as temporal_dot_api_dot_sdk_dot_v1_dot_event__group__marker__pb2, +) from temporalio.api.sdk.v1 import ( user_metadata_pb2 as temporal_dot_api_dot_sdk_dot_v1_dot_user__metadata__pb2, ) from temporalio.api.taskqueue.v1 import ( message_pb2 as temporal_dot_api_dot_taskqueue_dot_v1_dot_message__pb2, ) +from temporalio.api.workflow.v1 import ( + message_pb2 as temporal_dot_api_dot_workflow_dot_v1_dot_message__pb2, +) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n%temporal/api/command/v1/message.proto\x12\x17temporal.api.command.v1\x1a\x1egoogle/protobuf/duration.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a(temporal/api/enums/v1/command_type.proto\x1a$temporal/api/common/v1/message.proto\x1a%temporal/api/failure/v1/message.proto\x1a\'temporal/api/taskqueue/v1/message.proto\x1a\'temporal/api/sdk/v1/user_metadata.proto"\xb6\x05\n%ScheduleActivityTaskCommandAttributes\x12\x13\n\x0b\x61\x63tivity_id\x18\x01 \x01(\t\x12;\n\ractivity_type\x18\x02 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12\x38\n\ntask_queue\x18\x04 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12.\n\x06header\x18\x05 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12/\n\x05input\x18\x06 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12<\n\x19schedule_to_close_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x0b \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x1f\n\x17request_eager_execution\x18\x0c \x01(\x08\x12\x1d\n\x15use_workflow_build_id\x18\r \x01(\x08\x12\x32\n\x08priority\x18\x0e \x01(\x0b\x32 .temporal.api.common.v1.PriorityJ\x04\x08\x03\x10\x04"H\n*RequestCancelActivityTaskCommandAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03"i\n\x1bStartTimerCommandAttributes\x12\x10\n\x08timer_id\x18\x01 \x01(\t\x12\x38\n\x15start_to_fire_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration"^\n*CompleteWorkflowExecutionCommandAttributes\x12\x30\n\x06result\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads"[\n&FailWorkflowExecutionCommandAttributes\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure"0\n\x1c\x43\x61ncelTimerCommandAttributes\x12\x10\n\x08timer_id\x18\x01 \x01(\t"]\n(CancelWorkflowExecutionCommandAttributes\x12\x31\n\x07\x64\x65tails\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads"\xb7\x01\n7RequestCancelExternalWorkflowExecutionCommandAttributes\x12\x15\n\tnamespace\x18\x01 \x01(\tB\x02\x18\x01\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x13\n\x07\x63ontrol\x18\x04 \x01(\tB\x02\x18\x01\x12\x1b\n\x13\x63hild_workflow_only\x18\x05 \x01(\x08\x12\x0e\n\x06reason\x18\x06 \x01(\t"\xaf\x02\n0SignalExternalWorkflowExecutionCommandAttributes\x12\x15\n\tnamespace\x18\x01 \x01(\tB\x02\x18\x01\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x13\n\x0bsignal_name\x18\x03 \x01(\t\x12/\n\x05input\x18\x04 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x13\n\x07\x63ontrol\x18\x05 \x01(\tB\x02\x18\x01\x12\x1b\n\x13\x63hild_workflow_only\x18\x06 \x01(\x08\x12.\n\x06header\x18\x07 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header"v\n/UpsertWorkflowSearchAttributesCommandAttributes\x12\x43\n\x11search_attributes\x18\x01 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes"`\n)ModifyWorkflowPropertiesCommandAttributes\x12\x33\n\rupserted_memo\x18\x01 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo"\xbf\x02\n\x1dRecordMarkerCommandAttributes\x12\x13\n\x0bmarker_name\x18\x01 \x01(\t\x12T\n\x07\x64\x65tails\x18\x02 \x03(\x0b\x32\x43.temporal.api.command.v1.RecordMarkerCommandAttributes.DetailsEntry\x12.\n\x06header\x18\x03 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x31\n\x07\x66\x61ilure\x18\x04 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x1aP\n\x0c\x44\x65tailsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12/\n\x05value\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads:\x02\x38\x01"\xac\x07\n/ContinueAsNewWorkflowExecutionCommandAttributes\x12;\n\rworkflow_type\x18\x01 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x02 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x03 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x37\n\x14workflow_run_timeout\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16\x62\x61\x63koff_start_interval\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x07 \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12@\n\tinitiator\x18\x08 \x01(\x0e\x32-.temporal.api.enums.v1.ContinueAsNewInitiator\x12\x31\n\x07\x66\x61ilure\x18\t \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12@\n\x16last_completion_result\x18\n \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x15\n\rcron_schedule\x18\x0b \x01(\t\x12.\n\x06header\x18\x0c \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12*\n\x04memo\x18\r \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x0e \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x1c\n\x10inherit_build_id\x18\x0f \x01(\x08\x42\x02\x18\x01\x12[\n\x1binitial_versioning_behavior\x18\x10 \x01(\x0e\x32\x36.temporal.api.enums.v1.ContinueAsNewVersioningBehavior"\xa1\x07\n,StartChildWorkflowExecutionCommandAttributes\x12\x15\n\tnamespace\x18\x01 \x01(\tB\x02\x18\x01\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x04 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12=\n\x1aworkflow_execution_timeout\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x45\n\x13parent_close_policy\x18\t \x01(\x0e\x32(.temporal.api.enums.v1.ParentClosePolicy\x12\x0f\n\x07\x63ontrol\x18\n \x01(\t\x12N\n\x18workflow_id_reuse_policy\x18\x0b \x01(\x0e\x32,.temporal.api.enums.v1.WorkflowIdReusePolicy\x12\x39\n\x0cretry_policy\x18\x0c \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x15\n\rcron_schedule\x18\r \x01(\t\x12.\n\x06header\x18\x0e \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12*\n\x04memo\x18\x0f \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x10 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x1c\n\x10inherit_build_id\x18\x11 \x01(\x08\x42\x02\x18\x01\x12\x32\n\x08priority\x18\x12 \x01(\x0b\x32 .temporal.api.common.v1.Priority"6\n ProtocolMessageCommandAttributes\x12\x12\n\nmessage_id\x18\x01 \x01(\t"\xe3\x03\n\'ScheduleNexusOperationCommandAttributes\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\x11\n\toperation\x18\x03 \x01(\t\x12.\n\x05input\x18\x04 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12<\n\x19schedule_to_close_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12g\n\x0cnexus_header\x18\x06 \x03(\x0b\x32Q.temporal.api.command.v1.ScheduleNexusOperationCommandAttributes.NexusHeaderEntry\x12<\n\x19schedule_to_start_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x1a\x32\n\x10NexusHeaderEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"J\n,RequestCancelNexusOperationCommandAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03"\xc2\x11\n\x07\x43ommand\x12\x38\n\x0c\x63ommand_type\x18\x01 \x01(\x0e\x32".temporal.api.enums.v1.CommandType\x12\x39\n\ruser_metadata\x18\xad\x02 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12s\n)schedule_activity_task_command_attributes\x18\x02 \x01(\x0b\x32>.temporal.api.command.v1.ScheduleActivityTaskCommandAttributesH\x00\x12^\n\x1estart_timer_command_attributes\x18\x03 \x01(\x0b\x32\x34.temporal.api.command.v1.StartTimerCommandAttributesH\x00\x12}\n.complete_workflow_execution_command_attributes\x18\x04 \x01(\x0b\x32\x43.temporal.api.command.v1.CompleteWorkflowExecutionCommandAttributesH\x00\x12u\n*fail_workflow_execution_command_attributes\x18\x05 \x01(\x0b\x32?.temporal.api.command.v1.FailWorkflowExecutionCommandAttributesH\x00\x12~\n/request_cancel_activity_task_command_attributes\x18\x06 \x01(\x0b\x32\x43.temporal.api.command.v1.RequestCancelActivityTaskCommandAttributesH\x00\x12`\n\x1f\x63\x61ncel_timer_command_attributes\x18\x07 \x01(\x0b\x32\x35.temporal.api.command.v1.CancelTimerCommandAttributesH\x00\x12y\n,cancel_workflow_execution_command_attributes\x18\x08 \x01(\x0b\x32\x41.temporal.api.command.v1.CancelWorkflowExecutionCommandAttributesH\x00\x12\x99\x01\n=request_cancel_external_workflow_execution_command_attributes\x18\t \x01(\x0b\x32P.temporal.api.command.v1.RequestCancelExternalWorkflowExecutionCommandAttributesH\x00\x12\x62\n record_marker_command_attributes\x18\n \x01(\x0b\x32\x36.temporal.api.command.v1.RecordMarkerCommandAttributesH\x00\x12\x89\x01\n5continue_as_new_workflow_execution_command_attributes\x18\x0b \x01(\x0b\x32H.temporal.api.command.v1.ContinueAsNewWorkflowExecutionCommandAttributesH\x00\x12\x82\x01\n1start_child_workflow_execution_command_attributes\x18\x0c \x01(\x0b\x32\x45.temporal.api.command.v1.StartChildWorkflowExecutionCommandAttributesH\x00\x12\x8a\x01\n5signal_external_workflow_execution_command_attributes\x18\r \x01(\x0b\x32I.temporal.api.command.v1.SignalExternalWorkflowExecutionCommandAttributesH\x00\x12\x88\x01\n4upsert_workflow_search_attributes_command_attributes\x18\x0e \x01(\x0b\x32H.temporal.api.command.v1.UpsertWorkflowSearchAttributesCommandAttributesH\x00\x12h\n#protocol_message_command_attributes\x18\x0f \x01(\x0b\x32\x39.temporal.api.command.v1.ProtocolMessageCommandAttributesH\x00\x12{\n-modify_workflow_properties_command_attributes\x18\x11 \x01(\x0b\x32\x42.temporal.api.command.v1.ModifyWorkflowPropertiesCommandAttributesH\x00\x12w\n+schedule_nexus_operation_command_attributes\x18\x12 \x01(\x0b\x32@.temporal.api.command.v1.ScheduleNexusOperationCommandAttributesH\x00\x12\x82\x01\n1request_cancel_nexus_operation_command_attributes\x18\x13 \x01(\x0b\x32\x45.temporal.api.command.v1.RequestCancelNexusOperationCommandAttributesH\x00\x42\x0c\n\nattributesB\x8e\x01\n\x1aio.temporal.api.command.v1B\x0cMessageProtoP\x01Z%go.temporal.io/api/command/v1;command\xaa\x02\x19Temporalio.Api.Command.V1\xea\x02\x1cTemporalio::Api::Command::V1b\x06proto3' + b'\n%temporal/api/command/v1/message.proto\x12\x17temporal.api.command.v1\x1a\x1egoogle/protobuf/duration.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a(temporal/api/enums/v1/command_type.proto\x1a$temporal/api/common/v1/message.proto\x1a%temporal/api/failure/v1/message.proto\x1a\'temporal/api/taskqueue/v1/message.proto\x1a&temporal/api/workflow/v1/message.proto\x1a\'temporal/api/sdk/v1/user_metadata.proto\x1a,temporal/api/sdk/v1/event_group_marker.proto"\xb6\x05\n%ScheduleActivityTaskCommandAttributes\x12\x13\n\x0b\x61\x63tivity_id\x18\x01 \x01(\t\x12;\n\ractivity_type\x18\x02 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12\x38\n\ntask_queue\x18\x04 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12.\n\x06header\x18\x05 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12/\n\x05input\x18\x06 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12<\n\x19schedule_to_close_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x0b \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x1f\n\x17request_eager_execution\x18\x0c \x01(\x08\x12\x1d\n\x15use_workflow_build_id\x18\r \x01(\x08\x12\x32\n\x08priority\x18\x0e \x01(\x0b\x32 .temporal.api.common.v1.PriorityJ\x04\x08\x03\x10\x04"H\n*RequestCancelActivityTaskCommandAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03"i\n\x1bStartTimerCommandAttributes\x12\x10\n\x08timer_id\x18\x01 \x01(\t\x12\x38\n\x15start_to_fire_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration"^\n*CompleteWorkflowExecutionCommandAttributes\x12\x30\n\x06result\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads"[\n&FailWorkflowExecutionCommandAttributes\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure"0\n\x1c\x43\x61ncelTimerCommandAttributes\x12\x10\n\x08timer_id\x18\x01 \x01(\t"]\n(CancelWorkflowExecutionCommandAttributes\x12\x31\n\x07\x64\x65tails\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads"\xb7\x01\n7RequestCancelExternalWorkflowExecutionCommandAttributes\x12\x15\n\tnamespace\x18\x01 \x01(\tB\x02\x18\x01\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x13\n\x07\x63ontrol\x18\x04 \x01(\tB\x02\x18\x01\x12\x1b\n\x13\x63hild_workflow_only\x18\x05 \x01(\x08\x12\x0e\n\x06reason\x18\x06 \x01(\t"\xaf\x02\n0SignalExternalWorkflowExecutionCommandAttributes\x12\x15\n\tnamespace\x18\x01 \x01(\tB\x02\x18\x01\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x13\n\x0bsignal_name\x18\x03 \x01(\t\x12/\n\x05input\x18\x04 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x13\n\x07\x63ontrol\x18\x05 \x01(\tB\x02\x18\x01\x12\x1b\n\x13\x63hild_workflow_only\x18\x06 \x01(\x08\x12.\n\x06header\x18\x07 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header"v\n/UpsertWorkflowSearchAttributesCommandAttributes\x12\x43\n\x11search_attributes\x18\x01 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes"`\n)ModifyWorkflowPropertiesCommandAttributes\x12\x33\n\rupserted_memo\x18\x01 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo"\xbf\x02\n\x1dRecordMarkerCommandAttributes\x12\x13\n\x0bmarker_name\x18\x01 \x01(\t\x12T\n\x07\x64\x65tails\x18\x02 \x03(\x0b\x32\x43.temporal.api.command.v1.RecordMarkerCommandAttributes.DetailsEntry\x12.\n\x06header\x18\x03 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x31\n\x07\x66\x61ilure\x18\x04 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x1aP\n\x0c\x44\x65tailsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12/\n\x05value\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads:\x02\x38\x01"\xac\x07\n/ContinueAsNewWorkflowExecutionCommandAttributes\x12;\n\rworkflow_type\x18\x01 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x02 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x03 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x37\n\x14workflow_run_timeout\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16\x62\x61\x63koff_start_interval\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x07 \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12@\n\tinitiator\x18\x08 \x01(\x0e\x32-.temporal.api.enums.v1.ContinueAsNewInitiator\x12\x31\n\x07\x66\x61ilure\x18\t \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12@\n\x16last_completion_result\x18\n \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x15\n\rcron_schedule\x18\x0b \x01(\t\x12.\n\x06header\x18\x0c \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12*\n\x04memo\x18\r \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x0e \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x1c\n\x10inherit_build_id\x18\x0f \x01(\x08\x42\x02\x18\x01\x12[\n\x1binitial_versioning_behavior\x18\x10 \x01(\x0e\x32\x36.temporal.api.enums.v1.ContinueAsNewVersioningBehavior"\xec\x07\n,StartChildWorkflowExecutionCommandAttributes\x12\x15\n\tnamespace\x18\x01 \x01(\tB\x02\x18\x01\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x04 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12=\n\x1aworkflow_execution_timeout\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x45\n\x13parent_close_policy\x18\t \x01(\x0e\x32(.temporal.api.enums.v1.ParentClosePolicy\x12\x0f\n\x07\x63ontrol\x18\n \x01(\t\x12N\n\x18workflow_id_reuse_policy\x18\x0b \x01(\x0e\x32,.temporal.api.enums.v1.WorkflowIdReusePolicy\x12\x39\n\x0cretry_policy\x18\x0c \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x15\n\rcron_schedule\x18\r \x01(\t\x12.\n\x06header\x18\x0e \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12*\n\x04memo\x18\x0f \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x10 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x1c\n\x10inherit_build_id\x18\x11 \x01(\x08\x42\x02\x18\x01\x12\x32\n\x08priority\x18\x12 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12I\n\x13versioning_override\x18\x13 \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride"6\n ProtocolMessageCommandAttributes\x12\x12\n\nmessage_id\x18\x01 \x01(\t"\xe3\x03\n\'ScheduleNexusOperationCommandAttributes\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\x11\n\toperation\x18\x03 \x01(\t\x12.\n\x05input\x18\x04 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12<\n\x19schedule_to_close_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12g\n\x0cnexus_header\x18\x06 \x03(\x0b\x32Q.temporal.api.command.v1.ScheduleNexusOperationCommandAttributes.NexusHeaderEntry\x12<\n\x19schedule_to_start_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x1a\x32\n\x10NexusHeaderEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"J\n,RequestCancelNexusOperationCommandAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03"\x87\x12\n\x07\x43ommand\x12\x38\n\x0c\x63ommand_type\x18\x01 \x01(\x0e\x32".temporal.api.enums.v1.CommandType\x12\x39\n\ruser_metadata\x18\xad\x02 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12\x43\n\x13\x65vent_group_markers\x18\xae\x02 \x03(\x0b\x32%.temporal.api.sdk.v1.EventGroupMarker\x12s\n)schedule_activity_task_command_attributes\x18\x02 \x01(\x0b\x32>.temporal.api.command.v1.ScheduleActivityTaskCommandAttributesH\x00\x12^\n\x1estart_timer_command_attributes\x18\x03 \x01(\x0b\x32\x34.temporal.api.command.v1.StartTimerCommandAttributesH\x00\x12}\n.complete_workflow_execution_command_attributes\x18\x04 \x01(\x0b\x32\x43.temporal.api.command.v1.CompleteWorkflowExecutionCommandAttributesH\x00\x12u\n*fail_workflow_execution_command_attributes\x18\x05 \x01(\x0b\x32?.temporal.api.command.v1.FailWorkflowExecutionCommandAttributesH\x00\x12~\n/request_cancel_activity_task_command_attributes\x18\x06 \x01(\x0b\x32\x43.temporal.api.command.v1.RequestCancelActivityTaskCommandAttributesH\x00\x12`\n\x1f\x63\x61ncel_timer_command_attributes\x18\x07 \x01(\x0b\x32\x35.temporal.api.command.v1.CancelTimerCommandAttributesH\x00\x12y\n,cancel_workflow_execution_command_attributes\x18\x08 \x01(\x0b\x32\x41.temporal.api.command.v1.CancelWorkflowExecutionCommandAttributesH\x00\x12\x99\x01\n=request_cancel_external_workflow_execution_command_attributes\x18\t \x01(\x0b\x32P.temporal.api.command.v1.RequestCancelExternalWorkflowExecutionCommandAttributesH\x00\x12\x62\n record_marker_command_attributes\x18\n \x01(\x0b\x32\x36.temporal.api.command.v1.RecordMarkerCommandAttributesH\x00\x12\x89\x01\n5continue_as_new_workflow_execution_command_attributes\x18\x0b \x01(\x0b\x32H.temporal.api.command.v1.ContinueAsNewWorkflowExecutionCommandAttributesH\x00\x12\x82\x01\n1start_child_workflow_execution_command_attributes\x18\x0c \x01(\x0b\x32\x45.temporal.api.command.v1.StartChildWorkflowExecutionCommandAttributesH\x00\x12\x8a\x01\n5signal_external_workflow_execution_command_attributes\x18\r \x01(\x0b\x32I.temporal.api.command.v1.SignalExternalWorkflowExecutionCommandAttributesH\x00\x12\x88\x01\n4upsert_workflow_search_attributes_command_attributes\x18\x0e \x01(\x0b\x32H.temporal.api.command.v1.UpsertWorkflowSearchAttributesCommandAttributesH\x00\x12h\n#protocol_message_command_attributes\x18\x0f \x01(\x0b\x32\x39.temporal.api.command.v1.ProtocolMessageCommandAttributesH\x00\x12{\n-modify_workflow_properties_command_attributes\x18\x11 \x01(\x0b\x32\x42.temporal.api.command.v1.ModifyWorkflowPropertiesCommandAttributesH\x00\x12w\n+schedule_nexus_operation_command_attributes\x18\x12 \x01(\x0b\x32@.temporal.api.command.v1.ScheduleNexusOperationCommandAttributesH\x00\x12\x82\x01\n1request_cancel_nexus_operation_command_attributes\x18\x13 \x01(\x0b\x32\x45.temporal.api.command.v1.RequestCancelNexusOperationCommandAttributesH\x00\x42\x0c\n\nattributesB\x8e\x01\n\x1aio.temporal.api.command.v1B\x0cMessageProtoP\x01Z%go.temporal.io/api/command/v1;command\xaa\x02\x19Temporalio.Api.Command.V1\xea\x02\x1cTemporalio::Api::Command::V1b\x06proto3' ) @@ -377,44 +383,44 @@ _SCHEDULENEXUSOPERATIONCOMMANDATTRIBUTES_NEXUSHEADERENTRY._serialized_options = ( b"8\001" ) - _SCHEDULEACTIVITYTASKCOMMANDATTRIBUTES._serialized_start = 338 - _SCHEDULEACTIVITYTASKCOMMANDATTRIBUTES._serialized_end = 1032 - _REQUESTCANCELACTIVITYTASKCOMMANDATTRIBUTES._serialized_start = 1034 - _REQUESTCANCELACTIVITYTASKCOMMANDATTRIBUTES._serialized_end = 1106 - _STARTTIMERCOMMANDATTRIBUTES._serialized_start = 1108 - _STARTTIMERCOMMANDATTRIBUTES._serialized_end = 1213 - _COMPLETEWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 1215 - _COMPLETEWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 1309 - _FAILWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 1311 - _FAILWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 1402 - _CANCELTIMERCOMMANDATTRIBUTES._serialized_start = 1404 - _CANCELTIMERCOMMANDATTRIBUTES._serialized_end = 1452 - _CANCELWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 1454 - _CANCELWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 1547 - _REQUESTCANCELEXTERNALWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 1550 - _REQUESTCANCELEXTERNALWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 1733 - _SIGNALEXTERNALWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 1736 - _SIGNALEXTERNALWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 2039 - _UPSERTWORKFLOWSEARCHATTRIBUTESCOMMANDATTRIBUTES._serialized_start = 2041 - _UPSERTWORKFLOWSEARCHATTRIBUTESCOMMANDATTRIBUTES._serialized_end = 2159 - _MODIFYWORKFLOWPROPERTIESCOMMANDATTRIBUTES._serialized_start = 2161 - _MODIFYWORKFLOWPROPERTIESCOMMANDATTRIBUTES._serialized_end = 2257 - _RECORDMARKERCOMMANDATTRIBUTES._serialized_start = 2260 - _RECORDMARKERCOMMANDATTRIBUTES._serialized_end = 2579 - _RECORDMARKERCOMMANDATTRIBUTES_DETAILSENTRY._serialized_start = 2499 - _RECORDMARKERCOMMANDATTRIBUTES_DETAILSENTRY._serialized_end = 2579 - _CONTINUEASNEWWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 2582 - _CONTINUEASNEWWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 3522 - _STARTCHILDWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 3525 - _STARTCHILDWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 4454 - _PROTOCOLMESSAGECOMMANDATTRIBUTES._serialized_start = 4456 - _PROTOCOLMESSAGECOMMANDATTRIBUTES._serialized_end = 4510 - _SCHEDULENEXUSOPERATIONCOMMANDATTRIBUTES._serialized_start = 4513 - _SCHEDULENEXUSOPERATIONCOMMANDATTRIBUTES._serialized_end = 4996 - _SCHEDULENEXUSOPERATIONCOMMANDATTRIBUTES_NEXUSHEADERENTRY._serialized_start = 4946 - _SCHEDULENEXUSOPERATIONCOMMANDATTRIBUTES_NEXUSHEADERENTRY._serialized_end = 4996 - _REQUESTCANCELNEXUSOPERATIONCOMMANDATTRIBUTES._serialized_start = 4998 - _REQUESTCANCELNEXUSOPERATIONCOMMANDATTRIBUTES._serialized_end = 5072 - _COMMAND._serialized_start = 5075 - _COMMAND._serialized_end = 7317 + _SCHEDULEACTIVITYTASKCOMMANDATTRIBUTES._serialized_start = 424 + _SCHEDULEACTIVITYTASKCOMMANDATTRIBUTES._serialized_end = 1118 + _REQUESTCANCELACTIVITYTASKCOMMANDATTRIBUTES._serialized_start = 1120 + _REQUESTCANCELACTIVITYTASKCOMMANDATTRIBUTES._serialized_end = 1192 + _STARTTIMERCOMMANDATTRIBUTES._serialized_start = 1194 + _STARTTIMERCOMMANDATTRIBUTES._serialized_end = 1299 + _COMPLETEWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 1301 + _COMPLETEWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 1395 + _FAILWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 1397 + _FAILWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 1488 + _CANCELTIMERCOMMANDATTRIBUTES._serialized_start = 1490 + _CANCELTIMERCOMMANDATTRIBUTES._serialized_end = 1538 + _CANCELWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 1540 + _CANCELWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 1633 + _REQUESTCANCELEXTERNALWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 1636 + _REQUESTCANCELEXTERNALWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 1819 + _SIGNALEXTERNALWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 1822 + _SIGNALEXTERNALWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 2125 + _UPSERTWORKFLOWSEARCHATTRIBUTESCOMMANDATTRIBUTES._serialized_start = 2127 + _UPSERTWORKFLOWSEARCHATTRIBUTESCOMMANDATTRIBUTES._serialized_end = 2245 + _MODIFYWORKFLOWPROPERTIESCOMMANDATTRIBUTES._serialized_start = 2247 + _MODIFYWORKFLOWPROPERTIESCOMMANDATTRIBUTES._serialized_end = 2343 + _RECORDMARKERCOMMANDATTRIBUTES._serialized_start = 2346 + _RECORDMARKERCOMMANDATTRIBUTES._serialized_end = 2665 + _RECORDMARKERCOMMANDATTRIBUTES_DETAILSENTRY._serialized_start = 2585 + _RECORDMARKERCOMMANDATTRIBUTES_DETAILSENTRY._serialized_end = 2665 + _CONTINUEASNEWWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 2668 + _CONTINUEASNEWWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 3608 + _STARTCHILDWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_start = 3611 + _STARTCHILDWORKFLOWEXECUTIONCOMMANDATTRIBUTES._serialized_end = 4615 + _PROTOCOLMESSAGECOMMANDATTRIBUTES._serialized_start = 4617 + _PROTOCOLMESSAGECOMMANDATTRIBUTES._serialized_end = 4671 + _SCHEDULENEXUSOPERATIONCOMMANDATTRIBUTES._serialized_start = 4674 + _SCHEDULENEXUSOPERATIONCOMMANDATTRIBUTES._serialized_end = 5157 + _SCHEDULENEXUSOPERATIONCOMMANDATTRIBUTES_NEXUSHEADERENTRY._serialized_start = 5107 + _SCHEDULENEXUSOPERATIONCOMMANDATTRIBUTES_NEXUSHEADERENTRY._serialized_end = 5157 + _REQUESTCANCELNEXUSOPERATIONCOMMANDATTRIBUTES._serialized_start = 5159 + _REQUESTCANCELNEXUSOPERATIONCOMMANDATTRIBUTES._serialized_end = 5233 + _COMMAND._serialized_start = 5236 + _COMMAND._serialized_end = 7547 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/command/v1/message_pb2.pyi b/temporalio/api/command/v1/message_pb2.pyi index 1b6bb3404..18d0abc8b 100644 --- a/temporalio/api/command/v1/message_pb2.pyi +++ b/temporalio/api/command/v1/message_pb2.pyi @@ -16,8 +16,10 @@ import temporalio.api.common.v1.message_pb2 import temporalio.api.enums.v1.command_type_pb2 import temporalio.api.enums.v1.workflow_pb2 import temporalio.api.failure.v1.message_pb2 +import temporalio.api.sdk.v1.event_group_marker_pb2 import temporalio.api.sdk.v1.user_metadata_pb2 import temporalio.api.taskqueue.v1.message_pb2 +import temporalio.api.workflow.v1.message_pb2 if sys.version_info >= (3, 8): import typing as typing_extensions @@ -758,6 +760,7 @@ class StartChildWorkflowExecutionCommandAttributes(google.protobuf.message.Messa SEARCH_ATTRIBUTES_FIELD_NUMBER: builtins.int INHERIT_BUILD_ID_FIELD_NUMBER: builtins.int PRIORITY_FIELD_NUMBER: builtins.int + VERSIONING_OVERRIDE_FIELD_NUMBER: builtins.int namespace: builtins.str """Deprecated. Cross-namespace operations are disabled by default as of server 1.30.1.""" workflow_id: builtins.str @@ -807,6 +810,13 @@ class StartChildWorkflowExecutionCommandAttributes(google.protobuf.message.Messa """Priority metadata. If this message is not present, or any fields are not present, they inherit the values from the workflow. """ + @property + def versioning_override( + self, + ) -> temporalio.api.workflow.v1.message_pb2.VersioningOverride: + """Versioning override for the child workflow. If present, this explicit override takes + precedence over versioning behavior inherited from the parent workflow. + """ def __init__( self, *, @@ -829,6 +839,8 @@ class StartChildWorkflowExecutionCommandAttributes(google.protobuf.message.Messa | None = ..., inherit_build_id: builtins.bool = ..., priority: temporalio.api.common.v1.message_pb2.Priority | None = ..., + versioning_override: temporalio.api.workflow.v1.message_pb2.VersioningOverride + | None = ..., ) -> None: ... def HasField( self, @@ -847,6 +859,8 @@ class StartChildWorkflowExecutionCommandAttributes(google.protobuf.message.Messa b"search_attributes", "task_queue", b"task_queue", + "versioning_override", + b"versioning_override", "workflow_execution_timeout", b"workflow_execution_timeout", "workflow_run_timeout", @@ -884,6 +898,8 @@ class StartChildWorkflowExecutionCommandAttributes(google.protobuf.message.Messa b"search_attributes", "task_queue", b"task_queue", + "versioning_override", + b"versioning_override", "workflow_execution_timeout", b"workflow_execution_timeout", "workflow_id", @@ -1083,6 +1099,7 @@ class Command(google.protobuf.message.Message): COMMAND_TYPE_FIELD_NUMBER: builtins.int USER_METADATA_FIELD_NUMBER: builtins.int + EVENT_GROUP_MARKERS_FIELD_NUMBER: builtins.int SCHEDULE_ACTIVITY_TASK_COMMAND_ATTRIBUTES_FIELD_NUMBER: builtins.int START_TIMER_COMMAND_ATTRIBUTES_FIELD_NUMBER: builtins.int COMPLETE_WORKFLOW_EXECUTION_COMMAND_ATTRIBUTES_FIELD_NUMBER: builtins.int @@ -1117,6 +1134,13 @@ class Command(google.protobuf.message.Message): started where the summary is used to identify the timer. """ @property + def event_group_markers( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ + temporalio.api.sdk.v1.event_group_marker_pb2.EventGroupMarker + ]: + """Event Group Markers attached to the command by the workflow author.""" + @property def schedule_activity_task_command_attributes( self, ) -> global___ScheduleActivityTaskCommandAttributes: ... @@ -1191,6 +1215,10 @@ class Command(google.protobuf.message.Message): command_type: temporalio.api.enums.v1.command_type_pb2.CommandType.ValueType = ..., user_metadata: temporalio.api.sdk.v1.user_metadata_pb2.UserMetadata | None = ..., + event_group_markers: collections.abc.Iterable[ + temporalio.api.sdk.v1.event_group_marker_pb2.EventGroupMarker + ] + | None = ..., schedule_activity_task_command_attributes: global___ScheduleActivityTaskCommandAttributes | None = ..., start_timer_command_attributes: global___StartTimerCommandAttributes @@ -1284,6 +1312,8 @@ class Command(google.protobuf.message.Message): b"complete_workflow_execution_command_attributes", "continue_as_new_workflow_execution_command_attributes", b"continue_as_new_workflow_execution_command_attributes", + "event_group_markers", + b"event_group_markers", "fail_workflow_execution_command_attributes", b"fail_workflow_execution_command_attributes", "modify_workflow_properties_command_attributes", diff --git a/temporalio/api/common/v1/__init__.py b/temporalio/api/common/v1/__init__.py index 112068861..e613d71f8 100644 --- a/temporalio/api/common/v1/__init__.py +++ b/temporalio/api/common/v1/__init__.py @@ -3,6 +3,8 @@ ActivityType, Callback, DataBlob, + Execution, + FastForwardConfig, Header, Link, Memo, @@ -15,6 +17,10 @@ ResetOptions, RetryPolicy, SearchAttributes, + TimeSkippingConfig, + TimeSkippingFastForwardInfo, + TimeSkippingInfo, + TimeSkippingStatePropagation, WorkerSelector, WorkerVersionCapabilities, WorkerVersionStamp, @@ -26,6 +32,8 @@ "ActivityType", "Callback", "DataBlob", + "Execution", + "FastForwardConfig", "GrpcStatus", "Header", "Link", @@ -39,6 +47,10 @@ "ResetOptions", "RetryPolicy", "SearchAttributes", + "TimeSkippingConfig", + "TimeSkippingFastForwardInfo", + "TimeSkippingInfo", + "TimeSkippingStatePropagation", "WorkerSelector", "WorkerVersionCapabilities", "WorkerVersionStamp", diff --git a/temporalio/api/common/v1/message_pb2.py b/temporalio/api/common/v1/message_pb2.py index aed909611..538be6a23 100644 --- a/temporalio/api/common/v1/message_pb2.py +++ b/temporalio/api/common/v1/message_pb2.py @@ -16,6 +16,7 @@ from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 from temporalio.api.enums.v1 import ( common_pb2 as temporal_dot_api_dot_enums_dot_v1_dot_common__pb2, @@ -28,7 +29,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n$temporal/api/common/v1/message.proto\x12\x16temporal.api.common.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a"temporal/api/enums/v1/common.proto\x1a&temporal/api/enums/v1/event_type.proto\x1a!temporal/api/enums/v1/reset.proto"T\n\x08\x44\x61taBlob\x12:\n\rencoding_type\x18\x01 \x01(\x0e\x32#.temporal.api.enums.v1.EncodingType\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c"=\n\x08Payloads\x12\x31\n\x08payloads\x18\x01 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload"\x8a\x02\n\x07Payload\x12?\n\x08metadata\x18\x01 \x03(\x0b\x32-.temporal.api.common.v1.Payload.MetadataEntry\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12Q\n\x11\x65xternal_payloads\x18\x03 \x03(\x0b\x32\x36.temporal.api.common.v1.Payload.ExternalPayloadDetails\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\x1a,\n\x16\x45xternalPayloadDetails\x12\x12\n\nsize_bytes\x18\x01 \x01(\x03"\xbe\x01\n\x10SearchAttributes\x12S\n\x0eindexed_fields\x18\x01 \x03(\x0b\x32;.temporal.api.common.v1.SearchAttributes.IndexedFieldsEntry\x1aU\n\x12IndexedFieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\x90\x01\n\x04Memo\x12\x38\n\x06\x66ields\x18\x01 \x03(\x0b\x32(.temporal.api.common.v1.Memo.FieldsEntry\x1aN\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\x94\x01\n\x06Header\x12:\n\x06\x66ields\x18\x01 \x03(\x0b\x32*.temporal.api.common.v1.Header.FieldsEntry\x1aN\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"8\n\x11WorkflowExecution\x12\x13\n\x0bworkflow_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t"\x1c\n\x0cWorkflowType\x12\x0c\n\x04name\x18\x01 \x01(\t"\x1c\n\x0c\x41\x63tivityType\x12\x0c\n\x04name\x18\x01 \x01(\t"\xd1\x01\n\x0bRetryPolicy\x12\x33\n\x10initial_interval\x18\x01 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x1b\n\x13\x62\x61\x63koff_coefficient\x18\x02 \x01(\x01\x12\x33\n\x10maximum_interval\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x18\n\x10maximum_attempts\x18\x04 \x01(\x05\x12!\n\x19non_retryable_error_types\x18\x05 \x03(\t"F\n\x10MeteringMetadata\x12\x32\n*nonfirst_local_activity_execution_attempts\x18\r \x01(\r">\n\x12WorkerVersionStamp\x12\x10\n\x08\x62uild_id\x18\x01 \x01(\t\x12\x16\n\x0euse_versioning\x18\x03 \x01(\x08"e\n\x19WorkerVersionCapabilities\x12\x10\n\x08\x62uild_id\x18\x01 \x01(\t\x12\x16\n\x0euse_versioning\x18\x02 \x01(\x08\x12\x1e\n\x16\x64\x65ployment_series_name\x18\x04 \x01(\t"\xed\x02\n\x0cResetOptions\x12\x35\n\x13\x66irst_workflow_task\x18\x01 \x01(\x0b\x32\x16.google.protobuf.EmptyH\x00\x12\x34\n\x12last_workflow_task\x18\x02 \x01(\x0b\x32\x16.google.protobuf.EmptyH\x00\x12\x1a\n\x10workflow_task_id\x18\x03 \x01(\x03H\x00\x12\x12\n\x08\x62uild_id\x18\x04 \x01(\tH\x00\x12G\n\x12reset_reapply_type\x18\n \x01(\x0e\x32\'.temporal.api.enums.v1.ResetReapplyTypeB\x02\x18\x01\x12\x18\n\x10\x63urrent_run_only\x18\x0b \x01(\x08\x12S\n\x1breset_reapply_exclude_types\x18\x0c \x03(\x0e\x32..temporal.api.enums.v1.ResetReapplyExcludeTypeB\x08\n\x06target"\xe4\x02\n\x08\x43\x61llback\x12\x37\n\x05nexus\x18\x02 \x01(\x0b\x32&.temporal.api.common.v1.Callback.NexusH\x00\x12=\n\x08internal\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.Callback.InternalH\x00\x12+\n\x05links\x18\x64 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x1a\x87\x01\n\x05Nexus\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x42\n\x06header\x18\x02 \x03(\x0b\x32\x32.temporal.api.common.v1.Callback.Nexus.HeaderEntry\x1a-\n\x0bHeaderEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x18\n\x08Internal\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x42\t\n\x07variantJ\x04\x08\x01\x10\x02"\x8a\x08\n\x04Link\x12\x44\n\x0eworkflow_event\x18\x01 \x01(\x0b\x32*.temporal.api.common.v1.Link.WorkflowEventH\x00\x12:\n\tbatch_job\x18\x02 \x01(\x0b\x32%.temporal.api.common.v1.Link.BatchJobH\x00\x12\x39\n\x08\x61\x63tivity\x18\x03 \x01(\x0b\x32%.temporal.api.common.v1.Link.ActivityH\x00\x12\x46\n\x0fnexus_operation\x18\x04 \x01(\x0b\x32+.temporal.api.common.v1.Link.NexusOperationH\x00\x12\x39\n\x08workflow\x18\x05 \x01(\x0b\x32%.temporal.api.common.v1.Link.WorkflowH\x00\x1a\xb7\x03\n\rWorkflowEvent\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12N\n\tevent_ref\x18\x64 \x01(\x0b\x32\x39.temporal.api.common.v1.Link.WorkflowEvent.EventReferenceH\x00\x12W\n\x0erequest_id_ref\x18\x65 \x01(\x0b\x32=.temporal.api.common.v1.Link.WorkflowEvent.RequestIdReferenceH\x00\x1aX\n\x0e\x45ventReference\x12\x10\n\x08\x65vent_id\x18\x01 \x01(\x03\x12\x34\n\nevent_type\x18\x02 \x01(\x0e\x32 .temporal.api.enums.v1.EventType\x1a^\n\x12RequestIdReference\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x34\n\nevent_type\x18\x02 \x01(\x0e\x32 .temporal.api.enums.v1.EventTypeB\x0b\n\treference\x1a\x1a\n\x08\x42\x61tchJob\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x1a\x42\n\x08\x41\x63tivity\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x1aI\n\x0eNexusOperation\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0coperation_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x1aR\n\x08Workflow\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x0e\n\x06reason\x18\x04 \x01(\tB\t\n\x07variant"\'\n\tPrincipal\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t"O\n\x08Priority\x12\x14\n\x0cpriority_key\x18\x01 \x01(\x05\x12\x14\n\x0c\x66\x61irness_key\x18\x02 \x01(\t\x12\x17\n\x0f\x66\x61irness_weight\x18\x03 \x01(\x02";\n\x0eWorkerSelector\x12\x1d\n\x13worker_instance_key\x18\x01 \x01(\tH\x00\x42\n\n\x08selector"i\n\x11OnConflictOptions\x12\x19\n\x11\x61ttach_request_id\x18\x01 \x01(\x08\x12#\n\x1b\x61ttach_completion_callbacks\x18\x02 \x01(\x08\x12\x14\n\x0c\x61ttach_links\x18\x03 \x01(\x08\x42\x89\x01\n\x19io.temporal.api.common.v1B\x0cMessageProtoP\x01Z#go.temporal.io/api/common/v1;common\xaa\x02\x18Temporalio.Api.Common.V1\xea\x02\x1bTemporalio::Api::Common::V1b\x06proto3' + b'\n$temporal/api/common/v1/message.proto\x12\x16temporal.api.common.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a"temporal/api/enums/v1/common.proto\x1a&temporal/api/enums/v1/event_type.proto\x1a!temporal/api/enums/v1/reset.proto"T\n\x08\x44\x61taBlob\x12:\n\rencoding_type\x18\x01 \x01(\x0e\x32#.temporal.api.enums.v1.EncodingType\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c"=\n\x08Payloads\x12\x31\n\x08payloads\x18\x01 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload"\x8a\x02\n\x07Payload\x12?\n\x08metadata\x18\x01 \x03(\x0b\x32-.temporal.api.common.v1.Payload.MetadataEntry\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12Q\n\x11\x65xternal_payloads\x18\x03 \x03(\x0b\x32\x36.temporal.api.common.v1.Payload.ExternalPayloadDetails\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\x1a,\n\x16\x45xternalPayloadDetails\x12\x12\n\nsize_bytes\x18\x01 \x01(\x03"\xbe\x01\n\x10SearchAttributes\x12S\n\x0eindexed_fields\x18\x01 \x03(\x0b\x32;.temporal.api.common.v1.SearchAttributes.IndexedFieldsEntry\x1aU\n\x12IndexedFieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\x90\x01\n\x04Memo\x12\x38\n\x06\x66ields\x18\x01 \x03(\x0b\x32(.temporal.api.common.v1.Memo.FieldsEntry\x1aN\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\x94\x01\n\x06Header\x12:\n\x06\x66ields\x18\x01 \x03(\x0b\x32*.temporal.api.common.v1.Header.FieldsEntry\x1aN\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"8\n\x11WorkflowExecution\x12\x13\n\x0bworkflow_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t"d\n\tExecution\x12\x32\n\x04type\x18\x01 \x01(\x0e\x32$.temporal.api.enums.v1.ExecutionType\x12\x13\n\x0b\x62usiness_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t"\x1c\n\x0cWorkflowType\x12\x0c\n\x04name\x18\x01 \x01(\t"\x1c\n\x0c\x41\x63tivityType\x12\x0c\n\x04name\x18\x01 \x01(\t"\xd1\x01\n\x0bRetryPolicy\x12\x33\n\x10initial_interval\x18\x01 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x1b\n\x13\x62\x61\x63koff_coefficient\x18\x02 \x01(\x01\x12\x33\n\x10maximum_interval\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x18\n\x10maximum_attempts\x18\x04 \x01(\x05\x12!\n\x19non_retryable_error_types\x18\x05 \x03(\t"F\n\x10MeteringMetadata\x12\x32\n*nonfirst_local_activity_execution_attempts\x18\r \x01(\r">\n\x12WorkerVersionStamp\x12\x10\n\x08\x62uild_id\x18\x01 \x01(\t\x12\x16\n\x0euse_versioning\x18\x03 \x01(\x08"e\n\x19WorkerVersionCapabilities\x12\x10\n\x08\x62uild_id\x18\x01 \x01(\t\x12\x16\n\x0euse_versioning\x18\x02 \x01(\x08\x12\x1e\n\x16\x64\x65ployment_series_name\x18\x04 \x01(\t"\xed\x02\n\x0cResetOptions\x12\x35\n\x13\x66irst_workflow_task\x18\x01 \x01(\x0b\x32\x16.google.protobuf.EmptyH\x00\x12\x34\n\x12last_workflow_task\x18\x02 \x01(\x0b\x32\x16.google.protobuf.EmptyH\x00\x12\x1a\n\x10workflow_task_id\x18\x03 \x01(\x03H\x00\x12\x12\n\x08\x62uild_id\x18\x04 \x01(\tH\x00\x12G\n\x12reset_reapply_type\x18\n \x01(\x0e\x32\'.temporal.api.enums.v1.ResetReapplyTypeB\x02\x18\x01\x12\x18\n\x10\x63urrent_run_only\x18\x0b \x01(\x08\x12S\n\x1breset_reapply_exclude_types\x18\x0c \x03(\x0e\x32..temporal.api.enums.v1.ResetReapplyExcludeTypeB\x08\n\x06target"\xe4\x02\n\x08\x43\x61llback\x12\x37\n\x05nexus\x18\x02 \x01(\x0b\x32&.temporal.api.common.v1.Callback.NexusH\x00\x12=\n\x08internal\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.Callback.InternalH\x00\x12+\n\x05links\x18\x64 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x1a\x87\x01\n\x05Nexus\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x42\n\x06header\x18\x02 \x03(\x0b\x32\x32.temporal.api.common.v1.Callback.Nexus.HeaderEntry\x1a-\n\x0bHeaderEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x18\n\x08Internal\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x42\t\n\x07variantJ\x04\x08\x01\x10\x02"\x8a\x08\n\x04Link\x12\x44\n\x0eworkflow_event\x18\x01 \x01(\x0b\x32*.temporal.api.common.v1.Link.WorkflowEventH\x00\x12:\n\tbatch_job\x18\x02 \x01(\x0b\x32%.temporal.api.common.v1.Link.BatchJobH\x00\x12\x39\n\x08\x61\x63tivity\x18\x03 \x01(\x0b\x32%.temporal.api.common.v1.Link.ActivityH\x00\x12\x46\n\x0fnexus_operation\x18\x04 \x01(\x0b\x32+.temporal.api.common.v1.Link.NexusOperationH\x00\x12\x39\n\x08workflow\x18\x05 \x01(\x0b\x32%.temporal.api.common.v1.Link.WorkflowH\x00\x1a\xb7\x03\n\rWorkflowEvent\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12N\n\tevent_ref\x18\x64 \x01(\x0b\x32\x39.temporal.api.common.v1.Link.WorkflowEvent.EventReferenceH\x00\x12W\n\x0erequest_id_ref\x18\x65 \x01(\x0b\x32=.temporal.api.common.v1.Link.WorkflowEvent.RequestIdReferenceH\x00\x1aX\n\x0e\x45ventReference\x12\x10\n\x08\x65vent_id\x18\x01 \x01(\x03\x12\x34\n\nevent_type\x18\x02 \x01(\x0e\x32 .temporal.api.enums.v1.EventType\x1a^\n\x12RequestIdReference\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x34\n\nevent_type\x18\x02 \x01(\x0e\x32 .temporal.api.enums.v1.EventTypeB\x0b\n\treference\x1a\x1a\n\x08\x42\x61tchJob\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x1a\x42\n\x08\x41\x63tivity\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x1aI\n\x0eNexusOperation\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0coperation_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x1aR\n\x08Workflow\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x0e\n\x06reason\x18\x04 \x01(\tB\t\n\x07variant"\'\n\tPrincipal\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t"O\n\x08Priority\x12\x14\n\x0cpriority_key\x18\x01 \x01(\x05\x12\x14\n\x0c\x66\x61irness_key\x18\x02 \x01(\t\x12\x17\n\x0f\x66\x61irness_weight\x18\x03 \x01(\x02";\n\x0eWorkerSelector\x12\x1d\n\x13worker_instance_key\x18\x01 \x01(\tH\x00\x42\n\n\x08selector"i\n\x11OnConflictOptions\x12\x19\n\x11\x61ttach_request_id\x18\x01 \x01(\x08\x12#\n\x1b\x61ttach_completion_callbacks\x18\x02 \x01(\x08\x12\x14\n\x0c\x61ttach_links\x18\x03 \x01(\x08"\xaa\x01\n\x12TimeSkippingConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x46\n\x13\x66\x61st_forward_config\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.FastForwardConfig\x12\x1b\n\x13\x64isable_propagation\x18\x03 \x01(\x08\x12\x1e\n\x16max_session_skip_count\x18\x04 \x01(\x05"L\n\x11\x46\x61stForwardConfig\x12\n\n\x02id\x18\x01 \x01(\t\x12+\n\x08\x64uration\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration"\xb5\x01\n\x1cTimeSkippingStatePropagation\x12;\n\x18initial_skipped_duration\x18\x01 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x18\x66\x61st_forward_target_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x1a\n\x12initial_skip_count\x18\x03 \x01(\x05"\xfe\x01\n\x10TimeSkippingInfo\x12\x30\n\x0c\x63urrent_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x44\n\x10\x65\x66\x66\x65\x63tive_config\x18\x02 \x01(\x0b\x32*.temporal.api.common.v1.TimeSkippingConfig\x12N\n\x11\x66\x61st_forward_info\x18\x04 \x01(\x0b\x32\x33.temporal.api.common.v1.TimeSkippingFastForwardInfo\x12"\n\x1a\x63urrent_session_skip_count\x18\x06 \x01(\x05"\xb8\x01\n\x1bTimeSkippingFastForwardInfo\x12\x38\n\x15\x66\x61st_forward_duration\x18\x01 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x17\n\x0f\x66\x61st_forward_id\x18\x02 \x01(\t\x12/\n\x0btarget_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x15\n\rhas_completed\x18\x04 \x01(\x08\x42\x89\x01\n\x19io.temporal.api.common.v1B\x0cMessageProtoP\x01Z#go.temporal.io/api/common/v1;common\xaa\x02\x18Temporalio.Api.Common.V1\xea\x02\x1bTemporalio::Api::Common::V1b\x06proto3' ) @@ -48,6 +49,7 @@ _HEADER = DESCRIPTOR.message_types_by_name["Header"] _HEADER_FIELDSENTRY = _HEADER.nested_types_by_name["FieldsEntry"] _WORKFLOWEXECUTION = DESCRIPTOR.message_types_by_name["WorkflowExecution"] +_EXECUTION = DESCRIPTOR.message_types_by_name["Execution"] _WORKFLOWTYPE = DESCRIPTOR.message_types_by_name["WorkflowType"] _ACTIVITYTYPE = DESCRIPTOR.message_types_by_name["ActivityType"] _RETRYPOLICY = DESCRIPTOR.message_types_by_name["RetryPolicy"] @@ -77,6 +79,15 @@ _PRIORITY = DESCRIPTOR.message_types_by_name["Priority"] _WORKERSELECTOR = DESCRIPTOR.message_types_by_name["WorkerSelector"] _ONCONFLICTOPTIONS = DESCRIPTOR.message_types_by_name["OnConflictOptions"] +_TIMESKIPPINGCONFIG = DESCRIPTOR.message_types_by_name["TimeSkippingConfig"] +_FASTFORWARDCONFIG = DESCRIPTOR.message_types_by_name["FastForwardConfig"] +_TIMESKIPPINGSTATEPROPAGATION = DESCRIPTOR.message_types_by_name[ + "TimeSkippingStatePropagation" +] +_TIMESKIPPINGINFO = DESCRIPTOR.message_types_by_name["TimeSkippingInfo"] +_TIMESKIPPINGFASTFORWARDINFO = DESCRIPTOR.message_types_by_name[ + "TimeSkippingFastForwardInfo" +] DataBlob = _reflection.GeneratedProtocolMessageType( "DataBlob", (_message.Message,), @@ -204,6 +215,17 @@ ) _sym_db.RegisterMessage(WorkflowExecution) +Execution = _reflection.GeneratedProtocolMessageType( + "Execution", + (_message.Message,), + { + "DESCRIPTOR": _EXECUTION, + "__module__": "temporalio.api.common.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.common.v1.Execution) + }, +) +_sym_db.RegisterMessage(Execution) + WorkflowType = _reflection.GeneratedProtocolMessageType( "WorkflowType", (_message.Message,), @@ -447,6 +469,61 @@ ) _sym_db.RegisterMessage(OnConflictOptions) +TimeSkippingConfig = _reflection.GeneratedProtocolMessageType( + "TimeSkippingConfig", + (_message.Message,), + { + "DESCRIPTOR": _TIMESKIPPINGCONFIG, + "__module__": "temporalio.api.common.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.common.v1.TimeSkippingConfig) + }, +) +_sym_db.RegisterMessage(TimeSkippingConfig) + +FastForwardConfig = _reflection.GeneratedProtocolMessageType( + "FastForwardConfig", + (_message.Message,), + { + "DESCRIPTOR": _FASTFORWARDCONFIG, + "__module__": "temporalio.api.common.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.common.v1.FastForwardConfig) + }, +) +_sym_db.RegisterMessage(FastForwardConfig) + +TimeSkippingStatePropagation = _reflection.GeneratedProtocolMessageType( + "TimeSkippingStatePropagation", + (_message.Message,), + { + "DESCRIPTOR": _TIMESKIPPINGSTATEPROPAGATION, + "__module__": "temporalio.api.common.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.common.v1.TimeSkippingStatePropagation) + }, +) +_sym_db.RegisterMessage(TimeSkippingStatePropagation) + +TimeSkippingInfo = _reflection.GeneratedProtocolMessageType( + "TimeSkippingInfo", + (_message.Message,), + { + "DESCRIPTOR": _TIMESKIPPINGINFO, + "__module__": "temporalio.api.common.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.common.v1.TimeSkippingInfo) + }, +) +_sym_db.RegisterMessage(TimeSkippingInfo) + +TimeSkippingFastForwardInfo = _reflection.GeneratedProtocolMessageType( + "TimeSkippingFastForwardInfo", + (_message.Message,), + { + "DESCRIPTOR": _TIMESKIPPINGFASTFORWARDINFO, + "__module__": "temporalio.api.common.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.common.v1.TimeSkippingFastForwardInfo) + }, +) +_sym_db.RegisterMessage(TimeSkippingFastForwardInfo) + if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b"\n\031io.temporal.api.common.v1B\014MessageProtoP\001Z#go.temporal.io/api/common/v1;common\252\002\030Temporalio.Api.Common.V1\352\002\033Temporalio::Api::Common::V1" @@ -462,74 +539,86 @@ _RESETOPTIONS.fields_by_name["reset_reapply_type"]._serialized_options = b"\030\001" _CALLBACK_NEXUS_HEADERENTRY._options = None _CALLBACK_NEXUS_HEADERENTRY._serialized_options = b"8\001" - _DATABLOB._serialized_start = 236 - _DATABLOB._serialized_end = 320 - _PAYLOADS._serialized_start = 322 - _PAYLOADS._serialized_end = 383 - _PAYLOAD._serialized_start = 386 - _PAYLOAD._serialized_end = 652 - _PAYLOAD_METADATAENTRY._serialized_start = 559 - _PAYLOAD_METADATAENTRY._serialized_end = 606 - _PAYLOAD_EXTERNALPAYLOADDETAILS._serialized_start = 608 - _PAYLOAD_EXTERNALPAYLOADDETAILS._serialized_end = 652 - _SEARCHATTRIBUTES._serialized_start = 655 - _SEARCHATTRIBUTES._serialized_end = 845 - _SEARCHATTRIBUTES_INDEXEDFIELDSENTRY._serialized_start = 760 - _SEARCHATTRIBUTES_INDEXEDFIELDSENTRY._serialized_end = 845 - _MEMO._serialized_start = 848 - _MEMO._serialized_end = 992 - _MEMO_FIELDSENTRY._serialized_start = 914 - _MEMO_FIELDSENTRY._serialized_end = 992 - _HEADER._serialized_start = 995 - _HEADER._serialized_end = 1143 - _HEADER_FIELDSENTRY._serialized_start = 914 - _HEADER_FIELDSENTRY._serialized_end = 992 - _WORKFLOWEXECUTION._serialized_start = 1145 - _WORKFLOWEXECUTION._serialized_end = 1201 - _WORKFLOWTYPE._serialized_start = 1203 - _WORKFLOWTYPE._serialized_end = 1231 - _ACTIVITYTYPE._serialized_start = 1233 - _ACTIVITYTYPE._serialized_end = 1261 - _RETRYPOLICY._serialized_start = 1264 - _RETRYPOLICY._serialized_end = 1473 - _METERINGMETADATA._serialized_start = 1475 - _METERINGMETADATA._serialized_end = 1545 - _WORKERVERSIONSTAMP._serialized_start = 1547 - _WORKERVERSIONSTAMP._serialized_end = 1609 - _WORKERVERSIONCAPABILITIES._serialized_start = 1611 - _WORKERVERSIONCAPABILITIES._serialized_end = 1712 - _RESETOPTIONS._serialized_start = 1715 - _RESETOPTIONS._serialized_end = 2080 - _CALLBACK._serialized_start = 2083 - _CALLBACK._serialized_end = 2439 - _CALLBACK_NEXUS._serialized_start = 2261 - _CALLBACK_NEXUS._serialized_end = 2396 - _CALLBACK_NEXUS_HEADERENTRY._serialized_start = 2351 - _CALLBACK_NEXUS_HEADERENTRY._serialized_end = 2396 - _CALLBACK_INTERNAL._serialized_start = 2398 - _CALLBACK_INTERNAL._serialized_end = 2422 - _LINK._serialized_start = 2442 - _LINK._serialized_end = 3476 - _LINK_WORKFLOWEVENT._serialized_start = 2771 - _LINK_WORKFLOWEVENT._serialized_end = 3210 - _LINK_WORKFLOWEVENT_EVENTREFERENCE._serialized_start = 3013 - _LINK_WORKFLOWEVENT_EVENTREFERENCE._serialized_end = 3101 - _LINK_WORKFLOWEVENT_REQUESTIDREFERENCE._serialized_start = 3103 - _LINK_WORKFLOWEVENT_REQUESTIDREFERENCE._serialized_end = 3197 - _LINK_BATCHJOB._serialized_start = 3212 - _LINK_BATCHJOB._serialized_end = 3238 - _LINK_ACTIVITY._serialized_start = 3240 - _LINK_ACTIVITY._serialized_end = 3306 - _LINK_NEXUSOPERATION._serialized_start = 3308 - _LINK_NEXUSOPERATION._serialized_end = 3381 - _LINK_WORKFLOW._serialized_start = 3383 - _LINK_WORKFLOW._serialized_end = 3465 - _PRINCIPAL._serialized_start = 3478 - _PRINCIPAL._serialized_end = 3517 - _PRIORITY._serialized_start = 3519 - _PRIORITY._serialized_end = 3598 - _WORKERSELECTOR._serialized_start = 3600 - _WORKERSELECTOR._serialized_end = 3659 - _ONCONFLICTOPTIONS._serialized_start = 3661 - _ONCONFLICTOPTIONS._serialized_end = 3766 + _DATABLOB._serialized_start = 269 + _DATABLOB._serialized_end = 353 + _PAYLOADS._serialized_start = 355 + _PAYLOADS._serialized_end = 416 + _PAYLOAD._serialized_start = 419 + _PAYLOAD._serialized_end = 685 + _PAYLOAD_METADATAENTRY._serialized_start = 592 + _PAYLOAD_METADATAENTRY._serialized_end = 639 + _PAYLOAD_EXTERNALPAYLOADDETAILS._serialized_start = 641 + _PAYLOAD_EXTERNALPAYLOADDETAILS._serialized_end = 685 + _SEARCHATTRIBUTES._serialized_start = 688 + _SEARCHATTRIBUTES._serialized_end = 878 + _SEARCHATTRIBUTES_INDEXEDFIELDSENTRY._serialized_start = 793 + _SEARCHATTRIBUTES_INDEXEDFIELDSENTRY._serialized_end = 878 + _MEMO._serialized_start = 881 + _MEMO._serialized_end = 1025 + _MEMO_FIELDSENTRY._serialized_start = 947 + _MEMO_FIELDSENTRY._serialized_end = 1025 + _HEADER._serialized_start = 1028 + _HEADER._serialized_end = 1176 + _HEADER_FIELDSENTRY._serialized_start = 947 + _HEADER_FIELDSENTRY._serialized_end = 1025 + _WORKFLOWEXECUTION._serialized_start = 1178 + _WORKFLOWEXECUTION._serialized_end = 1234 + _EXECUTION._serialized_start = 1236 + _EXECUTION._serialized_end = 1336 + _WORKFLOWTYPE._serialized_start = 1338 + _WORKFLOWTYPE._serialized_end = 1366 + _ACTIVITYTYPE._serialized_start = 1368 + _ACTIVITYTYPE._serialized_end = 1396 + _RETRYPOLICY._serialized_start = 1399 + _RETRYPOLICY._serialized_end = 1608 + _METERINGMETADATA._serialized_start = 1610 + _METERINGMETADATA._serialized_end = 1680 + _WORKERVERSIONSTAMP._serialized_start = 1682 + _WORKERVERSIONSTAMP._serialized_end = 1744 + _WORKERVERSIONCAPABILITIES._serialized_start = 1746 + _WORKERVERSIONCAPABILITIES._serialized_end = 1847 + _RESETOPTIONS._serialized_start = 1850 + _RESETOPTIONS._serialized_end = 2215 + _CALLBACK._serialized_start = 2218 + _CALLBACK._serialized_end = 2574 + _CALLBACK_NEXUS._serialized_start = 2396 + _CALLBACK_NEXUS._serialized_end = 2531 + _CALLBACK_NEXUS_HEADERENTRY._serialized_start = 2486 + _CALLBACK_NEXUS_HEADERENTRY._serialized_end = 2531 + _CALLBACK_INTERNAL._serialized_start = 2533 + _CALLBACK_INTERNAL._serialized_end = 2557 + _LINK._serialized_start = 2577 + _LINK._serialized_end = 3611 + _LINK_WORKFLOWEVENT._serialized_start = 2906 + _LINK_WORKFLOWEVENT._serialized_end = 3345 + _LINK_WORKFLOWEVENT_EVENTREFERENCE._serialized_start = 3148 + _LINK_WORKFLOWEVENT_EVENTREFERENCE._serialized_end = 3236 + _LINK_WORKFLOWEVENT_REQUESTIDREFERENCE._serialized_start = 3238 + _LINK_WORKFLOWEVENT_REQUESTIDREFERENCE._serialized_end = 3332 + _LINK_BATCHJOB._serialized_start = 3347 + _LINK_BATCHJOB._serialized_end = 3373 + _LINK_ACTIVITY._serialized_start = 3375 + _LINK_ACTIVITY._serialized_end = 3441 + _LINK_NEXUSOPERATION._serialized_start = 3443 + _LINK_NEXUSOPERATION._serialized_end = 3516 + _LINK_WORKFLOW._serialized_start = 3518 + _LINK_WORKFLOW._serialized_end = 3600 + _PRINCIPAL._serialized_start = 3613 + _PRINCIPAL._serialized_end = 3652 + _PRIORITY._serialized_start = 3654 + _PRIORITY._serialized_end = 3733 + _WORKERSELECTOR._serialized_start = 3735 + _WORKERSELECTOR._serialized_end = 3794 + _ONCONFLICTOPTIONS._serialized_start = 3796 + _ONCONFLICTOPTIONS._serialized_end = 3901 + _TIMESKIPPINGCONFIG._serialized_start = 3904 + _TIMESKIPPINGCONFIG._serialized_end = 4074 + _FASTFORWARDCONFIG._serialized_start = 4076 + _FASTFORWARDCONFIG._serialized_end = 4152 + _TIMESKIPPINGSTATEPROPAGATION._serialized_start = 4155 + _TIMESKIPPINGSTATEPROPAGATION._serialized_end = 4336 + _TIMESKIPPINGINFO._serialized_start = 4339 + _TIMESKIPPINGINFO._serialized_end = 4593 + _TIMESKIPPINGFASTFORWARDINFO._serialized_start = 4596 + _TIMESKIPPINGFASTFORWARDINFO._serialized_end = 4780 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/common/v1/message_pb2.pyi b/temporalio/api/common/v1/message_pb2.pyi index 76e87fd56..f7ed8d0fd 100644 --- a/temporalio/api/common/v1/message_pb2.pyi +++ b/temporalio/api/common/v1/message_pb2.pyi @@ -12,6 +12,7 @@ import google.protobuf.duration_pb2 import google.protobuf.empty_pb2 import google.protobuf.internal.containers import google.protobuf.message +import google.protobuf.timestamp_pb2 import temporalio.api.enums.v1.common_pb2 import temporalio.api.enums.v1.event_type_pb2 @@ -320,6 +321,35 @@ class WorkflowExecution(google.protobuf.message.Message): global___WorkflowExecution = WorkflowExecution +class Execution(google.protobuf.message.Message): + """Identifies a specific execution within a namespace. This is used for standalone activities + executions in batch jobs currently. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TYPE_FIELD_NUMBER: builtins.int + BUSINESS_ID_FIELD_NUMBER: builtins.int + RUN_ID_FIELD_NUMBER: builtins.int + type: temporalio.api.enums.v1.common_pb2.ExecutionType.ValueType + business_id: builtins.str + run_id: builtins.str + def __init__( + self, + *, + type: temporalio.api.enums.v1.common_pb2.ExecutionType.ValueType = ..., + business_id: builtins.str = ..., + run_id: builtins.str = ..., + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "business_id", b"business_id", "run_id", b"run_id", "type", b"type" + ], + ) -> None: ... + +global___Execution = Execution + class WorkflowType(google.protobuf.message.Message): """Represents the identifier used by a workflow author to define the workflow. Typically, the name of a function. This is sometimes referred to as the workflow's "name" @@ -1255,3 +1285,283 @@ class OnConflictOptions(google.protobuf.message.Message): ) -> None: ... global___OnConflictOptions = OnConflictOptions + +class TimeSkippingConfig(google.protobuf.message.Message): + """The configuration for time skipping of an execution. + When time skipping is enabled, virtual time advances automatically whenever there is no in-flight work. + Options like fast_forward, disable_propagation, and max_session_skip_count are provided for granular + control of the execution's time skipping behavior. See each field's comment for a detailed explanation. + + An example of workflows with time skipping: + For workflows, an execution is a chain of runs including retries, cron, and continue-as-new. + In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, etc. + User timers are not classified as in-flight work and will be skipped over; the virtual clock may also skip to the + time point of the registered fast-forward when there is no in-flight work. + Whenever time is skipped, the skip count is incremented by one; max_session_skip_count bounds the number of skips allowed within a single time-skipping session. + For child workflows, by default, if the parent execution is skipping time, the child execution will also skip time, + but a parent's fast_forward won't affect its child's execution. A flag is provided to disable propagation of the + "enabled" flag to child workflows; regardless of that flag, a child workflow inherits the virtual time from the + parent execution as its start time. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ENABLED_FIELD_NUMBER: builtins.int + FAST_FORWARD_CONFIG_FIELD_NUMBER: builtins.int + DISABLE_PROPAGATION_FIELD_NUMBER: builtins.int + MAX_SESSION_SKIP_COUNT_FIELD_NUMBER: builtins.int + enabled: builtins.bool + """Enables or disables time skipping for this workflow execution.""" + @property + def fast_forward_config(self) -> global___FastForwardConfig: + """An optional opt-in to control time-skipping behavior through fast-forward; see its definition for details.""" + disable_propagation: builtins.bool + """By default, executions started by another execution (e.g. a child workflow of a parent workflow or + a schedule with the time-skipping policy enabled) inherit the "enabled" flag and skip time when possible. + This flag disables that inheritance. + """ + max_session_skip_count: builtins.int + """The maximum number of skips allowed every time this field is updated. It protects the execution from + situations like unlimited retries when backoff is skipped. + + Every time the execution skips time, the skip count is incremented by one, and when it reaches + max_session_skip_count, time skipping stops. Whenever this config field is updated, the accumulated + skip count is cleared, marking the start of a new session. + For an execution with a chain of runs (retry, cron, continue-as-new), the count is accumulated + across all runs within the same session. + + If this field is not set, the server applies a large default value (e.g. 100). The default can + be changed through dynamic config, and is overridden by this field when set. + """ + def __init__( + self, + *, + enabled: builtins.bool = ..., + fast_forward_config: global___FastForwardConfig | None = ..., + disable_propagation: builtins.bool = ..., + max_session_skip_count: builtins.int = ..., + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "fast_forward_config", b"fast_forward_config" + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "disable_propagation", + b"disable_propagation", + "enabled", + b"enabled", + "fast_forward_config", + b"fast_forward_config", + "max_session_skip_count", + b"max_session_skip_count", + ], + ) -> None: ... + +global___TimeSkippingConfig = TimeSkippingConfig + +class FastForwardConfig(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ID_FIELD_NUMBER: builtins.int + DURATION_FIELD_NUMBER: builtins.int + id: builtins.str + """A client-supplied ID, required field, set alongside `duration`. It is used to poll for + fast-forward completion via PollWorkflowExecutionTimeSkipping. + The server performs no idempotency check on this ID; the client is responsible for managing it. + """ + @property + def duration(self) -> google.protobuf.duration_pb2.Duration: + """Fast-forward the current execution by this duration ahead of the current execution time; required field. + The duration yields a target time (current execution time + duration), surfaced as `target_time` in + TimeSkippingFastForwardInfo. Once virtual time reaches that target, the fast-forward completes, time + skipping is disabled, and no further time is skipped. Time skipping can be resumed either + by updating the TimeSkippingConfig with a new FastForwardConfig, or by clearing the FastForwardConfig + to skip through to the end of the execution. + + If this duration exceeds the remaining execution timeout, time will not pass beyond the end + of the execution, and the fast-forward won't have a chance to complete. + """ + def __init__( + self, + *, + id: builtins.str = ..., + duration: google.protobuf.duration_pb2.Duration | None = ..., + ) -> None: ... + def HasField( + self, field_name: typing_extensions.Literal["duration", b"duration"] + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["duration", b"duration", "id", b"id"], + ) -> None: ... + +global___FastForwardConfig = FastForwardConfig + +class TimeSkippingStatePropagation(google.protobuf.message.Message): + """The time-skipping state that needs to be propagated from one execution to another, or through a chain of runs + within the same execution. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + INITIAL_SKIPPED_DURATION_FIELD_NUMBER: builtins.int + FAST_FORWARD_TARGET_TIME_FIELD_NUMBER: builtins.int + INITIAL_SKIP_COUNT_FIELD_NUMBER: builtins.int + @property + def initial_skipped_duration(self) -> google.protobuf.duration_pb2.Duration: + """The time skipped by the previous run. It is propagated both to executions started by the + current execution and through a chain of runs (CaN, cron, retry). + """ + @property + def fast_forward_target_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """The fast-forward target time. It only propagates across a chain of runs within the same execution.""" + initial_skip_count: builtins.int + """The initial skip count. It only propagates across a chain of runs within the same execution.""" + def __init__( + self, + *, + initial_skipped_duration: google.protobuf.duration_pb2.Duration | None = ..., + fast_forward_target_time: google.protobuf.timestamp_pb2.Timestamp | None = ..., + initial_skip_count: builtins.int = ..., + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "fast_forward_target_time", + b"fast_forward_target_time", + "initial_skipped_duration", + b"initial_skipped_duration", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "fast_forward_target_time", + b"fast_forward_target_time", + "initial_skip_count", + b"initial_skip_count", + "initial_skipped_duration", + b"initial_skipped_duration", + ], + ) -> None: ... + +global___TimeSkippingStatePropagation = TimeSkippingStatePropagation + +class TimeSkippingInfo(google.protobuf.message.Message): + """Describes the current time-skipping state of a workflow execution.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CURRENT_TIME_FIELD_NUMBER: builtins.int + EFFECTIVE_CONFIG_FIELD_NUMBER: builtins.int + FAST_FORWARD_INFO_FIELD_NUMBER: builtins.int + CURRENT_SESSION_SKIP_COUNT_FIELD_NUMBER: builtins.int + @property + def current_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """Current virtual time of the execution. If the execution hasn't skipped + any time yet, it will be the same as wall clock time. + """ + @property + def effective_config(self) -> global___TimeSkippingConfig: + """The current effective time-skipping config, which can differ from the config the user last set: + internally-defaulted fields are populated, and `enabled` reflects whether the execution is still + skipping time — e.g. it is set to false once `max_session_skip_count` is reached, the fast-forward + completes, or a client call disables time skipping. + """ + @property + def fast_forward_info(self) -> global___TimeSkippingFastForwardInfo: + """The execution's current fast-forward, if any. Unset if time skipping is enabled without a fast-forward.""" + current_session_skip_count: builtins.int + """The number of skips accumulated in the current session, bounded by `max_session_skip_count`. + A new session begins — and this resets to 0 — each time `max_session_skip_count` is updated. + """ + def __init__( + self, + *, + current_time: google.protobuf.timestamp_pb2.Timestamp | None = ..., + effective_config: global___TimeSkippingConfig | None = ..., + fast_forward_info: global___TimeSkippingFastForwardInfo | None = ..., + current_session_skip_count: builtins.int = ..., + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "current_time", + b"current_time", + "effective_config", + b"effective_config", + "fast_forward_info", + b"fast_forward_info", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "current_session_skip_count", + b"current_session_skip_count", + "current_time", + b"current_time", + "effective_config", + b"effective_config", + "fast_forward_info", + b"fast_forward_info", + ], + ) -> None: ... + +global___TimeSkippingInfo = TimeSkippingInfo + +class TimeSkippingFastForwardInfo(google.protobuf.message.Message): + """TimeSkippingFastForwardInfo describes the current time-skipping fast-forward on an execution.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + FAST_FORWARD_DURATION_FIELD_NUMBER: builtins.int + FAST_FORWARD_ID_FIELD_NUMBER: builtins.int + TARGET_TIME_FIELD_NUMBER: builtins.int + HAS_COMPLETED_FIELD_NUMBER: builtins.int + @property + def fast_forward_duration(self) -> google.protobuf.duration_pb2.Duration: + """The client-supplied `fast_forward` duration.""" + fast_forward_id: builtins.str + """The client-supplied ID set alongside `fast_forward` duration.""" + @property + def target_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """The target virtual time at which the fast-forward completes.""" + has_completed: builtins.bool + """True once `target_time` has been reached.""" + def __init__( + self, + *, + fast_forward_duration: google.protobuf.duration_pb2.Duration | None = ..., + fast_forward_id: builtins.str = ..., + target_time: google.protobuf.timestamp_pb2.Timestamp | None = ..., + has_completed: builtins.bool = ..., + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "fast_forward_duration", + b"fast_forward_duration", + "target_time", + b"target_time", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "fast_forward_duration", + b"fast_forward_duration", + "fast_forward_id", + b"fast_forward_id", + "has_completed", + b"has_completed", + "target_time", + b"target_time", + ], + ) -> None: ... + +global___TimeSkippingFastForwardInfo = TimeSkippingFastForwardInfo diff --git a/temporalio/api/deployment/v1/__init__.py b/temporalio/api/deployment/v1/__init__.py index 22f3e77e5..2abc00d31 100644 --- a/temporalio/api/deployment/v1/__init__.py +++ b/temporalio/api/deployment/v1/__init__.py @@ -1,4 +1,5 @@ from .message_pb2 import ( + ComputeStatus, Deployment, DeploymentInfo, DeploymentListInfo, @@ -14,6 +15,7 @@ ) __all__ = [ + "ComputeStatus", "Deployment", "DeploymentInfo", "DeploymentListInfo", diff --git a/temporalio/api/deployment/v1/message_pb2.py b/temporalio/api/deployment/v1/message_pb2.py index 08914a31f..cc3b1ea75 100644 --- a/temporalio/api/deployment/v1/message_pb2.py +++ b/temporalio/api/deployment/v1/message_pb2.py @@ -33,7 +33,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n(temporal/api/deployment/v1/message.proto\x12\x1atemporal.api.deployment.v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a&temporal/api/enums/v1/deployment.proto\x1a&temporal/api/enums/v1/task_queue.proto\x1a$temporal/api/common/v1/message.proto\x1a$temporal/api/compute/v1/config.proto"\x91\x01\n\x17WorkerDeploymentOptions\x12\x17\n\x0f\x64\x65ployment_name\x18\x01 \x01(\t\x12\x10\n\x08\x62uild_id\x18\x02 \x01(\t\x12K\n\x16worker_versioning_mode\x18\x03 \x01(\x0e\x32+.temporal.api.enums.v1.WorkerVersioningMode"3\n\nDeployment\x12\x13\n\x0bseries_name\x18\x01 \x01(\t\x12\x10\n\x08\x62uild_id\x18\x02 \x01(\t"\x8e\x04\n\x0e\x44\x65ploymentInfo\x12:\n\ndeployment\x18\x01 \x01(\x0b\x32&.temporal.api.deployment.v1.Deployment\x12/\n\x0b\x63reate_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12R\n\x10task_queue_infos\x18\x03 \x03(\x0b\x32\x38.temporal.api.deployment.v1.DeploymentInfo.TaskQueueInfo\x12J\n\x08metadata\x18\x04 \x03(\x0b\x32\x38.temporal.api.deployment.v1.DeploymentInfo.MetadataEntry\x12\x12\n\nis_current\x18\x05 \x01(\x08\x1aP\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01\x1a\x88\x01\n\rTaskQueueInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x32\n\x04type\x18\x02 \x01(\x0e\x32$.temporal.api.enums.v1.TaskQueueType\x12\x35\n\x11\x66irst_poller_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xea\x01\n\x18UpdateDeploymentMetadata\x12_\n\x0eupsert_entries\x18\x01 \x03(\x0b\x32G.temporal.api.deployment.v1.UpdateDeploymentMetadata.UpsertEntriesEntry\x12\x16\n\x0eremove_entries\x18\x02 \x03(\t\x1aU\n\x12UpsertEntriesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\x95\x01\n\x12\x44\x65ploymentListInfo\x12:\n\ndeployment\x18\x01 \x01(\x0b\x32&.temporal.api.deployment.v1.Deployment\x12/\n\x0b\x63reate_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x12\n\nis_current\x18\x03 \x01(\x08"\xad\x08\n\x1bWorkerDeploymentVersionInfo\x12\x13\n\x07version\x18\x01 \x01(\tB\x02\x18\x01\x12\x44\n\x06status\x18\x0e \x01(\x0e\x32\x34.temporal.api.enums.v1.WorkerDeploymentVersionStatus\x12O\n\x12\x64\x65ployment_version\x18\x0b \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t\x12/\n\x0b\x63reate_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x38\n\x14routing_changed_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x36\n\x12\x63urrent_since_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x36\n\x12ramping_since_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x39\n\x15\x66irst_activation_time\x18\x0c \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x35\n\x11last_current_time\x18\x0f \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12:\n\x16last_deactivation_time\x18\r \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x17\n\x0framp_percentage\x18\x07 \x01(\x02\x12\x66\n\x10task_queue_infos\x18\x08 \x03(\x0b\x32L.temporal.api.deployment.v1.WorkerDeploymentVersionInfo.VersionTaskQueueInfo\x12\x46\n\rdrainage_info\x18\t \x01(\x0b\x32/.temporal.api.deployment.v1.VersionDrainageInfo\x12=\n\x08metadata\x18\n \x01(\x0b\x32+.temporal.api.deployment.v1.VersionMetadata\x12>\n\x0e\x63ompute_config\x18\x10 \x01(\x0b\x32&.temporal.api.compute.v1.ComputeConfig\x12\x1e\n\x16last_modifier_identity\x18\x11 \x01(\t\x1aX\n\x14VersionTaskQueueInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x32\n\x04type\x18\x02 \x01(\x0e\x32$.temporal.api.enums.v1.TaskQueueType"\xc1\x01\n\x13VersionDrainageInfo\x12<\n\x06status\x18\x01 \x01(\x0e\x32,.temporal.api.enums.v1.VersionDrainageStatus\x12\x35\n\x11last_changed_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x35\n\x11last_checked_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xc1\t\n\x14WorkerDeploymentInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12j\n\x11version_summaries\x18\x02 \x03(\x0b\x32O.temporal.api.deployment.v1.WorkerDeploymentInfo.WorkerDeploymentVersionSummary\x12/\n\x0b\x63reate_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x41\n\x0erouting_config\x18\x04 \x01(\x0b\x32).temporal.api.deployment.v1.RoutingConfig\x12\x1e\n\x16last_modifier_identity\x18\x05 \x01(\t\x12\x18\n\x10manager_identity\x18\x06 \x01(\t\x12T\n\x1brouting_config_update_state\x18\x07 \x01(\x0e\x32/.temporal.api.enums.v1.RoutingConfigUpdateState\x1a\xaa\x06\n\x1eWorkerDeploymentVersionSummary\x12\x13\n\x07version\x18\x01 \x01(\tB\x02\x18\x01\x12\x44\n\x06status\x18\x0b \x01(\x0e\x32\x34.temporal.api.enums.v1.WorkerDeploymentVersionStatus\x12O\n\x12\x64\x65ployment_version\x18\x04 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12/\n\x0b\x63reate_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x45\n\x0f\x64rainage_status\x18\x03 \x01(\x0e\x32,.temporal.api.enums.v1.VersionDrainageStatus\x12\x46\n\rdrainage_info\x18\x05 \x01(\x0b\x32/.temporal.api.deployment.v1.VersionDrainageInfo\x12\x36\n\x12\x63urrent_since_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x36\n\x12ramping_since_time\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x37\n\x13routing_update_time\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x39\n\x15\x66irst_activation_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x35\n\x11last_current_time\x18\x0c \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12:\n\x16last_deactivation_time\x18\n \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x45\n\x0e\x63ompute_config\x18\r \x01(\x0b\x32-.temporal.api.compute.v1.ComputeConfigSummary"D\n\x17WorkerDeploymentVersion\x12\x10\n\x08\x62uild_id\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t"\xad\x01\n\x0fVersionMetadata\x12I\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x38.temporal.api.deployment.v1.VersionMetadata.EntriesEntry\x1aO\n\x0c\x45ntriesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\x89\x04\n\rRoutingConfig\x12W\n\x1a\x63urrent_deployment_version\x18\x07 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x1b\n\x0f\x63urrent_version\x18\x01 \x01(\tB\x02\x18\x01\x12W\n\x1aramping_deployment_version\x18\t \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x1b\n\x0framping_version\x18\x02 \x01(\tB\x02\x18\x01\x12"\n\x1aramping_version_percentage\x18\x03 \x01(\x02\x12@\n\x1c\x63urrent_version_changed_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12@\n\x1cramping_version_changed_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12K\n\'ramping_version_percentage_changed_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x17\n\x0frevision_number\x18\n \x01(\x03"\x8a\x02\n\x18InheritedAutoUpgradeInfo\x12V\n\x19source_deployment_version\x18\x01 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12)\n!source_deployment_revision_number\x18\x02 \x01(\x03\x12k\n+continue_as_new_initial_versioning_behavior\x18\x03 \x01(\x0e\x32\x36.temporal.api.enums.v1.ContinueAsNewVersioningBehaviorB\x9d\x01\n\x1dio.temporal.api.deployment.v1B\x0cMessageProtoP\x01Z+go.temporal.io/api/deployment/v1;deployment\xaa\x02\x1cTemporalio.Api.Deployment.V1\xea\x02\x1fTemporalio::Api::Deployment::V1b\x06proto3' + b'\n(temporal/api/deployment/v1/message.proto\x12\x1atemporal.api.deployment.v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a&temporal/api/enums/v1/deployment.proto\x1a&temporal/api/enums/v1/task_queue.proto\x1a$temporal/api/common/v1/message.proto\x1a$temporal/api/compute/v1/config.proto"\x91\x01\n\x17WorkerDeploymentOptions\x12\x17\n\x0f\x64\x65ployment_name\x18\x01 \x01(\t\x12\x10\n\x08\x62uild_id\x18\x02 \x01(\t\x12K\n\x16worker_versioning_mode\x18\x03 \x01(\x0e\x32+.temporal.api.enums.v1.WorkerVersioningMode"3\n\nDeployment\x12\x13\n\x0bseries_name\x18\x01 \x01(\t\x12\x10\n\x08\x62uild_id\x18\x02 \x01(\t"\x8e\x04\n\x0e\x44\x65ploymentInfo\x12:\n\ndeployment\x18\x01 \x01(\x0b\x32&.temporal.api.deployment.v1.Deployment\x12/\n\x0b\x63reate_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12R\n\x10task_queue_infos\x18\x03 \x03(\x0b\x32\x38.temporal.api.deployment.v1.DeploymentInfo.TaskQueueInfo\x12J\n\x08metadata\x18\x04 \x03(\x0b\x32\x38.temporal.api.deployment.v1.DeploymentInfo.MetadataEntry\x12\x12\n\nis_current\x18\x05 \x01(\x08\x1aP\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01\x1a\x88\x01\n\rTaskQueueInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x32\n\x04type\x18\x02 \x01(\x0e\x32$.temporal.api.enums.v1.TaskQueueType\x12\x35\n\x11\x66irst_poller_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xea\x01\n\x18UpdateDeploymentMetadata\x12_\n\x0eupsert_entries\x18\x01 \x03(\x0b\x32G.temporal.api.deployment.v1.UpdateDeploymentMetadata.UpsertEntriesEntry\x12\x16\n\x0eremove_entries\x18\x02 \x03(\t\x1aU\n\x12UpsertEntriesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\x95\x01\n\x12\x44\x65ploymentListInfo\x12:\n\ndeployment\x18\x01 \x01(\x0b\x32&.temporal.api.deployment.v1.Deployment\x12/\n\x0b\x63reate_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x12\n\nis_current\x18\x03 \x01(\x08"\xad\x08\n\x1bWorkerDeploymentVersionInfo\x12\x13\n\x07version\x18\x01 \x01(\tB\x02\x18\x01\x12\x44\n\x06status\x18\x0e \x01(\x0e\x32\x34.temporal.api.enums.v1.WorkerDeploymentVersionStatus\x12O\n\x12\x64\x65ployment_version\x18\x0b \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t\x12/\n\x0b\x63reate_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x38\n\x14routing_changed_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x36\n\x12\x63urrent_since_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x36\n\x12ramping_since_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x39\n\x15\x66irst_activation_time\x18\x0c \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x35\n\x11last_current_time\x18\x0f \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12:\n\x16last_deactivation_time\x18\r \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x17\n\x0framp_percentage\x18\x07 \x01(\x02\x12\x66\n\x10task_queue_infos\x18\x08 \x03(\x0b\x32L.temporal.api.deployment.v1.WorkerDeploymentVersionInfo.VersionTaskQueueInfo\x12\x46\n\rdrainage_info\x18\t \x01(\x0b\x32/.temporal.api.deployment.v1.VersionDrainageInfo\x12=\n\x08metadata\x18\n \x01(\x0b\x32+.temporal.api.deployment.v1.VersionMetadata\x12>\n\x0e\x63ompute_config\x18\x10 \x01(\x0b\x32&.temporal.api.compute.v1.ComputeConfig\x12\x1e\n\x16last_modifier_identity\x18\x11 \x01(\t\x1aX\n\x14VersionTaskQueueInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x32\n\x04type\x18\x02 \x01(\x0e\x32$.temporal.api.enums.v1.TaskQueueType"\xc1\x01\n\x13VersionDrainageInfo\x12<\n\x06status\x18\x01 \x01(\x0e\x32,.temporal.api.enums.v1.VersionDrainageStatus\x12\x35\n\x11last_changed_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x35\n\x11last_checked_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xd8\x01\n\rComputeStatus\x12_\n\x13provider_validation\x18\x01 \x01(\x0b\x32\x42.temporal.api.deployment.v1.ComputeStatus.ProviderValidationStatus\x1a\x66\n\x18ProviderValidationStatus\x12\x15\n\rerror_message\x18\x01 \x01(\t\x12\x33\n\x0flast_check_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\x84\n\n\x14WorkerDeploymentInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12j\n\x11version_summaries\x18\x02 \x03(\x0b\x32O.temporal.api.deployment.v1.WorkerDeploymentInfo.WorkerDeploymentVersionSummary\x12/\n\x0b\x63reate_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x41\n\x0erouting_config\x18\x04 \x01(\x0b\x32).temporal.api.deployment.v1.RoutingConfig\x12\x1e\n\x16last_modifier_identity\x18\x05 \x01(\t\x12\x18\n\x10manager_identity\x18\x06 \x01(\t\x12T\n\x1brouting_config_update_state\x18\x07 \x01(\x0e\x32/.temporal.api.enums.v1.RoutingConfigUpdateState\x1a\xed\x06\n\x1eWorkerDeploymentVersionSummary\x12\x13\n\x07version\x18\x01 \x01(\tB\x02\x18\x01\x12\x44\n\x06status\x18\x0b \x01(\x0e\x32\x34.temporal.api.enums.v1.WorkerDeploymentVersionStatus\x12O\n\x12\x64\x65ployment_version\x18\x04 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12/\n\x0b\x63reate_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x45\n\x0f\x64rainage_status\x18\x03 \x01(\x0e\x32,.temporal.api.enums.v1.VersionDrainageStatus\x12\x46\n\rdrainage_info\x18\x05 \x01(\x0b\x32/.temporal.api.deployment.v1.VersionDrainageInfo\x12\x36\n\x12\x63urrent_since_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x36\n\x12ramping_since_time\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x37\n\x13routing_update_time\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x39\n\x15\x66irst_activation_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x35\n\x11last_current_time\x18\x0c \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12:\n\x16last_deactivation_time\x18\n \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x45\n\x0e\x63ompute_config\x18\r \x01(\x0b\x32-.temporal.api.compute.v1.ComputeConfigSummary\x12\x41\n\x0e\x63ompute_status\x18\x0e \x01(\x0b\x32).temporal.api.deployment.v1.ComputeStatus"D\n\x17WorkerDeploymentVersion\x12\x10\n\x08\x62uild_id\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t"\xad\x01\n\x0fVersionMetadata\x12I\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x38.temporal.api.deployment.v1.VersionMetadata.EntriesEntry\x1aO\n\x0c\x45ntriesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\x89\x04\n\rRoutingConfig\x12W\n\x1a\x63urrent_deployment_version\x18\x07 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x1b\n\x0f\x63urrent_version\x18\x01 \x01(\tB\x02\x18\x01\x12W\n\x1aramping_deployment_version\x18\t \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x1b\n\x0framping_version\x18\x02 \x01(\tB\x02\x18\x01\x12"\n\x1aramping_version_percentage\x18\x03 \x01(\x02\x12@\n\x1c\x63urrent_version_changed_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12@\n\x1cramping_version_changed_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12K\n\'ramping_version_percentage_changed_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x17\n\x0frevision_number\x18\n \x01(\x03"\x8a\x02\n\x18InheritedAutoUpgradeInfo\x12V\n\x19source_deployment_version\x18\x01 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12)\n!source_deployment_revision_number\x18\x02 \x01(\x03\x12k\n+continue_as_new_initial_versioning_behavior\x18\x03 \x01(\x0e\x32\x36.temporal.api.enums.v1.ContinueAsNewVersioningBehaviorB\x9d\x01\n\x1dio.temporal.api.deployment.v1B\x0cMessageProtoP\x01Z+go.temporal.io/api/deployment/v1;deployment\xaa\x02\x1cTemporalio.Api.Deployment.V1\xea\x02\x1fTemporalio::Api::Deployment::V1b\x06proto3' ) @@ -54,6 +54,10 @@ _WORKERDEPLOYMENTVERSIONINFO.nested_types_by_name["VersionTaskQueueInfo"] ) _VERSIONDRAINAGEINFO = DESCRIPTOR.message_types_by_name["VersionDrainageInfo"] +_COMPUTESTATUS = DESCRIPTOR.message_types_by_name["ComputeStatus"] +_COMPUTESTATUS_PROVIDERVALIDATIONSTATUS = _COMPUTESTATUS.nested_types_by_name[ + "ProviderValidationStatus" +] _WORKERDEPLOYMENTINFO = DESCRIPTOR.message_types_by_name["WorkerDeploymentInfo"] _WORKERDEPLOYMENTINFO_WORKERDEPLOYMENTVERSIONSUMMARY = ( _WORKERDEPLOYMENTINFO.nested_types_by_name["WorkerDeploymentVersionSummary"] @@ -180,6 +184,27 @@ ) _sym_db.RegisterMessage(VersionDrainageInfo) +ComputeStatus = _reflection.GeneratedProtocolMessageType( + "ComputeStatus", + (_message.Message,), + { + "ProviderValidationStatus": _reflection.GeneratedProtocolMessageType( + "ProviderValidationStatus", + (_message.Message,), + { + "DESCRIPTOR": _COMPUTESTATUS_PROVIDERVALIDATIONSTATUS, + "__module__": "temporalio.api.deployment.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.deployment.v1.ComputeStatus.ProviderValidationStatus) + }, + ), + "DESCRIPTOR": _COMPUTESTATUS, + "__module__": "temporalio.api.deployment.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.deployment.v1.ComputeStatus) + }, +) +_sym_db.RegisterMessage(ComputeStatus) +_sym_db.RegisterMessage(ComputeStatus.ProviderValidationStatus) + WorkerDeploymentInfo = _reflection.GeneratedProtocolMessageType( "WorkerDeploymentInfo", (_message.Message,), @@ -300,18 +325,22 @@ _WORKERDEPLOYMENTVERSIONINFO_VERSIONTASKQUEUEINFO._serialized_end = 2488 _VERSIONDRAINAGEINFO._serialized_start = 2491 _VERSIONDRAINAGEINFO._serialized_end = 2684 - _WORKERDEPLOYMENTINFO._serialized_start = 2687 - _WORKERDEPLOYMENTINFO._serialized_end = 3904 - _WORKERDEPLOYMENTINFO_WORKERDEPLOYMENTVERSIONSUMMARY._serialized_start = 3094 - _WORKERDEPLOYMENTINFO_WORKERDEPLOYMENTVERSIONSUMMARY._serialized_end = 3904 - _WORKERDEPLOYMENTVERSION._serialized_start = 3906 - _WORKERDEPLOYMENTVERSION._serialized_end = 3974 - _VERSIONMETADATA._serialized_start = 3977 - _VERSIONMETADATA._serialized_end = 4150 - _VERSIONMETADATA_ENTRIESENTRY._serialized_start = 4071 - _VERSIONMETADATA_ENTRIESENTRY._serialized_end = 4150 - _ROUTINGCONFIG._serialized_start = 4153 - _ROUTINGCONFIG._serialized_end = 4674 - _INHERITEDAUTOUPGRADEINFO._serialized_start = 4677 - _INHERITEDAUTOUPGRADEINFO._serialized_end = 4943 + _COMPUTESTATUS._serialized_start = 2687 + _COMPUTESTATUS._serialized_end = 2903 + _COMPUTESTATUS_PROVIDERVALIDATIONSTATUS._serialized_start = 2801 + _COMPUTESTATUS_PROVIDERVALIDATIONSTATUS._serialized_end = 2903 + _WORKERDEPLOYMENTINFO._serialized_start = 2906 + _WORKERDEPLOYMENTINFO._serialized_end = 4190 + _WORKERDEPLOYMENTINFO_WORKERDEPLOYMENTVERSIONSUMMARY._serialized_start = 3313 + _WORKERDEPLOYMENTINFO_WORKERDEPLOYMENTVERSIONSUMMARY._serialized_end = 4190 + _WORKERDEPLOYMENTVERSION._serialized_start = 4192 + _WORKERDEPLOYMENTVERSION._serialized_end = 4260 + _VERSIONMETADATA._serialized_start = 4263 + _VERSIONMETADATA._serialized_end = 4436 + _VERSIONMETADATA_ENTRIESENTRY._serialized_start = 4357 + _VERSIONMETADATA_ENTRIESENTRY._serialized_end = 4436 + _ROUTINGCONFIG._serialized_start = 4439 + _ROUTINGCONFIG._serialized_end = 4960 + _INHERITEDAUTOUPGRADEINFO._serialized_start = 4963 + _INHERITEDAUTOUPGRADEINFO._serialized_end = 5229 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/deployment/v1/message_pb2.pyi b/temporalio/api/deployment/v1/message_pb2.pyi index 1b97a8db1..96f31edd1 100644 --- a/temporalio/api/deployment/v1/message_pb2.pyi +++ b/temporalio/api/deployment/v1/message_pb2.pyi @@ -614,6 +614,71 @@ class VersionDrainageInfo(google.protobuf.message.Message): global___VersionDrainageInfo = VersionDrainageInfo +class ComputeStatus(google.protobuf.message.Message): + """ComputeStatus represents compute-related configuration and health for a Worker Deployment Version.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class ProviderValidationStatus(google.protobuf.message.Message): + """ProviderValidationStatus represents the result of the most recent + connectivity check between Temporal and a customer's compute provider. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ERROR_MESSAGE_FIELD_NUMBER: builtins.int + LAST_CHECK_TIME_FIELD_NUMBER: builtins.int + error_message: builtins.str + """Human-readable error message if connectivity validation failed. + An empty string means validation passed. + """ + @property + def last_check_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """Timestamp of the last validation check.""" + def __init__( + self, + *, + error_message: builtins.str = ..., + last_check_time: google.protobuf.timestamp_pb2.Timestamp | None = ..., + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "last_check_time", b"last_check_time" + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "error_message", b"error_message", "last_check_time", b"last_check_time" + ], + ) -> None: ... + + PROVIDER_VALIDATION_FIELD_NUMBER: builtins.int + @property + def provider_validation(self) -> global___ComputeStatus.ProviderValidationStatus: + """provider_validation encapsulates the health signal for validating the compute provider.""" + def __init__( + self, + *, + provider_validation: global___ComputeStatus.ProviderValidationStatus + | None = ..., + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "provider_validation", b"provider_validation" + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "provider_validation", b"provider_validation" + ], + ) -> None: ... + +global___ComputeStatus = ComputeStatus + class WorkerDeploymentInfo(google.protobuf.message.Message): """A Worker Deployment (Deployment, for short) represents all workers serving a shared set of Task Queues. Typically, a Deployment represents one service or @@ -642,6 +707,7 @@ class WorkerDeploymentInfo(google.protobuf.message.Message): LAST_CURRENT_TIME_FIELD_NUMBER: builtins.int LAST_DEACTIVATION_TIME_FIELD_NUMBER: builtins.int COMPUTE_CONFIG_FIELD_NUMBER: builtins.int + COMPUTE_STATUS_FIELD_NUMBER: builtins.int version: builtins.str """Deprecated. Use `deployment_version`.""" status: temporalio.api.enums.v1.deployment_pb2.WorkerDeploymentVersionStatus.ValueType @@ -692,6 +758,9 @@ class WorkerDeploymentInfo(google.protobuf.message.Message): def compute_config( self, ) -> temporalio.api.compute.v1.config_pb2.ComputeConfigSummary: ... + @property + def compute_status(self) -> global___ComputeStatus: + """ComputeStatus represents compute-related configuration and healthchecks.""" def __init__( self, *, @@ -710,12 +779,15 @@ class WorkerDeploymentInfo(google.protobuf.message.Message): | None = ..., compute_config: temporalio.api.compute.v1.config_pb2.ComputeConfigSummary | None = ..., + compute_status: global___ComputeStatus | None = ..., ) -> None: ... def HasField( self, field_name: typing_extensions.Literal[ "compute_config", b"compute_config", + "compute_status", + b"compute_status", "create_time", b"create_time", "current_since_time", @@ -741,6 +813,8 @@ class WorkerDeploymentInfo(google.protobuf.message.Message): field_name: typing_extensions.Literal[ "compute_config", b"compute_config", + "compute_status", + b"compute_status", "create_time", b"create_time", "current_since_time", diff --git a/temporalio/api/enums/v1/__init__.py b/temporalio/api/enums/v1/__init__.py index 82fef9b2c..3dc3a179b 100644 --- a/temporalio/api/enums/v1/__init__.py +++ b/temporalio/api/enums/v1/__init__.py @@ -9,6 +9,7 @@ ApplicationErrorCategory, CallbackState, EncodingType, + ExecutionType, IndexedValueType, NexusOperationCancellationState, PendingNexusOperationState, @@ -51,6 +52,7 @@ TaskQueueType, TaskReachability, ) +from .time_skipping_pb2 import FastForwardPollingResult from .update_pb2 import UpdateAdmittedEventOrigin, UpdateWorkflowExecutionLifecycleStage from .workflow_pb2 import ( ContinueAsNewInitiator, @@ -86,6 +88,8 @@ "DescribeTaskQueueMode", "EncodingType", "EventType", + "ExecutionType", + "FastForwardPollingResult", "HistoryEventFilterType", "IndexedValueType", "NamespaceState", diff --git a/temporalio/api/enums/v1/activity_pb2.py b/temporalio/api/enums/v1/activity_pb2.py index ba5fddd25..fd709beb6 100644 --- a/temporalio/api/enums/v1/activity_pb2.py +++ b/temporalio/api/enums/v1/activity_pb2.py @@ -16,7 +16,7 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b"\n$temporal/api/enums/v1/activity.proto\x12\x15temporal.api.enums.v1*\xb5\x02\n\x17\x41\x63tivityExecutionStatus\x12)\n%ACTIVITY_EXECUTION_STATUS_UNSPECIFIED\x10\x00\x12%\n!ACTIVITY_EXECUTION_STATUS_RUNNING\x10\x01\x12'\n#ACTIVITY_EXECUTION_STATUS_COMPLETED\x10\x02\x12$\n ACTIVITY_EXECUTION_STATUS_FAILED\x10\x03\x12&\n\"ACTIVITY_EXECUTION_STATUS_CANCELED\x10\x04\x12(\n$ACTIVITY_EXECUTION_STATUS_TERMINATED\x10\x05\x12'\n#ACTIVITY_EXECUTION_STATUS_TIMED_OUT\x10\x06*\xd8\x01\n\x15\x41\x63tivityIdReusePolicy\x12(\n$ACTIVITY_ID_REUSE_POLICY_UNSPECIFIED\x10\x00\x12,\n(ACTIVITY_ID_REUSE_POLICY_ALLOW_DUPLICATE\x10\x01\x12\x38\n4ACTIVITY_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY\x10\x02\x12-\n)ACTIVITY_ID_REUSE_POLICY_REJECT_DUPLICATE\x10\x03*\x9b\x01\n\x18\x41\x63tivityIdConflictPolicy\x12+\n'ACTIVITY_ID_CONFLICT_POLICY_UNSPECIFIED\x10\x00\x12$\n ACTIVITY_ID_CONFLICT_POLICY_FAIL\x10\x01\x12,\n(ACTIVITY_ID_CONFLICT_POLICY_USE_EXISTING\x10\x02\x42\x85\x01\n\x18io.temporal.api.enums.v1B\rActivityProtoP\x01Z!go.temporal.io/api/enums/v1;enums\xaa\x02\x17Temporalio.Api.Enums.V1\xea\x02\x1aTemporalio::Api::Enums::V1b\x06proto3" + b"\n$temporal/api/enums/v1/activity.proto\x12\x15temporal.api.enums.v1*\xdb\x02\n\x17\x41\x63tivityExecutionStatus\x12)\n%ACTIVITY_EXECUTION_STATUS_UNSPECIFIED\x10\x00\x12%\n!ACTIVITY_EXECUTION_STATUS_RUNNING\x10\x01\x12'\n#ACTIVITY_EXECUTION_STATUS_COMPLETED\x10\x02\x12$\n ACTIVITY_EXECUTION_STATUS_FAILED\x10\x03\x12&\n\"ACTIVITY_EXECUTION_STATUS_CANCELED\x10\x04\x12(\n$ACTIVITY_EXECUTION_STATUS_TERMINATED\x10\x05\x12'\n#ACTIVITY_EXECUTION_STATUS_TIMED_OUT\x10\x06\x12$\n ACTIVITY_EXECUTION_STATUS_PAUSED\x10\x07*\xd8\x01\n\x15\x41\x63tivityIdReusePolicy\x12(\n$ACTIVITY_ID_REUSE_POLICY_UNSPECIFIED\x10\x00\x12,\n(ACTIVITY_ID_REUSE_POLICY_ALLOW_DUPLICATE\x10\x01\x12\x38\n4ACTIVITY_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY\x10\x02\x12-\n)ACTIVITY_ID_REUSE_POLICY_REJECT_DUPLICATE\x10\x03*\x9b\x01\n\x18\x41\x63tivityIdConflictPolicy\x12+\n'ACTIVITY_ID_CONFLICT_POLICY_UNSPECIFIED\x10\x00\x12$\n ACTIVITY_ID_CONFLICT_POLICY_FAIL\x10\x01\x12,\n(ACTIVITY_ID_CONFLICT_POLICY_USE_EXISTING\x10\x02\x42\x85\x01\n\x18io.temporal.api.enums.v1B\rActivityProtoP\x01Z!go.temporal.io/api/enums/v1;enums\xaa\x02\x17Temporalio.Api.Enums.V1\xea\x02\x1aTemporalio::Api::Enums::V1b\x06proto3" ) _ACTIVITYEXECUTIONSTATUS = DESCRIPTOR.enum_types_by_name["ActivityExecutionStatus"] @@ -32,6 +32,7 @@ ACTIVITY_EXECUTION_STATUS_CANCELED = 4 ACTIVITY_EXECUTION_STATUS_TERMINATED = 5 ACTIVITY_EXECUTION_STATUS_TIMED_OUT = 6 +ACTIVITY_EXECUTION_STATUS_PAUSED = 7 ACTIVITY_ID_REUSE_POLICY_UNSPECIFIED = 0 ACTIVITY_ID_REUSE_POLICY_ALLOW_DUPLICATE = 1 ACTIVITY_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY = 2 @@ -45,9 +46,9 @@ DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b"\n\030io.temporal.api.enums.v1B\rActivityProtoP\001Z!go.temporal.io/api/enums/v1;enums\252\002\027Temporalio.Api.Enums.V1\352\002\032Temporalio::Api::Enums::V1" _ACTIVITYEXECUTIONSTATUS._serialized_start = 64 - _ACTIVITYEXECUTIONSTATUS._serialized_end = 373 - _ACTIVITYIDREUSEPOLICY._serialized_start = 376 - _ACTIVITYIDREUSEPOLICY._serialized_end = 592 - _ACTIVITYIDCONFLICTPOLICY._serialized_start = 595 - _ACTIVITYIDCONFLICTPOLICY._serialized_end = 750 + _ACTIVITYEXECUTIONSTATUS._serialized_end = 411 + _ACTIVITYIDREUSEPOLICY._serialized_start = 414 + _ACTIVITYIDREUSEPOLICY._serialized_end = 630 + _ACTIVITYIDCONFLICTPOLICY._serialized_start = 633 + _ACTIVITYIDCONFLICTPOLICY._serialized_end = 788 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/enums/v1/activity_pb2.pyi b/temporalio/api/enums/v1/activity_pb2.pyi index 73d846050..cadaedb37 100644 --- a/temporalio/api/enums/v1/activity_pb2.pyi +++ b/temporalio/api/enums/v1/activity_pb2.pyi @@ -62,13 +62,17 @@ class _ActivityExecutionStatusEnumTypeWrapper( reached when retry is blocked (RetryPolicy.maximum_attempts exhausted, SCHEDULE_TO_CLOSE would be exceeded, or cancellation has been requested). """ + ACTIVITY_EXECUTION_STATUS_PAUSED: _ActivityExecutionStatus.ValueType # 7 + """The activity is paused. Paused state is only reachable after calling + PauseActivityExecution on a standalone activity. + """ class ActivityExecutionStatus( _ActivityExecutionStatus, metaclass=_ActivityExecutionStatusEnumTypeWrapper ): """Status of a standalone activity. - The status is updated once, when the activity is originally scheduled, and again when the activity reaches a terminal - status. + The status is updated when the activity is originally scheduled, paused, unpaused, and when the + activity reaches a terminal state. (-- api-linter: core::0216::synonyms=disabled aip.dev/not-precedent: Named consistently with WorkflowExecutionStatus. --) """ @@ -107,6 +111,10 @@ ACTIVITY_EXECUTION_STATUS_TIMED_OUT: ActivityExecutionStatus.ValueType # 6 reached when retry is blocked (RetryPolicy.maximum_attempts exhausted, SCHEDULE_TO_CLOSE would be exceeded, or cancellation has been requested). """ +ACTIVITY_EXECUTION_STATUS_PAUSED: ActivityExecutionStatus.ValueType # 7 +"""The activity is paused. Paused state is only reachable after calling +PauseActivityExecution on a standalone activity. +""" global___ActivityExecutionStatus = ActivityExecutionStatus class _ActivityIdReusePolicy: diff --git a/temporalio/api/enums/v1/batch_operation_pb2.py b/temporalio/api/enums/v1/batch_operation_pb2.py index 60c9d7f2f..73d4e011b 100644 --- a/temporalio/api/enums/v1/batch_operation_pb2.py +++ b/temporalio/api/enums/v1/batch_operation_pb2.py @@ -16,7 +16,7 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b"\n+temporal/api/enums/v1/batch_operation.proto\x12\x15temporal.api.enums.v1*\x9a\x03\n\x12\x42\x61tchOperationType\x12$\n BATCH_OPERATION_TYPE_UNSPECIFIED\x10\x00\x12\"\n\x1e\x42\x41TCH_OPERATION_TYPE_TERMINATE\x10\x01\x12\x1f\n\x1b\x42\x41TCH_OPERATION_TYPE_CANCEL\x10\x02\x12\x1f\n\x1b\x42\x41TCH_OPERATION_TYPE_SIGNAL\x10\x03\x12\x1f\n\x1b\x42\x41TCH_OPERATION_TYPE_DELETE\x10\x04\x12\x1e\n\x1a\x42\x41TCH_OPERATION_TYPE_RESET\x10\x05\x12\x31\n-BATCH_OPERATION_TYPE_UPDATE_EXECUTION_OPTIONS\x10\x06\x12)\n%BATCH_OPERATION_TYPE_UNPAUSE_ACTIVITY\x10\x07\x12\x30\n,BATCH_OPERATION_TYPE_UPDATE_ACTIVITY_OPTIONS\x10\x08\x12'\n#BATCH_OPERATION_TYPE_RESET_ACTIVITY\x10\t*\xa6\x01\n\x13\x42\x61tchOperationState\x12%\n!BATCH_OPERATION_STATE_UNSPECIFIED\x10\x00\x12!\n\x1d\x42\x41TCH_OPERATION_STATE_RUNNING\x10\x01\x12#\n\x1f\x42\x41TCH_OPERATION_STATE_COMPLETED\x10\x02\x12 \n\x1c\x42\x41TCH_OPERATION_STATE_FAILED\x10\x03\x42\x8b\x01\n\x18io.temporal.api.enums.v1B\x13\x42\x61tchOperationProtoP\x01Z!go.temporal.io/api/enums/v1;enums\xaa\x02\x17Temporalio.Api.Enums.V1\xea\x02\x1aTemporalio::Api::Enums::V1b\x06proto3" + b"\n+temporal/api/enums/v1/batch_operation.proto\x12\x15temporal.api.enums.v1*\xc3\x06\n\x12\x42\x61tchOperationType\x12$\n BATCH_OPERATION_TYPE_UNSPECIFIED\x10\x00\x12&\n\x1e\x42\x41TCH_OPERATION_TYPE_TERMINATE\x10\x01\x1a\x02\x08\x01\x12+\n'BATCH_OPERATION_TYPE_TERMINATE_WORKFLOW\x10\r\x12#\n\x1b\x42\x41TCH_OPERATION_TYPE_CANCEL\x10\x02\x1a\x02\x08\x01\x12(\n$BATCH_OPERATION_TYPE_CANCEL_WORKFLOW\x10\x0e\x12#\n\x1b\x42\x41TCH_OPERATION_TYPE_SIGNAL\x10\x03\x1a\x02\x08\x01\x12(\n$BATCH_OPERATION_TYPE_SIGNAL_WORKFLOW\x10\x0f\x12#\n\x1b\x42\x41TCH_OPERATION_TYPE_DELETE\x10\x04\x1a\x02\x08\x01\x12(\n$BATCH_OPERATION_TYPE_DELETE_WORKFLOW\x10\x10\x12\"\n\x1a\x42\x41TCH_OPERATION_TYPE_RESET\x10\x05\x1a\x02\x08\x01\x12'\n#BATCH_OPERATION_TYPE_RESET_WORKFLOW\x10\x11\x12\x35\n-BATCH_OPERATION_TYPE_UPDATE_EXECUTION_OPTIONS\x10\x06\x1a\x02\x08\x01\x12:\n6BATCH_OPERATION_TYPE_UPDATE_WORKFLOW_EXECUTION_OPTIONS\x10\x12\x12)\n%BATCH_OPERATION_TYPE_UNPAUSE_ACTIVITY\x10\x07\x12\x30\n,BATCH_OPERATION_TYPE_UPDATE_ACTIVITY_OPTIONS\x10\x08\x12'\n#BATCH_OPERATION_TYPE_RESET_ACTIVITY\x10\t\x12+\n'BATCH_OPERATION_TYPE_TERMINATE_ACTIVITY\x10\n\x12(\n$BATCH_OPERATION_TYPE_CANCEL_ACTIVITY\x10\x0b\x12(\n$BATCH_OPERATION_TYPE_DELETE_ACTIVITY\x10\x0c*\xa6\x01\n\x13\x42\x61tchOperationState\x12%\n!BATCH_OPERATION_STATE_UNSPECIFIED\x10\x00\x12!\n\x1d\x42\x41TCH_OPERATION_STATE_RUNNING\x10\x01\x12#\n\x1f\x42\x41TCH_OPERATION_STATE_COMPLETED\x10\x02\x12 \n\x1c\x42\x41TCH_OPERATION_STATE_FAILED\x10\x03\x42\x8b\x01\n\x18io.temporal.api.enums.v1B\x13\x42\x61tchOperationProtoP\x01Z!go.temporal.io/api/enums/v1;enums\xaa\x02\x17Temporalio.Api.Enums.V1\xea\x02\x1aTemporalio::Api::Enums::V1b\x06proto3" ) _BATCHOPERATIONTYPE = DESCRIPTOR.enum_types_by_name["BatchOperationType"] @@ -25,14 +25,23 @@ BatchOperationState = enum_type_wrapper.EnumTypeWrapper(_BATCHOPERATIONSTATE) BATCH_OPERATION_TYPE_UNSPECIFIED = 0 BATCH_OPERATION_TYPE_TERMINATE = 1 +BATCH_OPERATION_TYPE_TERMINATE_WORKFLOW = 13 BATCH_OPERATION_TYPE_CANCEL = 2 +BATCH_OPERATION_TYPE_CANCEL_WORKFLOW = 14 BATCH_OPERATION_TYPE_SIGNAL = 3 +BATCH_OPERATION_TYPE_SIGNAL_WORKFLOW = 15 BATCH_OPERATION_TYPE_DELETE = 4 +BATCH_OPERATION_TYPE_DELETE_WORKFLOW = 16 BATCH_OPERATION_TYPE_RESET = 5 +BATCH_OPERATION_TYPE_RESET_WORKFLOW = 17 BATCH_OPERATION_TYPE_UPDATE_EXECUTION_OPTIONS = 6 +BATCH_OPERATION_TYPE_UPDATE_WORKFLOW_EXECUTION_OPTIONS = 18 BATCH_OPERATION_TYPE_UNPAUSE_ACTIVITY = 7 BATCH_OPERATION_TYPE_UPDATE_ACTIVITY_OPTIONS = 8 BATCH_OPERATION_TYPE_RESET_ACTIVITY = 9 +BATCH_OPERATION_TYPE_TERMINATE_ACTIVITY = 10 +BATCH_OPERATION_TYPE_CANCEL_ACTIVITY = 11 +BATCH_OPERATION_TYPE_DELETE_ACTIVITY = 12 BATCH_OPERATION_STATE_UNSPECIFIED = 0 BATCH_OPERATION_STATE_RUNNING = 1 BATCH_OPERATION_STATE_COMPLETED = 2 @@ -42,8 +51,34 @@ if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b"\n\030io.temporal.api.enums.v1B\023BatchOperationProtoP\001Z!go.temporal.io/api/enums/v1;enums\252\002\027Temporalio.Api.Enums.V1\352\002\032Temporalio::Api::Enums::V1" + _BATCHOPERATIONTYPE.values_by_name["BATCH_OPERATION_TYPE_TERMINATE"]._options = None + _BATCHOPERATIONTYPE.values_by_name[ + "BATCH_OPERATION_TYPE_TERMINATE" + ]._serialized_options = b"\010\001" + _BATCHOPERATIONTYPE.values_by_name["BATCH_OPERATION_TYPE_CANCEL"]._options = None + _BATCHOPERATIONTYPE.values_by_name[ + "BATCH_OPERATION_TYPE_CANCEL" + ]._serialized_options = b"\010\001" + _BATCHOPERATIONTYPE.values_by_name["BATCH_OPERATION_TYPE_SIGNAL"]._options = None + _BATCHOPERATIONTYPE.values_by_name[ + "BATCH_OPERATION_TYPE_SIGNAL" + ]._serialized_options = b"\010\001" + _BATCHOPERATIONTYPE.values_by_name["BATCH_OPERATION_TYPE_DELETE"]._options = None + _BATCHOPERATIONTYPE.values_by_name[ + "BATCH_OPERATION_TYPE_DELETE" + ]._serialized_options = b"\010\001" + _BATCHOPERATIONTYPE.values_by_name["BATCH_OPERATION_TYPE_RESET"]._options = None + _BATCHOPERATIONTYPE.values_by_name[ + "BATCH_OPERATION_TYPE_RESET" + ]._serialized_options = b"\010\001" + _BATCHOPERATIONTYPE.values_by_name[ + "BATCH_OPERATION_TYPE_UPDATE_EXECUTION_OPTIONS" + ]._options = None + _BATCHOPERATIONTYPE.values_by_name[ + "BATCH_OPERATION_TYPE_UPDATE_EXECUTION_OPTIONS" + ]._serialized_options = b"\010\001" _BATCHOPERATIONTYPE._serialized_start = 71 - _BATCHOPERATIONTYPE._serialized_end = 481 - _BATCHOPERATIONSTATE._serialized_start = 484 - _BATCHOPERATIONSTATE._serialized_end = 650 + _BATCHOPERATIONTYPE._serialized_end = 906 + _BATCHOPERATIONSTATE._serialized_start = 909 + _BATCHOPERATIONSTATE._serialized_end = 1075 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/enums/v1/batch_operation_pb2.pyi b/temporalio/api/enums/v1/batch_operation_pb2.pyi index 49f426ebb..156ba51ed 100644 --- a/temporalio/api/enums/v1/batch_operation_pb2.pyi +++ b/temporalio/api/enums/v1/batch_operation_pb2.pyi @@ -30,14 +30,31 @@ class _BatchOperationTypeEnumTypeWrapper( DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor BATCH_OPERATION_TYPE_UNSPECIFIED: _BatchOperationType.ValueType # 0 BATCH_OPERATION_TYPE_TERMINATE: _BatchOperationType.ValueType # 1 + """DEPRECATED: Use BATCH_OPERATION_TYPE_TERMINATE_WORKFLOW instead.""" + BATCH_OPERATION_TYPE_TERMINATE_WORKFLOW: _BatchOperationType.ValueType # 13 BATCH_OPERATION_TYPE_CANCEL: _BatchOperationType.ValueType # 2 + """DEPRECATED: Use BATCH_OPERATION_TYPE_CANCEL_WORKFLOW instead.""" + BATCH_OPERATION_TYPE_CANCEL_WORKFLOW: _BatchOperationType.ValueType # 14 BATCH_OPERATION_TYPE_SIGNAL: _BatchOperationType.ValueType # 3 + """DEPRECATED: Use BATCH_OPERATION_TYPE_SIGNAL_WORKFLOW instead.""" + BATCH_OPERATION_TYPE_SIGNAL_WORKFLOW: _BatchOperationType.ValueType # 15 BATCH_OPERATION_TYPE_DELETE: _BatchOperationType.ValueType # 4 + """DEPRECATED: Use BATCH_OPERATION_TYPE_DELETE_WORKFLOW instead.""" + BATCH_OPERATION_TYPE_DELETE_WORKFLOW: _BatchOperationType.ValueType # 16 BATCH_OPERATION_TYPE_RESET: _BatchOperationType.ValueType # 5 + """DEPRECATED: Use BATCH_OPERATION_TYPE_RESET_WORKFLOW instead.""" + BATCH_OPERATION_TYPE_RESET_WORKFLOW: _BatchOperationType.ValueType # 17 BATCH_OPERATION_TYPE_UPDATE_EXECUTION_OPTIONS: _BatchOperationType.ValueType # 6 + """DEPRECATED: Use BATCH_OPERATION_TYPE_UPDATE_WORKFLOW_EXECUTION_OPTIONS instead.""" + BATCH_OPERATION_TYPE_UPDATE_WORKFLOW_EXECUTION_OPTIONS: ( + _BatchOperationType.ValueType + ) # 18 BATCH_OPERATION_TYPE_UNPAUSE_ACTIVITY: _BatchOperationType.ValueType # 7 BATCH_OPERATION_TYPE_UPDATE_ACTIVITY_OPTIONS: _BatchOperationType.ValueType # 8 BATCH_OPERATION_TYPE_RESET_ACTIVITY: _BatchOperationType.ValueType # 9 + BATCH_OPERATION_TYPE_TERMINATE_ACTIVITY: _BatchOperationType.ValueType # 10 + BATCH_OPERATION_TYPE_CANCEL_ACTIVITY: _BatchOperationType.ValueType # 11 + BATCH_OPERATION_TYPE_DELETE_ACTIVITY: _BatchOperationType.ValueType # 12 class BatchOperationType( _BatchOperationType, metaclass=_BatchOperationTypeEnumTypeWrapper @@ -45,14 +62,31 @@ class BatchOperationType( BATCH_OPERATION_TYPE_UNSPECIFIED: BatchOperationType.ValueType # 0 BATCH_OPERATION_TYPE_TERMINATE: BatchOperationType.ValueType # 1 +"""DEPRECATED: Use BATCH_OPERATION_TYPE_TERMINATE_WORKFLOW instead.""" +BATCH_OPERATION_TYPE_TERMINATE_WORKFLOW: BatchOperationType.ValueType # 13 BATCH_OPERATION_TYPE_CANCEL: BatchOperationType.ValueType # 2 +"""DEPRECATED: Use BATCH_OPERATION_TYPE_CANCEL_WORKFLOW instead.""" +BATCH_OPERATION_TYPE_CANCEL_WORKFLOW: BatchOperationType.ValueType # 14 BATCH_OPERATION_TYPE_SIGNAL: BatchOperationType.ValueType # 3 +"""DEPRECATED: Use BATCH_OPERATION_TYPE_SIGNAL_WORKFLOW instead.""" +BATCH_OPERATION_TYPE_SIGNAL_WORKFLOW: BatchOperationType.ValueType # 15 BATCH_OPERATION_TYPE_DELETE: BatchOperationType.ValueType # 4 +"""DEPRECATED: Use BATCH_OPERATION_TYPE_DELETE_WORKFLOW instead.""" +BATCH_OPERATION_TYPE_DELETE_WORKFLOW: BatchOperationType.ValueType # 16 BATCH_OPERATION_TYPE_RESET: BatchOperationType.ValueType # 5 +"""DEPRECATED: Use BATCH_OPERATION_TYPE_RESET_WORKFLOW instead.""" +BATCH_OPERATION_TYPE_RESET_WORKFLOW: BatchOperationType.ValueType # 17 BATCH_OPERATION_TYPE_UPDATE_EXECUTION_OPTIONS: BatchOperationType.ValueType # 6 +"""DEPRECATED: Use BATCH_OPERATION_TYPE_UPDATE_WORKFLOW_EXECUTION_OPTIONS instead.""" +BATCH_OPERATION_TYPE_UPDATE_WORKFLOW_EXECUTION_OPTIONS: ( + BatchOperationType.ValueType +) # 18 BATCH_OPERATION_TYPE_UNPAUSE_ACTIVITY: BatchOperationType.ValueType # 7 BATCH_OPERATION_TYPE_UPDATE_ACTIVITY_OPTIONS: BatchOperationType.ValueType # 8 BATCH_OPERATION_TYPE_RESET_ACTIVITY: BatchOperationType.ValueType # 9 +BATCH_OPERATION_TYPE_TERMINATE_ACTIVITY: BatchOperationType.ValueType # 10 +BATCH_OPERATION_TYPE_CANCEL_ACTIVITY: BatchOperationType.ValueType # 11 +BATCH_OPERATION_TYPE_DELETE_ACTIVITY: BatchOperationType.ValueType # 12 global___BatchOperationType = BatchOperationType class _BatchOperationState: diff --git a/temporalio/api/enums/v1/common_pb2.py b/temporalio/api/enums/v1/common_pb2.py index 557ce7631..a646df4ad 100644 --- a/temporalio/api/enums/v1/common_pb2.py +++ b/temporalio/api/enums/v1/common_pb2.py @@ -16,7 +16,7 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b"\n\"temporal/api/enums/v1/common.proto\x12\x15temporal.api.enums.v1*_\n\x0c\x45ncodingType\x12\x1d\n\x19\x45NCODING_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14\x45NCODING_TYPE_PROTO3\x10\x01\x12\x16\n\x12\x45NCODING_TYPE_JSON\x10\x02*\x91\x02\n\x10IndexedValueType\x12\"\n\x1eINDEXED_VALUE_TYPE_UNSPECIFIED\x10\x00\x12\x1b\n\x17INDEXED_VALUE_TYPE_TEXT\x10\x01\x12\x1e\n\x1aINDEXED_VALUE_TYPE_KEYWORD\x10\x02\x12\x1a\n\x16INDEXED_VALUE_TYPE_INT\x10\x03\x12\x1d\n\x19INDEXED_VALUE_TYPE_DOUBLE\x10\x04\x12\x1b\n\x17INDEXED_VALUE_TYPE_BOOL\x10\x05\x12\x1f\n\x1bINDEXED_VALUE_TYPE_DATETIME\x10\x06\x12#\n\x1fINDEXED_VALUE_TYPE_KEYWORD_LIST\x10\x07*^\n\x08Severity\x12\x18\n\x14SEVERITY_UNSPECIFIED\x10\x00\x12\x11\n\rSEVERITY_HIGH\x10\x01\x12\x13\n\x0fSEVERITY_MEDIUM\x10\x02\x12\x10\n\x0cSEVERITY_LOW\x10\x03*\xde\x01\n\rCallbackState\x12\x1e\n\x1a\x43\x41LLBACK_STATE_UNSPECIFIED\x10\x00\x12\x1a\n\x16\x43\x41LLBACK_STATE_STANDBY\x10\x01\x12\x1c\n\x18\x43\x41LLBACK_STATE_SCHEDULED\x10\x02\x12\x1e\n\x1a\x43\x41LLBACK_STATE_BACKING_OFF\x10\x03\x12\x19\n\x15\x43\x41LLBACK_STATE_FAILED\x10\x04\x12\x1c\n\x18\x43\x41LLBACK_STATE_SUCCEEDED\x10\x05\x12\x1a\n\x16\x43\x41LLBACK_STATE_BLOCKED\x10\x06*\xfd\x01\n\x1aPendingNexusOperationState\x12-\n)PENDING_NEXUS_OPERATION_STATE_UNSPECIFIED\x10\x00\x12+\n'PENDING_NEXUS_OPERATION_STATE_SCHEDULED\x10\x01\x12-\n)PENDING_NEXUS_OPERATION_STATE_BACKING_OFF\x10\x02\x12)\n%PENDING_NEXUS_OPERATION_STATE_STARTED\x10\x03\x12)\n%PENDING_NEXUS_OPERATION_STATE_BLOCKED\x10\x04*\xfe\x02\n\x1fNexusOperationCancellationState\x12\x32\n.NEXUS_OPERATION_CANCELLATION_STATE_UNSPECIFIED\x10\x00\x12\x30\n,NEXUS_OPERATION_CANCELLATION_STATE_SCHEDULED\x10\x01\x12\x32\n.NEXUS_OPERATION_CANCELLATION_STATE_BACKING_OFF\x10\x02\x12\x30\n,NEXUS_OPERATION_CANCELLATION_STATE_SUCCEEDED\x10\x03\x12-\n)NEXUS_OPERATION_CANCELLATION_STATE_FAILED\x10\x04\x12\x30\n,NEXUS_OPERATION_CANCELLATION_STATE_TIMED_OUT\x10\x05\x12.\n*NEXUS_OPERATION_CANCELLATION_STATE_BLOCKED\x10\x06*\x97\x01\n\x17WorkflowRuleActionScope\x12*\n&WORKFLOW_RULE_ACTION_SCOPE_UNSPECIFIED\x10\x00\x12'\n#WORKFLOW_RULE_ACTION_SCOPE_WORKFLOW\x10\x01\x12'\n#WORKFLOW_RULE_ACTION_SCOPE_ACTIVITY\x10\x02*m\n\x18\x41pplicationErrorCategory\x12*\n&APPLICATION_ERROR_CATEGORY_UNSPECIFIED\x10\x00\x12%\n!APPLICATION_ERROR_CATEGORY_BENIGN\x10\x01*\x85\x01\n\x0cWorkerStatus\x12\x1d\n\x19WORKER_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15WORKER_STATUS_RUNNING\x10\x01\x12\x1f\n\x1bWORKER_STATUS_SHUTTING_DOWN\x10\x02\x12\x1a\n\x16WORKER_STATUS_SHUTDOWN\x10\x03\x42\x83\x01\n\x18io.temporal.api.enums.v1B\x0b\x43ommonProtoP\x01Z!go.temporal.io/api/enums/v1;enums\xaa\x02\x17Temporalio.Api.Enums.V1\xea\x02\x1aTemporalio::Api::Enums::V1b\x06proto3" + b"\n\"temporal/api/enums/v1/common.proto\x12\x15temporal.api.enums.v1*_\n\x0c\x45ncodingType\x12\x1d\n\x19\x45NCODING_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14\x45NCODING_TYPE_PROTO3\x10\x01\x12\x16\n\x12\x45NCODING_TYPE_JSON\x10\x02*\x91\x02\n\x10IndexedValueType\x12\"\n\x1eINDEXED_VALUE_TYPE_UNSPECIFIED\x10\x00\x12\x1b\n\x17INDEXED_VALUE_TYPE_TEXT\x10\x01\x12\x1e\n\x1aINDEXED_VALUE_TYPE_KEYWORD\x10\x02\x12\x1a\n\x16INDEXED_VALUE_TYPE_INT\x10\x03\x12\x1d\n\x19INDEXED_VALUE_TYPE_DOUBLE\x10\x04\x12\x1b\n\x17INDEXED_VALUE_TYPE_BOOL\x10\x05\x12\x1f\n\x1bINDEXED_VALUE_TYPE_DATETIME\x10\x06\x12#\n\x1fINDEXED_VALUE_TYPE_KEYWORD_LIST\x10\x07*^\n\x08Severity\x12\x18\n\x14SEVERITY_UNSPECIFIED\x10\x00\x12\x11\n\rSEVERITY_HIGH\x10\x01\x12\x13\n\x0fSEVERITY_MEDIUM\x10\x02\x12\x10\n\x0cSEVERITY_LOW\x10\x03*\xde\x01\n\rCallbackState\x12\x1e\n\x1a\x43\x41LLBACK_STATE_UNSPECIFIED\x10\x00\x12\x1a\n\x16\x43\x41LLBACK_STATE_STANDBY\x10\x01\x12\x1c\n\x18\x43\x41LLBACK_STATE_SCHEDULED\x10\x02\x12\x1e\n\x1a\x43\x41LLBACK_STATE_BACKING_OFF\x10\x03\x12\x19\n\x15\x43\x41LLBACK_STATE_FAILED\x10\x04\x12\x1c\n\x18\x43\x41LLBACK_STATE_SUCCEEDED\x10\x05\x12\x1a\n\x16\x43\x41LLBACK_STATE_BLOCKED\x10\x06*\xfd\x01\n\x1aPendingNexusOperationState\x12-\n)PENDING_NEXUS_OPERATION_STATE_UNSPECIFIED\x10\x00\x12+\n'PENDING_NEXUS_OPERATION_STATE_SCHEDULED\x10\x01\x12-\n)PENDING_NEXUS_OPERATION_STATE_BACKING_OFF\x10\x02\x12)\n%PENDING_NEXUS_OPERATION_STATE_STARTED\x10\x03\x12)\n%PENDING_NEXUS_OPERATION_STATE_BLOCKED\x10\x04*\xfe\x02\n\x1fNexusOperationCancellationState\x12\x32\n.NEXUS_OPERATION_CANCELLATION_STATE_UNSPECIFIED\x10\x00\x12\x30\n,NEXUS_OPERATION_CANCELLATION_STATE_SCHEDULED\x10\x01\x12\x32\n.NEXUS_OPERATION_CANCELLATION_STATE_BACKING_OFF\x10\x02\x12\x30\n,NEXUS_OPERATION_CANCELLATION_STATE_SUCCEEDED\x10\x03\x12-\n)NEXUS_OPERATION_CANCELLATION_STATE_FAILED\x10\x04\x12\x30\n,NEXUS_OPERATION_CANCELLATION_STATE_TIMED_OUT\x10\x05\x12.\n*NEXUS_OPERATION_CANCELLATION_STATE_BLOCKED\x10\x06*\x97\x01\n\x17WorkflowRuleActionScope\x12*\n&WORKFLOW_RULE_ACTION_SCOPE_UNSPECIFIED\x10\x00\x12'\n#WORKFLOW_RULE_ACTION_SCOPE_WORKFLOW\x10\x01\x12'\n#WORKFLOW_RULE_ACTION_SCOPE_ACTIVITY\x10\x02*m\n\x18\x41pplicationErrorCategory\x12*\n&APPLICATION_ERROR_CATEGORY_UNSPECIFIED\x10\x00\x12%\n!APPLICATION_ERROR_CATEGORY_BENIGN\x10\x01*\x85\x01\n\x0cWorkerStatus\x12\x1d\n\x19WORKER_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15WORKER_STATUS_RUNNING\x10\x01\x12\x1f\n\x1bWORKER_STATUS_SHUTTING_DOWN\x10\x02\x12\x1a\n\x16WORKER_STATUS_SHUTDOWN\x10\x03*i\n\rExecutionType\x12\x1e\n\x1a\x45XECUTION_TYPE_UNSPECIFIED\x10\x00\x12\x1b\n\x17\x45XECUTION_TYPE_WORKFLOW\x10\x01\x12\x1b\n\x17\x45XECUTION_TYPE_ACTIVITY\x10\x02\x42\x83\x01\n\x18io.temporal.api.enums.v1B\x0b\x43ommonProtoP\x01Z!go.temporal.io/api/enums/v1;enums\xaa\x02\x17Temporalio.Api.Enums.V1\xea\x02\x1aTemporalio::Api::Enums::V1b\x06proto3" ) _ENCODINGTYPE = DESCRIPTOR.enum_types_by_name["EncodingType"] @@ -45,6 +45,8 @@ ApplicationErrorCategory = enum_type_wrapper.EnumTypeWrapper(_APPLICATIONERRORCATEGORY) _WORKERSTATUS = DESCRIPTOR.enum_types_by_name["WorkerStatus"] WorkerStatus = enum_type_wrapper.EnumTypeWrapper(_WORKERSTATUS) +_EXECUTIONTYPE = DESCRIPTOR.enum_types_by_name["ExecutionType"] +ExecutionType = enum_type_wrapper.EnumTypeWrapper(_EXECUTIONTYPE) ENCODING_TYPE_UNSPECIFIED = 0 ENCODING_TYPE_PROTO3 = 1 ENCODING_TYPE_JSON = 2 @@ -88,6 +90,9 @@ WORKER_STATUS_RUNNING = 1 WORKER_STATUS_SHUTTING_DOWN = 2 WORKER_STATUS_SHUTDOWN = 3 +EXECUTION_TYPE_UNSPECIFIED = 0 +EXECUTION_TYPE_WORKFLOW = 1 +EXECUTION_TYPE_ACTIVITY = 2 if _descriptor._USE_C_DESCRIPTORS == False: @@ -111,4 +116,6 @@ _APPLICATIONERRORCATEGORY._serialized_end = 1659 _WORKERSTATUS._serialized_start = 1662 _WORKERSTATUS._serialized_end = 1795 + _EXECUTIONTYPE._serialized_start = 1797 + _EXECUTIONTYPE._serialized_end = 1902 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/enums/v1/common_pb2.pyi b/temporalio/api/enums/v1/common_pb2.pyi index 47e470295..47b6d030b 100644 --- a/temporalio/api/enums/v1/common_pb2.pyi +++ b/temporalio/api/enums/v1/common_pb2.pyi @@ -339,3 +339,29 @@ WORKER_STATUS_RUNNING: WorkerStatus.ValueType # 1 WORKER_STATUS_SHUTTING_DOWN: WorkerStatus.ValueType # 2 WORKER_STATUS_SHUTDOWN: WorkerStatus.ValueType # 3 global___WorkerStatus = WorkerStatus + +class _ExecutionType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ExecutionTypeEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ + _ExecutionType.ValueType + ], + builtins.type, +): # noqa: F821 + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + EXECUTION_TYPE_UNSPECIFIED: _ExecutionType.ValueType # 0 + EXECUTION_TYPE_WORKFLOW: _ExecutionType.ValueType # 1 + """A workflow execution archetype.""" + EXECUTION_TYPE_ACTIVITY: _ExecutionType.ValueType # 2 + """An activity execution archetype. This is reserved for standalone activities.""" + +class ExecutionType(_ExecutionType, metaclass=_ExecutionTypeEnumTypeWrapper): ... + +EXECUTION_TYPE_UNSPECIFIED: ExecutionType.ValueType # 0 +EXECUTION_TYPE_WORKFLOW: ExecutionType.ValueType # 1 +"""A workflow execution archetype.""" +EXECUTION_TYPE_ACTIVITY: ExecutionType.ValueType # 2 +"""An activity execution archetype. This is reserved for standalone activities.""" +global___ExecutionType = ExecutionType diff --git a/temporalio/api/enums/v1/failed_cause_pb2.py b/temporalio/api/enums/v1/failed_cause_pb2.py index 141a06fcd..03eb094f9 100644 --- a/temporalio/api/enums/v1/failed_cause_pb2.py +++ b/temporalio/api/enums/v1/failed_cause_pb2.py @@ -16,7 +16,7 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n(temporal/api/enums/v1/failed_cause.proto\x12\x15temporal.api.enums.v1*\xd8\x12\n\x17WorkflowTaskFailedCause\x12*\n&WORKFLOW_TASK_FAILED_CAUSE_UNSPECIFIED\x10\x00\x12\x30\n,WORKFLOW_TASK_FAILED_CAUSE_UNHANDLED_COMMAND\x10\x01\x12?\n;WORKFLOW_TASK_FAILED_CAUSE_BAD_SCHEDULE_ACTIVITY_ATTRIBUTES\x10\x02\x12\x45\nAWORKFLOW_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_ACTIVITY_ATTRIBUTES\x10\x03\x12\x39\n5WORKFLOW_TASK_FAILED_CAUSE_BAD_START_TIMER_ATTRIBUTES\x10\x04\x12:\n6WORKFLOW_TASK_FAILED_CAUSE_BAD_CANCEL_TIMER_ATTRIBUTES\x10\x05\x12;\n7WORKFLOW_TASK_FAILED_CAUSE_BAD_RECORD_MARKER_ATTRIBUTES\x10\x06\x12I\nEWORKFLOW_TASK_FAILED_CAUSE_BAD_COMPLETE_WORKFLOW_EXECUTION_ATTRIBUTES\x10\x07\x12\x45\nAWORKFLOW_TASK_FAILED_CAUSE_BAD_FAIL_WORKFLOW_EXECUTION_ATTRIBUTES\x10\x08\x12G\nCWORKFLOW_TASK_FAILED_CAUSE_BAD_CANCEL_WORKFLOW_EXECUTION_ATTRIBUTES\x10\t\x12X\nTWORKFLOW_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_ATTRIBUTES\x10\n\x12=\n9WORKFLOW_TASK_FAILED_CAUSE_BAD_CONTINUE_AS_NEW_ATTRIBUTES\x10\x0b\x12\x37\n3WORKFLOW_TASK_FAILED_CAUSE_START_TIMER_DUPLICATE_ID\x10\x0c\x12\x36\n2WORKFLOW_TASK_FAILED_CAUSE_RESET_STICKY_TASK_QUEUE\x10\r\x12@\n= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _FastForwardPollingResult: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _FastForwardPollingResultEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ + _FastForwardPollingResult.ValueType + ], + builtins.type, +): # noqa: F821 + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + FAST_FORWARD_POLLING_RESULT_UNSPECIFIED: _FastForwardPollingResult.ValueType # 0 + """Never returned; guards against an unset result.""" + FAST_FORWARD_POLLING_RESULT_POLL_TIMEOUT: _FastForwardPollingResult.ValueType # 1 + """The poll timed out server-side before the fast-forward completed. The caller may poll again.""" + FAST_FORWARD_POLLING_RESULT_FAST_FORWARD_COMPLETED: ( + _FastForwardPollingResult.ValueType + ) # 2 + """The fast-forward identified by the request's `fast_forward_id` reached its target time and completed.""" + FAST_FORWARD_POLLING_RESULT_FAST_FORWARD_FAILED: ( + _FastForwardPollingResult.ValueType + ) # 3 + """The fast-forward can no longer complete, which usually indicates improper usage of + fast-forward on the client side. Possible reasons: the `fast_forward_id` does not match + the execution's current fast-forward, the execution ended before the fast-forward + completed, the fast-forward config was updated while the poll was in flight, etc. + See `failed_reason` in the response for the specific cause. + """ + +class FastForwardPollingResult( + _FastForwardPollingResult, metaclass=_FastForwardPollingResultEnumTypeWrapper +): + """FastForwardPollingResult is the result of polling and waiting for a fast-forward to complete + on a time-skipping execution. + FAST_FORWARD_POLLING_RESULT_POLL_TIMEOUT and FAST_FORWARD_POLLING_RESULT_FAST_FORWARD_COMPLETED + are the normal poll outcomes; FAST_FORWARD_POLLING_RESULT_FAST_FORWARD_FAILED means the + fast-forward can no longer complete. + """ + +FAST_FORWARD_POLLING_RESULT_UNSPECIFIED: FastForwardPollingResult.ValueType # 0 +"""Never returned; guards against an unset result.""" +FAST_FORWARD_POLLING_RESULT_POLL_TIMEOUT: FastForwardPollingResult.ValueType # 1 +"""The poll timed out server-side before the fast-forward completed. The caller may poll again.""" +FAST_FORWARD_POLLING_RESULT_FAST_FORWARD_COMPLETED: ( + FastForwardPollingResult.ValueType +) # 2 +"""The fast-forward identified by the request's `fast_forward_id` reached its target time and completed.""" +FAST_FORWARD_POLLING_RESULT_FAST_FORWARD_FAILED: FastForwardPollingResult.ValueType # 3 +"""The fast-forward can no longer complete, which usually indicates improper usage of +fast-forward on the client side. Possible reasons: the `fast_forward_id` does not match +the execution's current fast-forward, the execution ended before the fast-forward +completed, the fast-forward config was updated while the poll was in flight, etc. +See `failed_reason` in the response for the specific cause. +""" +global___FastForwardPollingResult = FastForwardPollingResult diff --git a/temporalio/api/errordetails/v1/__init__.py b/temporalio/api/errordetails/v1/__init__.py index 01a078bdb..068503e9e 100644 --- a/temporalio/api/errordetails/v1/__init__.py +++ b/temporalio/api/errordetails/v1/__init__.py @@ -18,6 +18,7 @@ SystemWorkflowFailure, WorkflowExecutionAlreadyStartedFailure, WorkflowNotReadyFailure, + WorkflowTaskCompletionBufferLostFailure, ) __all__ = [ @@ -40,4 +41,5 @@ "SystemWorkflowFailure", "WorkflowExecutionAlreadyStartedFailure", "WorkflowNotReadyFailure", + "WorkflowTaskCompletionBufferLostFailure", ] diff --git a/temporalio/api/errordetails/v1/message_pb2.py b/temporalio/api/errordetails/v1/message_pb2.py index 3c5ec075e..22c58484c 100644 --- a/temporalio/api/errordetails/v1/message_pb2.py +++ b/temporalio/api/errordetails/v1/message_pb2.py @@ -30,7 +30,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n*temporal/api/errordetails/v1/message.proto\x12\x1ctemporal.api.errordetails.v1\x1a\x19google/protobuf/any.proto\x1a$temporal/api/common/v1/message.proto\x1a(temporal/api/enums/v1/failed_cause.proto\x1a%temporal/api/enums/v1/namespace.proto\x1a%temporal/api/failure/v1/message.proto"B\n\x0fNotFoundFailure\x12\x17\n\x0f\x63urrent_cluster\x18\x01 \x01(\t\x12\x16\n\x0e\x61\x63tive_cluster\x18\x02 \x01(\t"R\n&WorkflowExecutionAlreadyStartedFailure\x12\x18\n\x10start_request_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t"_\n\x19NamespaceNotActiveFailure\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x63urrent_cluster\x18\x02 \x01(\t\x12\x16\n\x0e\x61\x63tive_cluster\x18\x03 \x01(\t"0\n\x1bNamespaceUnavailableFailure\x12\x11\n\tnamespace\x18\x01 \x01(\t"\xa6\x01\n\x1cNamespaceInvalidStateFailure\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x34\n\x05state\x18\x02 \x01(\x0e\x32%.temporal.api.enums.v1.NamespaceState\x12=\n\x0e\x61llowed_states\x18\x03 \x03(\x0e\x32%.temporal.api.enums.v1.NamespaceState"-\n\x18NamespaceNotFoundFailure\x12\x11\n\tnamespace\x18\x01 \x01(\t"\x1f\n\x1dNamespaceAlreadyExistsFailure"k\n ClientVersionNotSupportedFailure\x12\x16\n\x0e\x63lient_version\x18\x01 \x01(\t\x12\x13\n\x0b\x63lient_name\x18\x02 \x01(\t\x12\x1a\n\x12supported_versions\x18\x03 \x01(\t"d\n ServerVersionNotSupportedFailure\x12\x16\n\x0eserver_version\x18\x01 \x01(\t\x12(\n client_supported_server_versions\x18\x02 \x01(\t"%\n#CancellationAlreadyRequestedFailure"G\n\x12QueryFailedFailure\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure")\n\x17PermissionDeniedFailure\x12\x0e\n\x06reason\x18\x01 \x01(\t"\x96\x01\n\x18ResourceExhaustedFailure\x12<\n\x05\x63\x61use\x18\x01 \x01(\x0e\x32-.temporal.api.enums.v1.ResourceExhaustedCause\x12<\n\x05scope\x18\x02 \x01(\x0e\x32-.temporal.api.enums.v1.ResourceExhaustedScope"v\n\x15SystemWorkflowFailure\x12\x45\n\x12workflow_execution\x18\x01 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x16\n\x0eworkflow_error\x18\x02 \x01(\t"\x19\n\x17WorkflowNotReadyFailure"3\n\x17NewerBuildExistsFailure\x12\x18\n\x10\x64\x65\x66\x61ult_build_id\x18\x01 \x01(\t"\xd9\x01\n\x1eMultiOperationExecutionFailure\x12^\n\x08statuses\x18\x01 \x03(\x0b\x32L.temporal.api.errordetails.v1.MultiOperationExecutionFailure.OperationStatus\x1aW\n\x0fOperationStatus\x12\x0c\n\x04\x63ode\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\x12%\n\x07\x64\x65tails\x18\x03 \x03(\x0b\x32\x14.google.protobuf.Any"R\n&ActivityExecutionAlreadyStartedFailure\x12\x18\n\x10start_request_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t"X\n,NexusOperationExecutionAlreadyStartedFailure\x12\x18\n\x10start_request_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\tB\xa7\x01\n\x1fio.temporal.api.errordetails.v1B\x0cMessageProtoP\x01Z/go.temporal.io/api/errordetails/v1;errordetails\xaa\x02\x1eTemporalio.Api.ErrorDetails.V1\xea\x02!Temporalio::Api::ErrorDetails::V1b\x06proto3' + b'\n*temporal/api/errordetails/v1/message.proto\x12\x1ctemporal.api.errordetails.v1\x1a\x19google/protobuf/any.proto\x1a$temporal/api/common/v1/message.proto\x1a(temporal/api/enums/v1/failed_cause.proto\x1a%temporal/api/enums/v1/namespace.proto\x1a%temporal/api/failure/v1/message.proto"B\n\x0fNotFoundFailure\x12\x17\n\x0f\x63urrent_cluster\x18\x01 \x01(\t\x12\x16\n\x0e\x61\x63tive_cluster\x18\x02 \x01(\t"r\n&WorkflowExecutionAlreadyStartedFailure\x12\x18\n\x10start_request_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12\x1e\n\x16\x66irst_execution_run_id\x18\x03 \x01(\t"_\n\x19NamespaceNotActiveFailure\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x63urrent_cluster\x18\x02 \x01(\t\x12\x16\n\x0e\x61\x63tive_cluster\x18\x03 \x01(\t"0\n\x1bNamespaceUnavailableFailure\x12\x11\n\tnamespace\x18\x01 \x01(\t"\xa6\x01\n\x1cNamespaceInvalidStateFailure\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x34\n\x05state\x18\x02 \x01(\x0e\x32%.temporal.api.enums.v1.NamespaceState\x12=\n\x0e\x61llowed_states\x18\x03 \x03(\x0e\x32%.temporal.api.enums.v1.NamespaceState"-\n\x18NamespaceNotFoundFailure\x12\x11\n\tnamespace\x18\x01 \x01(\t"\x1f\n\x1dNamespaceAlreadyExistsFailure"k\n ClientVersionNotSupportedFailure\x12\x16\n\x0e\x63lient_version\x18\x01 \x01(\t\x12\x13\n\x0b\x63lient_name\x18\x02 \x01(\t\x12\x1a\n\x12supported_versions\x18\x03 \x01(\t"d\n ServerVersionNotSupportedFailure\x12\x16\n\x0eserver_version\x18\x01 \x01(\t\x12(\n client_supported_server_versions\x18\x02 \x01(\t"%\n#CancellationAlreadyRequestedFailure"G\n\x12QueryFailedFailure\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure")\n\x17PermissionDeniedFailure\x12\x0e\n\x06reason\x18\x01 \x01(\t"\x96\x01\n\x18ResourceExhaustedFailure\x12<\n\x05\x63\x61use\x18\x01 \x01(\x0e\x32-.temporal.api.enums.v1.ResourceExhaustedCause\x12<\n\x05scope\x18\x02 \x01(\x0e\x32-.temporal.api.enums.v1.ResourceExhaustedScope"v\n\x15SystemWorkflowFailure\x12\x45\n\x12workflow_execution\x18\x01 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x16\n\x0eworkflow_error\x18\x02 \x01(\t"\x19\n\x17WorkflowNotReadyFailure"3\n\x17NewerBuildExistsFailure\x12\x18\n\x10\x64\x65\x66\x61ult_build_id\x18\x01 \x01(\t"\xd9\x01\n\x1eMultiOperationExecutionFailure\x12^\n\x08statuses\x18\x01 \x03(\x0b\x32L.temporal.api.errordetails.v1.MultiOperationExecutionFailure.OperationStatus\x1aW\n\x0fOperationStatus\x12\x0c\n\x04\x63ode\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\x12%\n\x07\x64\x65tails\x18\x03 \x03(\x0b\x32\x14.google.protobuf.Any"R\n&ActivityExecutionAlreadyStartedFailure\x12\x18\n\x10start_request_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t"X\n,NexusOperationExecutionAlreadyStartedFailure\x12\x18\n\x10start_request_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t")\n\'WorkflowTaskCompletionBufferLostFailureB\xa7\x01\n\x1fio.temporal.api.errordetails.v1B\x0cMessageProtoP\x01Z/go.temporal.io/api/errordetails/v1;errordetails\xaa\x02\x1eTemporalio.Api.ErrorDetails.V1\xea\x02!Temporalio::Api::ErrorDetails::V1b\x06proto3' ) @@ -78,6 +78,9 @@ _NEXUSOPERATIONEXECUTIONALREADYSTARTEDFAILURE = DESCRIPTOR.message_types_by_name[ "NexusOperationExecutionAlreadyStartedFailure" ] +_WORKFLOWTASKCOMPLETIONBUFFERLOSTFAILURE = DESCRIPTOR.message_types_by_name[ + "WorkflowTaskCompletionBufferLostFailure" +] NotFoundFailure = _reflection.GeneratedProtocolMessageType( "NotFoundFailure", (_message.Message,), @@ -297,47 +300,60 @@ ) _sym_db.RegisterMessage(NexusOperationExecutionAlreadyStartedFailure) +WorkflowTaskCompletionBufferLostFailure = _reflection.GeneratedProtocolMessageType( + "WorkflowTaskCompletionBufferLostFailure", + (_message.Message,), + { + "DESCRIPTOR": _WORKFLOWTASKCOMPLETIONBUFFERLOSTFAILURE, + "__module__": "temporalio.api.errordetails.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.errordetails.v1.WorkflowTaskCompletionBufferLostFailure) + }, +) +_sym_db.RegisterMessage(WorkflowTaskCompletionBufferLostFailure) + if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b"\n\037io.temporal.api.errordetails.v1B\014MessageProtoP\001Z/go.temporal.io/api/errordetails/v1;errordetails\252\002\036Temporalio.Api.ErrorDetails.V1\352\002!Temporalio::Api::ErrorDetails::V1" _NOTFOUNDFAILURE._serialized_start = 261 _NOTFOUNDFAILURE._serialized_end = 327 _WORKFLOWEXECUTIONALREADYSTARTEDFAILURE._serialized_start = 329 - _WORKFLOWEXECUTIONALREADYSTARTEDFAILURE._serialized_end = 411 - _NAMESPACENOTACTIVEFAILURE._serialized_start = 413 - _NAMESPACENOTACTIVEFAILURE._serialized_end = 508 - _NAMESPACEUNAVAILABLEFAILURE._serialized_start = 510 - _NAMESPACEUNAVAILABLEFAILURE._serialized_end = 558 - _NAMESPACEINVALIDSTATEFAILURE._serialized_start = 561 - _NAMESPACEINVALIDSTATEFAILURE._serialized_end = 727 - _NAMESPACENOTFOUNDFAILURE._serialized_start = 729 - _NAMESPACENOTFOUNDFAILURE._serialized_end = 774 - _NAMESPACEALREADYEXISTSFAILURE._serialized_start = 776 - _NAMESPACEALREADYEXISTSFAILURE._serialized_end = 807 - _CLIENTVERSIONNOTSUPPORTEDFAILURE._serialized_start = 809 - _CLIENTVERSIONNOTSUPPORTEDFAILURE._serialized_end = 916 - _SERVERVERSIONNOTSUPPORTEDFAILURE._serialized_start = 918 - _SERVERVERSIONNOTSUPPORTEDFAILURE._serialized_end = 1018 - _CANCELLATIONALREADYREQUESTEDFAILURE._serialized_start = 1020 - _CANCELLATIONALREADYREQUESTEDFAILURE._serialized_end = 1057 - _QUERYFAILEDFAILURE._serialized_start = 1059 - _QUERYFAILEDFAILURE._serialized_end = 1130 - _PERMISSIONDENIEDFAILURE._serialized_start = 1132 - _PERMISSIONDENIEDFAILURE._serialized_end = 1173 - _RESOURCEEXHAUSTEDFAILURE._serialized_start = 1176 - _RESOURCEEXHAUSTEDFAILURE._serialized_end = 1326 - _SYSTEMWORKFLOWFAILURE._serialized_start = 1328 - _SYSTEMWORKFLOWFAILURE._serialized_end = 1446 - _WORKFLOWNOTREADYFAILURE._serialized_start = 1448 - _WORKFLOWNOTREADYFAILURE._serialized_end = 1473 - _NEWERBUILDEXISTSFAILURE._serialized_start = 1475 - _NEWERBUILDEXISTSFAILURE._serialized_end = 1526 - _MULTIOPERATIONEXECUTIONFAILURE._serialized_start = 1529 - _MULTIOPERATIONEXECUTIONFAILURE._serialized_end = 1746 - _MULTIOPERATIONEXECUTIONFAILURE_OPERATIONSTATUS._serialized_start = 1659 - _MULTIOPERATIONEXECUTIONFAILURE_OPERATIONSTATUS._serialized_end = 1746 - _ACTIVITYEXECUTIONALREADYSTARTEDFAILURE._serialized_start = 1748 - _ACTIVITYEXECUTIONALREADYSTARTEDFAILURE._serialized_end = 1830 - _NEXUSOPERATIONEXECUTIONALREADYSTARTEDFAILURE._serialized_start = 1832 - _NEXUSOPERATIONEXECUTIONALREADYSTARTEDFAILURE._serialized_end = 1920 + _WORKFLOWEXECUTIONALREADYSTARTEDFAILURE._serialized_end = 443 + _NAMESPACENOTACTIVEFAILURE._serialized_start = 445 + _NAMESPACENOTACTIVEFAILURE._serialized_end = 540 + _NAMESPACEUNAVAILABLEFAILURE._serialized_start = 542 + _NAMESPACEUNAVAILABLEFAILURE._serialized_end = 590 + _NAMESPACEINVALIDSTATEFAILURE._serialized_start = 593 + _NAMESPACEINVALIDSTATEFAILURE._serialized_end = 759 + _NAMESPACENOTFOUNDFAILURE._serialized_start = 761 + _NAMESPACENOTFOUNDFAILURE._serialized_end = 806 + _NAMESPACEALREADYEXISTSFAILURE._serialized_start = 808 + _NAMESPACEALREADYEXISTSFAILURE._serialized_end = 839 + _CLIENTVERSIONNOTSUPPORTEDFAILURE._serialized_start = 841 + _CLIENTVERSIONNOTSUPPORTEDFAILURE._serialized_end = 948 + _SERVERVERSIONNOTSUPPORTEDFAILURE._serialized_start = 950 + _SERVERVERSIONNOTSUPPORTEDFAILURE._serialized_end = 1050 + _CANCELLATIONALREADYREQUESTEDFAILURE._serialized_start = 1052 + _CANCELLATIONALREADYREQUESTEDFAILURE._serialized_end = 1089 + _QUERYFAILEDFAILURE._serialized_start = 1091 + _QUERYFAILEDFAILURE._serialized_end = 1162 + _PERMISSIONDENIEDFAILURE._serialized_start = 1164 + _PERMISSIONDENIEDFAILURE._serialized_end = 1205 + _RESOURCEEXHAUSTEDFAILURE._serialized_start = 1208 + _RESOURCEEXHAUSTEDFAILURE._serialized_end = 1358 + _SYSTEMWORKFLOWFAILURE._serialized_start = 1360 + _SYSTEMWORKFLOWFAILURE._serialized_end = 1478 + _WORKFLOWNOTREADYFAILURE._serialized_start = 1480 + _WORKFLOWNOTREADYFAILURE._serialized_end = 1505 + _NEWERBUILDEXISTSFAILURE._serialized_start = 1507 + _NEWERBUILDEXISTSFAILURE._serialized_end = 1558 + _MULTIOPERATIONEXECUTIONFAILURE._serialized_start = 1561 + _MULTIOPERATIONEXECUTIONFAILURE._serialized_end = 1778 + _MULTIOPERATIONEXECUTIONFAILURE_OPERATIONSTATUS._serialized_start = 1691 + _MULTIOPERATIONEXECUTIONFAILURE_OPERATIONSTATUS._serialized_end = 1778 + _ACTIVITYEXECUTIONALREADYSTARTEDFAILURE._serialized_start = 1780 + _ACTIVITYEXECUTIONALREADYSTARTEDFAILURE._serialized_end = 1862 + _NEXUSOPERATIONEXECUTIONALREADYSTARTEDFAILURE._serialized_start = 1864 + _NEXUSOPERATIONEXECUTIONALREADYSTARTEDFAILURE._serialized_end = 1952 + _WORKFLOWTASKCOMPLETIONBUFFERLOSTFAILURE._serialized_start = 1954 + _WORKFLOWTASKCOMPLETIONBUFFERLOSTFAILURE._serialized_end = 1995 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/errordetails/v1/message_pb2.pyi b/temporalio/api/errordetails/v1/message_pb2.pyi index 774dc7ad1..5ffe230b3 100644 --- a/temporalio/api/errordetails/v1/message_pb2.pyi +++ b/temporalio/api/errordetails/v1/message_pb2.pyi @@ -53,18 +53,26 @@ class WorkflowExecutionAlreadyStartedFailure(google.protobuf.message.Message): START_REQUEST_ID_FIELD_NUMBER: builtins.int RUN_ID_FIELD_NUMBER: builtins.int + FIRST_EXECUTION_RUN_ID_FIELD_NUMBER: builtins.int start_request_id: builtins.str run_id: builtins.str + first_execution_run_id: builtins.str def __init__( self, *, start_request_id: builtins.str = ..., run_id: builtins.str = ..., + first_execution_run_id: builtins.str = ..., ) -> None: ... def ClearField( self, field_name: typing_extensions.Literal[ - "run_id", b"run_id", "start_request_id", b"start_request_id" + "first_execution_run_id", + b"first_execution_run_id", + "run_id", + b"run_id", + "start_request_id", + b"start_request_id", ], ) -> None: ... @@ -496,3 +504,19 @@ class NexusOperationExecutionAlreadyStartedFailure(google.protobuf.message.Messa global___NexusOperationExecutionAlreadyStartedFailure = ( NexusOperationExecutionAlreadyStartedFailure ) + +class WorkflowTaskCompletionBufferLostFailure(google.protobuf.message.Message): + """An error indicating that the server lost the buffered pages of a paginated workflow task + completion. This is a transient error: the workflow task is still valid, and the client + should resend all pages from page 0 using the same task token. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___WorkflowTaskCompletionBufferLostFailure = ( + WorkflowTaskCompletionBufferLostFailure +) diff --git a/temporalio/api/history/v1/message_pb2.py b/temporalio/api/history/v1/message_pb2.py index 3d3cd5132..ac337b708 100644 --- a/temporalio/api/history/v1/message_pb2.py +++ b/temporalio/api/history/v1/message_pb2.py @@ -38,6 +38,9 @@ from temporalio.api.failure.v1 import ( message_pb2 as temporal_dot_api_dot_failure_dot_v1_dot_message__pb2, ) +from temporalio.api.sdk.v1 import ( + event_group_marker_pb2 as temporal_dot_api_dot_sdk_dot_v1_dot_event__group__marker__pb2, +) from temporalio.api.sdk.v1 import ( task_complete_metadata_pb2 as temporal_dot_api_dot_sdk_dot_v1_dot_task__complete__metadata__pb2, ) @@ -55,7 +58,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n%temporal/api/history/v1/message.proto\x12\x17temporal.api.history.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a&temporal/api/enums/v1/event_type.proto\x1a(temporal/api/enums/v1/failed_cause.proto\x1a"temporal/api/enums/v1/update.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a$temporal/api/common/v1/message.proto\x1a(temporal/api/deployment/v1/message.proto\x1a%temporal/api/failure/v1/message.proto\x1a\'temporal/api/taskqueue/v1/message.proto\x1a$temporal/api/update/v1/message.proto\x1a&temporal/api/workflow/v1/message.proto\x1a\x30temporal/api/sdk/v1/task_complete_metadata.proto\x1a\'temporal/api/sdk/v1/user_metadata.proto"\x9a\x12\n\'WorkflowExecutionStartedEventAttributes\x12;\n\rworkflow_type\x18\x01 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12!\n\x19parent_workflow_namespace\x18\x02 \x01(\t\x12$\n\x1cparent_workflow_namespace_id\x18\x1b \x01(\t\x12L\n\x19parent_workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12!\n\x19parent_initiated_event_id\x18\x04 \x01(\x03\x12\x38\n\ntask_queue\x18\x05 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x06 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12=\n\x1aworkflow_execution_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12"\n\x1a\x63ontinued_execution_run_id\x18\n \x01(\t\x12@\n\tinitiator\x18\x0b \x01(\x0e\x32-.temporal.api.enums.v1.ContinueAsNewInitiator\x12;\n\x11\x63ontinued_failure\x18\x0c \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12@\n\x16last_completion_result\x18\r \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12!\n\x19original_execution_run_id\x18\x0e \x01(\t\x12\x10\n\x08identity\x18\x0f \x01(\t\x12\x1e\n\x16\x66irst_execution_run_id\x18\x10 \x01(\t\x12\x39\n\x0cretry_policy\x18\x11 \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x0f\n\x07\x61ttempt\x18\x12 \x01(\x05\x12\x46\n"workflow_execution_expiration_time\x18\x13 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x15\n\rcron_schedule\x18\x14 \x01(\t\x12>\n\x1b\x66irst_workflow_task_backoff\x18\x15 \x01(\x0b\x32\x19.google.protobuf.Duration\x12*\n\x04memo\x18\x16 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x17 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x45\n\x16prev_auto_reset_points\x18\x18 \x01(\x0b\x32%.temporal.api.workflow.v1.ResetPoints\x12.\n\x06header\x18\x19 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12&\n\x1eparent_initiated_event_version\x18\x1a \x01(\x03\x12\x13\n\x0bworkflow_id\x18\x1c \x01(\t\x12L\n\x14source_version_stamp\x18\x1d \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12>\n\x14\x63ompletion_callbacks\x18\x1e \x03(\x0b\x32 .temporal.api.common.v1.Callback\x12J\n\x17root_workflow_execution\x18\x1f \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x1e\n\x12inherited_build_id\x18 \x01(\tB\x02\x18\x01\x12I\n\x13versioning_override\x18! \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12\x33\n\'parent_pinned_worker_deployment_version\x18" \x01(\tB\x02\x18\x01\x12\x32\n\x08priority\x18# \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12U\n\x18inherited_pinned_version\x18% \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12Y\n\x1binherited_auto_upgrade_info\x18\' \x01(\x0b\x32\x34.temporal.api.deployment.v1.InheritedAutoUpgradeInfo\x12 \n\x18\x65\x61ger_execution_accepted\x18& \x01(\x08\x12^\n\x1f\x64\x65\x63lined_target_version_upgrade\x18( \x01(\x0b\x32\x35.temporal.api.history.v1.DeclinedTargetVersionUpgrade\x12J\n\x14time_skipping_config\x18) \x01(\x0b\x32,.temporal.api.workflow.v1.TimeSkippingConfig\x12;\n\x18initial_skipped_duration\x18* \x01(\x0b\x32\x19.google.protobuf.DurationJ\x04\x08$\x10%R parent_pinned_deployment_version"\x88\x01\n\x1c\x44\x65\x63linedTargetVersionUpgrade\x12O\n\x12\x64\x65ployment_version\x18\x01 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x17\n\x0frevision_number\x18\x02 \x01(\x03"\xa5\x01\n)WorkflowExecutionCompletedEventAttributes\x12\x30\n\x06result\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03\x12\x1c\n\x14new_execution_run_id\x18\x03 \x01(\t"\xdb\x01\n&WorkflowExecutionFailedEventAttributes\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x36\n\x0bretry_state\x18\x02 \x01(\x0e\x32!.temporal.api.enums.v1.RetryState\x12(\n workflow_task_completed_event_id\x18\x03 \x01(\x03\x12\x1c\n\x14new_execution_run_id\x18\x04 \x01(\t"\x80\x01\n(WorkflowExecutionTimedOutEventAttributes\x12\x36\n\x0bretry_state\x18\x01 \x01(\x0e\x32!.temporal.api.enums.v1.RetryState\x12\x1c\n\x14new_execution_run_id\x18\x02 \x01(\t"\xa5\x07\n.WorkflowExecutionContinuedAsNewEventAttributes\x12\x1c\n\x14new_execution_run_id\x18\x01 \x01(\t\x12;\n\rworkflow_type\x18\x02 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x03 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x04 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x37\n\x14workflow_run_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12(\n workflow_task_completed_event_id\x18\x07 \x01(\x03\x12\x39\n\x16\x62\x61\x63koff_start_interval\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12@\n\tinitiator\x18\t \x01(\x0e\x32-.temporal.api.enums.v1.ContinueAsNewInitiator\x12\x35\n\x07\x66\x61ilure\x18\n \x01(\x0b\x32 .temporal.api.failure.v1.FailureB\x02\x18\x01\x12@\n\x16last_completion_result\x18\x0b \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12.\n\x06header\x18\x0c \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12*\n\x04memo\x18\r \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x0e \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x1c\n\x10inherit_build_id\x18\x0f \x01(\x08\x42\x02\x18\x01\x12[\n\x1binitial_versioning_behavior\x18\x10 \x01(\x0e\x32\x36.temporal.api.enums.v1.ContinueAsNewVersioningBehavior"\xac\x01\n$WorkflowTaskScheduledEventAttributes\x12\x38\n\ntask_queue\x18\x01 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12\x39\n\x16start_to_close_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x0f\n\x07\x61ttempt\x18\x03 \x01(\x05"\xa0\x03\n"WorkflowTaskStartedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\nrequest_id\x18\x03 \x01(\t\x12\x1f\n\x17suggest_continue_as_new\x18\x04 \x01(\x08\x12Z\n\x1fsuggest_continue_as_new_reasons\x18\x08 \x03(\x0e\x32\x31.temporal.api.enums.v1.SuggestContinueAsNewReason\x12\x30\n(target_worker_deployment_version_changed\x18\t \x01(\x08\x12\x1a\n\x12history_size_bytes\x18\x05 \x01(\x03\x12\x46\n\x0eworker_version\x18\x06 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12%\n\x19\x62uild_id_redirect_counter\x18\x07 \x01(\x03\x42\x02\x18\x01"\x82\x05\n$WorkflowTaskCompletedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x18\n\x10started_event_id\x18\x02 \x01(\x03\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x1b\n\x0f\x62inary_checksum\x18\x04 \x01(\tB\x02\x18\x01\x12\x46\n\x0eworker_version\x18\x05 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12H\n\x0csdk_metadata\x18\x06 \x01(\x0b\x32\x32.temporal.api.sdk.v1.WorkflowTaskCompletedMetadata\x12\x43\n\x11metering_metadata\x18\r \x01(\x0b\x32(.temporal.api.common.v1.MeteringMetadata\x12>\n\ndeployment\x18\x07 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12\x46\n\x13versioning_behavior\x18\x08 \x01(\x0e\x32).temporal.api.enums.v1.VersioningBehavior\x12%\n\x19worker_deployment_version\x18\t \x01(\tB\x02\x18\x01\x12\x1e\n\x16worker_deployment_name\x18\n \x01(\t\x12O\n\x12\x64\x65ployment_version\x18\x0b \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion"\x95\x01\n#WorkflowTaskTimedOutEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x18\n\x10started_event_id\x18\x02 \x01(\x03\x12\x38\n\x0ctimeout_type\x18\x03 \x01(\x0e\x32".temporal.api.enums.v1.TimeoutType"\x87\x03\n!WorkflowTaskFailedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x18\n\x10started_event_id\x18\x02 \x01(\x03\x12=\n\x05\x63\x61use\x18\x03 \x01(\x0e\x32..temporal.api.enums.v1.WorkflowTaskFailedCause\x12\x31\n\x07\x66\x61ilure\x18\x04 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x13\n\x0b\x62\x61se_run_id\x18\x06 \x01(\t\x12\x12\n\nnew_run_id\x18\x07 \x01(\t\x12\x1a\n\x12\x66ork_event_version\x18\x08 \x01(\x03\x12\x1b\n\x0f\x62inary_checksum\x18\t \x01(\tB\x02\x18\x01\x12\x46\n\x0eworker_version\x18\n \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01"\xc2\x05\n$ActivityTaskScheduledEventAttributes\x12\x13\n\x0b\x61\x63tivity_id\x18\x01 \x01(\t\x12;\n\ractivity_type\x18\x02 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12\x38\n\ntask_queue\x18\x04 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12.\n\x06header\x18\x05 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12/\n\x05input\x18\x06 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12<\n\x19schedule_to_close_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12(\n workflow_task_completed_event_id\x18\x0b \x01(\x03\x12\x39\n\x0cretry_policy\x18\x0c \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12!\n\x15use_workflow_build_id\x18\r \x01(\x08\x42\x02\x18\x01\x12\x32\n\x08priority\x18\x0e \x01(\x0b\x32 .temporal.api.common.v1.PriorityJ\x04\x08\x03\x10\x04"\x9e\x02\n"ActivityTaskStartedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\nrequest_id\x18\x03 \x01(\t\x12\x0f\n\x07\x61ttempt\x18\x04 \x01(\x05\x12\x36\n\x0clast_failure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x46\n\x0eworker_version\x18\x06 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12%\n\x19\x62uild_id_redirect_counter\x18\x07 \x01(\x03\x42\x02\x18\x01"\xe8\x01\n$ActivityTaskCompletedEventAttributes\x12\x30\n\x06result\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x1a\n\x12scheduled_event_id\x18\x02 \x01(\x03\x12\x18\n\x10started_event_id\x18\x03 \x01(\x03\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x46\n\x0eworker_version\x18\x05 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01"\x9e\x02\n!ActivityTaskFailedEventAttributes\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x1a\n\x12scheduled_event_id\x18\x02 \x01(\x03\x12\x18\n\x10started_event_id\x18\x03 \x01(\x03\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x36\n\x0bretry_state\x18\x05 \x01(\x0e\x32!.temporal.api.enums.v1.RetryState\x12\x46\n\x0eworker_version\x18\x06 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01"\xc6\x01\n#ActivityTaskTimedOutEventAttributes\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x1a\n\x12scheduled_event_id\x18\x02 \x01(\x03\x12\x18\n\x10started_event_id\x18\x03 \x01(\x03\x12\x36\n\x0bretry_state\x18\x04 \x01(\x0e\x32!.temporal.api.enums.v1.RetryState"r\n*ActivityTaskCancelRequestedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03"\x92\x02\n#ActivityTaskCanceledEventAttributes\x12\x31\n\x07\x64\x65tails\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12(\n latest_cancel_requested_event_id\x18\x02 \x01(\x03\x12\x1a\n\x12scheduled_event_id\x18\x03 \x01(\x03\x12\x18\n\x10started_event_id\x18\x04 \x01(\x03\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x46\n\x0eworker_version\x18\x06 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01"\x93\x01\n\x1bTimerStartedEventAttributes\x12\x10\n\x08timer_id\x18\x01 \x01(\t\x12\x38\n\x15start_to_fire_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration\x12(\n workflow_task_completed_event_id\x18\x03 \x01(\x03"G\n\x19TimerFiredEventAttributes\x12\x10\n\x08timer_id\x18\x01 \x01(\t\x12\x18\n\x10started_event_id\x18\x02 \x01(\x03"\x86\x01\n\x1cTimerCanceledEventAttributes\x12\x10\n\x08timer_id\x18\x01 \x01(\t\x12\x18\n\x10started_event_id\x18\x02 \x01(\x03\x12(\n workflow_task_completed_event_id\x18\x03 \x01(\x03\x12\x10\n\x08identity\x18\x04 \x01(\t"\xc7\x01\n/WorkflowExecutionCancelRequestedEventAttributes\x12\r\n\x05\x63\x61use\x18\x01 \x01(\t\x12#\n\x1b\x65xternal_initiated_event_id\x18\x02 \x01(\x03\x12N\n\x1b\x65xternal_workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x10\n\x08identity\x18\x04 \x01(\t"\x87\x01\n(WorkflowExecutionCanceledEventAttributes\x12(\n workflow_task_completed_event_id\x18\x01 \x01(\x03\x12\x31\n\x07\x64\x65tails\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads"\xe9\x02\n\x1dMarkerRecordedEventAttributes\x12\x13\n\x0bmarker_name\x18\x01 \x01(\t\x12T\n\x07\x64\x65tails\x18\x02 \x03(\x0b\x32\x43.temporal.api.history.v1.MarkerRecordedEventAttributes.DetailsEntry\x12(\n workflow_task_completed_event_id\x18\x03 \x01(\x03\x12.\n\x06header\x18\x04 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x31\n\x07\x66\x61ilure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x1aP\n\x0c\x44\x65tailsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12/\n\x05value\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads:\x02\x38\x01"\xbf\x02\n(WorkflowExecutionSignaledEventAttributes\x12\x13\n\x0bsignal_name\x18\x01 \x01(\t\x12/\n\x05input\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x03 \x01(\t\x12.\n\x06header\x18\x04 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\'\n\x1bskip_generate_workflow_task\x18\x05 \x01(\x08\x42\x02\x18\x01\x12N\n\x1b\x65xternal_workflow_execution\x18\x06 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x12\n\nrequest_id\x18\x07 \x01(\t"\x81\x01\n*WorkflowExecutionTerminatedEventAttributes\x12\x0e\n\x06reason\x18\x01 \x01(\t\x12\x31\n\x07\x64\x65tails\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x03 \x01(\t"\x9c\x02\n>RequestCancelExternalWorkflowExecutionInitiatedEventAttributes\x12(\n workflow_task_completed_event_id\x18\x01 \x01(\x03\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x07 \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x13\n\x07\x63ontrol\x18\x04 \x01(\tB\x02\x18\x01\x12\x1b\n\x13\x63hild_workflow_only\x18\x05 \x01(\x08\x12\x0e\n\x06reason\x18\x06 \x01(\t"\xda\x02\n;RequestCancelExternalWorkflowExecutionFailedEventAttributes\x12P\n\x05\x63\x61use\x18\x01 \x01(\x0e\x32\x41.temporal.api.enums.v1.CancelExternalWorkflowExecutionFailedCause\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03\x12\x11\n\tnamespace\x18\x03 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x07 \x01(\t\x12\x45\n\x12workflow_execution\x18\x04 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x1a\n\x12initiated_event_id\x18\x05 \x01(\x03\x12\x13\n\x07\x63ontrol\x18\x06 \x01(\tB\x02\x18\x01"\xc5\x01\n7ExternalWorkflowExecutionCancelRequestedEventAttributes\x12\x1a\n\x12initiated_event_id\x18\x01 \x01(\x03\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x04 \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution"\xfb\x02\n7SignalExternalWorkflowExecutionInitiatedEventAttributes\x12(\n workflow_task_completed_event_id\x18\x01 \x01(\x03\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\t \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x13\n\x0bsignal_name\x18\x04 \x01(\t\x12/\n\x05input\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x13\n\x07\x63ontrol\x18\x06 \x01(\tB\x02\x18\x01\x12\x1b\n\x13\x63hild_workflow_only\x18\x07 \x01(\x08\x12.\n\x06header\x18\x08 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header"\xd3\x02\n4SignalExternalWorkflowExecutionFailedEventAttributes\x12P\n\x05\x63\x61use\x18\x01 \x01(\x0e\x32\x41.temporal.api.enums.v1.SignalExternalWorkflowExecutionFailedCause\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03\x12\x11\n\tnamespace\x18\x03 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x07 \x01(\t\x12\x45\n\x12workflow_execution\x18\x04 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x1a\n\x12initiated_event_id\x18\x05 \x01(\x03\x12\x13\n\x07\x63ontrol\x18\x06 \x01(\tB\x02\x18\x01"\xd3\x01\n0ExternalWorkflowExecutionSignaledEventAttributes\x12\x1a\n\x12initiated_event_id\x18\x01 \x01(\x03\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x05 \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x13\n\x07\x63ontrol\x18\x04 \x01(\tB\x02\x18\x01"\x9e\x01\n-UpsertWorkflowSearchAttributesEventAttributes\x12(\n workflow_task_completed_event_id\x18\x01 \x01(\x03\x12\x43\n\x11search_attributes\x18\x02 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes"\x8a\x01\n)WorkflowPropertiesModifiedEventAttributes\x12(\n workflow_task_completed_event_id\x18\x01 \x01(\x03\x12\x33\n\rupserted_memo\x18\x02 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo"\xf1\x08\n3StartChildWorkflowExecutionInitiatedEventAttributes\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x12 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x04 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12=\n\x1aworkflow_execution_timeout\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x45\n\x13parent_close_policy\x18\t \x01(\x0e\x32(.temporal.api.enums.v1.ParentClosePolicy\x12\x13\n\x07\x63ontrol\x18\n \x01(\tB\x02\x18\x01\x12(\n workflow_task_completed_event_id\x18\x0b \x01(\x03\x12N\n\x18workflow_id_reuse_policy\x18\x0c \x01(\x0e\x32,.temporal.api.enums.v1.WorkflowIdReusePolicy\x12\x39\n\x0cretry_policy\x18\r \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x15\n\rcron_schedule\x18\x0e \x01(\t\x12.\n\x06header\x18\x0f \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12*\n\x04memo\x18\x10 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x11 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x1c\n\x10inherit_build_id\x18\x13 \x01(\x08\x42\x02\x18\x01\x12\x32\n\x08priority\x18\x14 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12J\n\x14time_skipping_config\x18\x15 \x01(\x0b\x32,.temporal.api.workflow.v1.TimeSkippingConfig\x12;\n\x18initial_skipped_duration\x18\x1e \x01(\x0b\x32\x19.google.protobuf.Duration"\xd6\x02\n0StartChildWorkflowExecutionFailedEventAttributes\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x08 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12L\n\x05\x63\x61use\x18\x04 \x01(\x0e\x32=.temporal.api.enums.v1.StartChildWorkflowExecutionFailedCause\x12\x13\n\x07\x63ontrol\x18\x05 \x01(\tB\x02\x18\x01\x12\x1a\n\x12initiated_event_id\x18\x06 \x01(\x03\x12(\n workflow_task_completed_event_id\x18\x07 \x01(\x03"\xa7\x02\n,ChildWorkflowExecutionStartedEventAttributes\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x06 \x01(\t\x12\x1a\n\x12initiated_event_id\x18\x02 \x01(\x03\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x04 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12.\n\x06header\x18\x05 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header"\xc5\x02\n.ChildWorkflowExecutionCompletedEventAttributes\x12\x30\n\x06result\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x07 \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x04 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x1a\n\x12initiated_event_id\x18\x05 \x01(\x03\x12\x18\n\x10started_event_id\x18\x06 \x01(\x03"\xfb\x02\n+ChildWorkflowExecutionFailedEventAttributes\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x08 \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x04 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x1a\n\x12initiated_event_id\x18\x05 \x01(\x03\x12\x18\n\x10started_event_id\x18\x06 \x01(\x03\x12\x36\n\x0bretry_state\x18\x07 \x01(\x0e\x32!.temporal.api.enums.v1.RetryState"\xc5\x02\n-ChildWorkflowExecutionCanceledEventAttributes\x12\x31\n\x07\x64\x65tails\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x07 \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x04 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x1a\n\x12initiated_event_id\x18\x05 \x01(\x03\x12\x18\n\x10started_event_id\x18\x06 \x01(\x03"\xca\x02\n-ChildWorkflowExecutionTimedOutEventAttributes\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x07 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x1a\n\x12initiated_event_id\x18\x04 \x01(\x03\x12\x18\n\x10started_event_id\x18\x05 \x01(\x03\x12\x36\n\x0bretry_state\x18\x06 \x01(\x0e\x32!.temporal.api.enums.v1.RetryState"\x94\x02\n/ChildWorkflowExecutionTerminatedEventAttributes\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x06 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x1a\n\x12initiated_event_id\x18\x04 \x01(\x03\x12\x18\n\x10started_event_id\x18\x05 \x01(\x03"\xb6\x05\n.WorkflowExecutionOptionsUpdatedEventAttributes\x12I\n\x13versioning_override\x18\x01 \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12!\n\x19unset_versioning_override\x18\x02 \x01(\x08\x12\x1b\n\x13\x61ttached_request_id\x18\x03 \x01(\t\x12G\n\x1d\x61ttached_completion_callbacks\x18\x04 \x03(\x0b\x32 .temporal.api.common.v1.Callback\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x32\n\x08priority\x18\x06 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12J\n\x14time_skipping_config\x18\x07 \x01(\x0b\x32,.temporal.api.workflow.v1.TimeSkippingConfig\x12\x84\x01\n\x17workflow_update_options\x18\x08 \x03(\x0b\x32\x63.temporal.api.history.v1.WorkflowExecutionOptionsUpdatedEventAttributes.WorkflowUpdateOptionsUpdate\x1a\x96\x01\n\x1bWorkflowUpdateOptionsUpdate\x12\x11\n\tupdate_id\x18\x01 \x01(\t\x12\x1b\n\x13\x61ttached_request_id\x18\x02 \x01(\t\x12G\n\x1d\x61ttached_completion_callbacks\x18\x03 \x03(\x0b\x32 .temporal.api.common.v1.Callback"\xc0\x02\n3WorkflowPropertiesModifiedExternallyEventAttributes\x12\x16\n\x0enew_task_queue\x18\x01 \x01(\t\x12<\n\x19new_workflow_task_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration\x12;\n\x18new_workflow_run_timeout\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x41\n\x1enew_workflow_execution_timeout\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x33\n\rupserted_memo\x18\x05 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo"\x90\x01\n3ActivityPropertiesModifiedExternallyEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12=\n\x10new_retry_policy\x18\x02 \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy"\xdc\x01\n.WorkflowExecutionUpdateAcceptedEventAttributes\x12\x1c\n\x14protocol_instance_id\x18\x01 \x01(\t\x12#\n\x1b\x61\x63\x63\x65pted_request_message_id\x18\x02 \x01(\t\x12,\n$accepted_request_sequencing_event_id\x18\x03 \x01(\x03\x12\x39\n\x10\x61\x63\x63\x65pted_request\x18\x04 \x01(\x0b\x32\x1f.temporal.api.update.v1.Request"\xaa\x01\n/WorkflowExecutionUpdateCompletedEventAttributes\x12*\n\x04meta\x18\x01 \x01(\x0b\x32\x1c.temporal.api.update.v1.Meta\x12\x19\n\x11\x61\x63\x63\x65pted_event_id\x18\x03 \x01(\x03\x12\x30\n\x07outcome\x18\x02 \x01(\x0b\x32\x1f.temporal.api.update.v1.Outcome"\x8f\x02\n.WorkflowExecutionUpdateRejectedEventAttributes\x12\x1c\n\x14protocol_instance_id\x18\x01 \x01(\t\x12#\n\x1brejected_request_message_id\x18\x02 \x01(\t\x12,\n$rejected_request_sequencing_event_id\x18\x03 \x01(\x03\x12\x39\n\x10rejected_request\x18\x04 \x01(\x0b\x32\x1f.temporal.api.update.v1.Request\x12\x31\n\x07\x66\x61ilure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.Failure"\xa4\x01\n.WorkflowExecutionUpdateAdmittedEventAttributes\x12\x30\n\x07request\x18\x01 \x01(\x0b\x32\x1f.temporal.api.update.v1.Request\x12@\n\x06origin\x18\x02 \x01(\x0e\x32\x30.temporal.api.enums.v1.UpdateAdmittedEventOrigin"^\n&WorkflowExecutionPausedEventAttributes\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x12\n\nrequest_id\x18\x03 \x01(\t"`\n(WorkflowExecutionUnpausedEventAttributes\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x12\n\nrequest_id\x18\x03 \x01(\t"\xbe\x01\n8WorkflowExecutionTimeSkippingTransitionedEventAttributes\x12/\n\x0btarget_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x1c\n\x14\x64isabled_after_bound\x18\x02 \x01(\x08\x12\x33\n\x0fwall_clock_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xb4\x04\n&NexusOperationScheduledEventAttributes\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\x11\n\toperation\x18\x03 \x01(\t\x12.\n\x05input\x18\x04 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12<\n\x19schedule_to_close_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x66\n\x0cnexus_header\x18\x06 \x03(\x0b\x32P.temporal.api.history.v1.NexusOperationScheduledEventAttributes.NexusHeaderEntry\x12(\n workflow_task_completed_event_id\x18\x07 \x01(\x03\x12\x12\n\nrequest_id\x18\x08 \x01(\t\x12\x13\n\x0b\x65ndpoint_id\x18\t \x01(\t\x12<\n\x19schedule_to_start_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\x0b \x01(\x0b\x32\x19.google.protobuf.Duration\x1a\x32\n\x10NexusHeaderEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"\x89\x01\n$NexusOperationStartedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x18\n\x0coperation_id\x18\x03 \x01(\tB\x02\x18\x01\x12\x12\n\nrequest_id\x18\x04 \x01(\t\x12\x17\n\x0foperation_token\x18\x05 \x01(\t"\x89\x01\n&NexusOperationCompletedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12/\n\x06result\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\x12\n\nrequest_id\x18\x03 \x01(\t"\x88\x01\n#NexusOperationFailedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x31\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x12\n\nrequest_id\x18\x03 \x01(\t"\x8a\x01\n%NexusOperationTimedOutEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x31\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x12\n\nrequest_id\x18\x03 \x01(\t"\x8a\x01\n%NexusOperationCanceledEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x31\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x12\n\nrequest_id\x18\x03 \x01(\t"t\n,NexusOperationCancelRequestedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03"\x97\x01\n3NexusOperationCancelRequestCompletedEventAttributes\x12\x1a\n\x12requested_event_id\x18\x01 \x01(\x03\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03\x12\x1a\n\x12scheduled_event_id\x18\x03 \x01(\x03"\xc7\x01\n0NexusOperationCancelRequestFailedEventAttributes\x12\x1a\n\x12requested_event_id\x18\x01 \x01(\x03\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03\x12\x31\n\x07\x66\x61ilure\x18\x03 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x1a\n\x12scheduled_event_id\x18\x04 \x01(\x03"\x85?\n\x0cHistoryEvent\x12\x10\n\x08\x65vent_id\x18\x01 \x01(\x03\x12.\n\nevent_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x34\n\nevent_type\x18\x03 \x01(\x0e\x32 .temporal.api.enums.v1.EventType\x12\x0f\n\x07version\x18\x04 \x01(\x03\x12\x0f\n\x07task_id\x18\x05 \x01(\x03\x12\x1a\n\x11worker_may_ignore\x18\xac\x02 \x01(\x08\x12\x39\n\ruser_metadata\x18\xad\x02 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12,\n\x05links\x18\xae\x02 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x12\x35\n\tprincipal\x18\xaf\x02 \x01(\x0b\x32!.temporal.api.common.v1.Principal\x12w\n+workflow_execution_started_event_attributes\x18\x06 \x01(\x0b\x32@.temporal.api.history.v1.WorkflowExecutionStartedEventAttributesH\x00\x12{\n-workflow_execution_completed_event_attributes\x18\x07 \x01(\x0b\x32\x42.temporal.api.history.v1.WorkflowExecutionCompletedEventAttributesH\x00\x12u\n*workflow_execution_failed_event_attributes\x18\x08 \x01(\x0b\x32?.temporal.api.history.v1.WorkflowExecutionFailedEventAttributesH\x00\x12z\n-workflow_execution_timed_out_event_attributes\x18\t \x01(\x0b\x32\x41.temporal.api.history.v1.WorkflowExecutionTimedOutEventAttributesH\x00\x12q\n(workflow_task_scheduled_event_attributes\x18\n \x01(\x0b\x32=.temporal.api.history.v1.WorkflowTaskScheduledEventAttributesH\x00\x12m\n&workflow_task_started_event_attributes\x18\x0b \x01(\x0b\x32;.temporal.api.history.v1.WorkflowTaskStartedEventAttributesH\x00\x12q\n(workflow_task_completed_event_attributes\x18\x0c \x01(\x0b\x32=.temporal.api.history.v1.WorkflowTaskCompletedEventAttributesH\x00\x12p\n(workflow_task_timed_out_event_attributes\x18\r \x01(\x0b\x32<.temporal.api.history.v1.WorkflowTaskTimedOutEventAttributesH\x00\x12k\n%workflow_task_failed_event_attributes\x18\x0e \x01(\x0b\x32:.temporal.api.history.v1.WorkflowTaskFailedEventAttributesH\x00\x12q\n(activity_task_scheduled_event_attributes\x18\x0f \x01(\x0b\x32=.temporal.api.history.v1.ActivityTaskScheduledEventAttributesH\x00\x12m\n&activity_task_started_event_attributes\x18\x10 \x01(\x0b\x32;.temporal.api.history.v1.ActivityTaskStartedEventAttributesH\x00\x12q\n(activity_task_completed_event_attributes\x18\x11 \x01(\x0b\x32=.temporal.api.history.v1.ActivityTaskCompletedEventAttributesH\x00\x12k\n%activity_task_failed_event_attributes\x18\x12 \x01(\x0b\x32:.temporal.api.history.v1.ActivityTaskFailedEventAttributesH\x00\x12p\n(activity_task_timed_out_event_attributes\x18\x13 \x01(\x0b\x32<.temporal.api.history.v1.ActivityTaskTimedOutEventAttributesH\x00\x12^\n\x1etimer_started_event_attributes\x18\x14 \x01(\x0b\x32\x34.temporal.api.history.v1.TimerStartedEventAttributesH\x00\x12Z\n\x1ctimer_fired_event_attributes\x18\x15 \x01(\x0b\x32\x32.temporal.api.history.v1.TimerFiredEventAttributesH\x00\x12~\n/activity_task_cancel_requested_event_attributes\x18\x16 \x01(\x0b\x32\x43.temporal.api.history.v1.ActivityTaskCancelRequestedEventAttributesH\x00\x12o\n\'activity_task_canceled_event_attributes\x18\x17 \x01(\x0b\x32<.temporal.api.history.v1.ActivityTaskCanceledEventAttributesH\x00\x12`\n\x1ftimer_canceled_event_attributes\x18\x18 \x01(\x0b\x32\x35.temporal.api.history.v1.TimerCanceledEventAttributesH\x00\x12\x62\n marker_recorded_event_attributes\x18\x19 \x01(\x0b\x32\x36.temporal.api.history.v1.MarkerRecordedEventAttributesH\x00\x12y\n,workflow_execution_signaled_event_attributes\x18\x1a \x01(\x0b\x32\x41.temporal.api.history.v1.WorkflowExecutionSignaledEventAttributesH\x00\x12}\n.workflow_execution_terminated_event_attributes\x18\x1b \x01(\x0b\x32\x43.temporal.api.history.v1.WorkflowExecutionTerminatedEventAttributesH\x00\x12\x88\x01\n4workflow_execution_cancel_requested_event_attributes\x18\x1c \x01(\x0b\x32H.temporal.api.history.v1.WorkflowExecutionCancelRequestedEventAttributesH\x00\x12y\n,workflow_execution_canceled_event_attributes\x18\x1d \x01(\x0b\x32\x41.temporal.api.history.v1.WorkflowExecutionCanceledEventAttributesH\x00\x12\xa8\x01\nErequest_cancel_external_workflow_execution_initiated_event_attributes\x18\x1e \x01(\x0b\x32W.temporal.api.history.v1.RequestCancelExternalWorkflowExecutionInitiatedEventAttributesH\x00\x12\xa2\x01\nBrequest_cancel_external_workflow_execution_failed_event_attributes\x18\x1f \x01(\x0b\x32T.temporal.api.history.v1.RequestCancelExternalWorkflowExecutionFailedEventAttributesH\x00\x12\x99\x01\n=external_workflow_execution_cancel_requested_event_attributes\x18 \x01(\x0b\x32P.temporal.api.history.v1.ExternalWorkflowExecutionCancelRequestedEventAttributesH\x00\x12\x87\x01\n4workflow_execution_continued_as_new_event_attributes\x18! \x01(\x0b\x32G.temporal.api.history.v1.WorkflowExecutionContinuedAsNewEventAttributesH\x00\x12\x91\x01\n9start_child_workflow_execution_initiated_event_attributes\x18" \x01(\x0b\x32L.temporal.api.history.v1.StartChildWorkflowExecutionInitiatedEventAttributesH\x00\x12\x8b\x01\n6start_child_workflow_execution_failed_event_attributes\x18# \x01(\x0b\x32I.temporal.api.history.v1.StartChildWorkflowExecutionFailedEventAttributesH\x00\x12\x82\x01\n1child_workflow_execution_started_event_attributes\x18$ \x01(\x0b\x32\x45.temporal.api.history.v1.ChildWorkflowExecutionStartedEventAttributesH\x00\x12\x86\x01\n3child_workflow_execution_completed_event_attributes\x18% \x01(\x0b\x32G.temporal.api.history.v1.ChildWorkflowExecutionCompletedEventAttributesH\x00\x12\x80\x01\n0child_workflow_execution_failed_event_attributes\x18& \x01(\x0b\x32\x44.temporal.api.history.v1.ChildWorkflowExecutionFailedEventAttributesH\x00\x12\x84\x01\n2child_workflow_execution_canceled_event_attributes\x18\' \x01(\x0b\x32\x46.temporal.api.history.v1.ChildWorkflowExecutionCanceledEventAttributesH\x00\x12\x85\x01\n3child_workflow_execution_timed_out_event_attributes\x18( \x01(\x0b\x32\x46.temporal.api.history.v1.ChildWorkflowExecutionTimedOutEventAttributesH\x00\x12\x88\x01\n4child_workflow_execution_terminated_event_attributes\x18) \x01(\x0b\x32H.temporal.api.history.v1.ChildWorkflowExecutionTerminatedEventAttributesH\x00\x12\x99\x01\n=signal_external_workflow_execution_initiated_event_attributes\x18* \x01(\x0b\x32P.temporal.api.history.v1.SignalExternalWorkflowExecutionInitiatedEventAttributesH\x00\x12\x93\x01\n:signal_external_workflow_execution_failed_event_attributes\x18+ \x01(\x0b\x32M.temporal.api.history.v1.SignalExternalWorkflowExecutionFailedEventAttributesH\x00\x12\x8a\x01\n5external_workflow_execution_signaled_event_attributes\x18, \x01(\x0b\x32I.temporal.api.history.v1.ExternalWorkflowExecutionSignaledEventAttributesH\x00\x12\x84\x01\n2upsert_workflow_search_attributes_event_attributes\x18- \x01(\x0b\x32\x46.temporal.api.history.v1.UpsertWorkflowSearchAttributesEventAttributesH\x00\x12\x86\x01\n3workflow_execution_update_accepted_event_attributes\x18. \x01(\x0b\x32G.temporal.api.history.v1.WorkflowExecutionUpdateAcceptedEventAttributesH\x00\x12\x86\x01\n3workflow_execution_update_rejected_event_attributes\x18/ \x01(\x0b\x32G.temporal.api.history.v1.WorkflowExecutionUpdateRejectedEventAttributesH\x00\x12\x88\x01\n4workflow_execution_update_completed_event_attributes\x18\x30 \x01(\x0b\x32H.temporal.api.history.v1.WorkflowExecutionUpdateCompletedEventAttributesH\x00\x12\x90\x01\n8workflow_properties_modified_externally_event_attributes\x18\x31 \x01(\x0b\x32L.temporal.api.history.v1.WorkflowPropertiesModifiedExternallyEventAttributesH\x00\x12\x90\x01\n8activity_properties_modified_externally_event_attributes\x18\x32 \x01(\x0b\x32L.temporal.api.history.v1.ActivityPropertiesModifiedExternallyEventAttributesH\x00\x12{\n-workflow_properties_modified_event_attributes\x18\x33 \x01(\x0b\x32\x42.temporal.api.history.v1.WorkflowPropertiesModifiedEventAttributesH\x00\x12\x86\x01\n3workflow_execution_update_admitted_event_attributes\x18\x34 \x01(\x0b\x32G.temporal.api.history.v1.WorkflowExecutionUpdateAdmittedEventAttributesH\x00\x12u\n*nexus_operation_scheduled_event_attributes\x18\x35 \x01(\x0b\x32?.temporal.api.history.v1.NexusOperationScheduledEventAttributesH\x00\x12q\n(nexus_operation_started_event_attributes\x18\x36 \x01(\x0b\x32=.temporal.api.history.v1.NexusOperationStartedEventAttributesH\x00\x12u\n*nexus_operation_completed_event_attributes\x18\x37 \x01(\x0b\x32?.temporal.api.history.v1.NexusOperationCompletedEventAttributesH\x00\x12o\n\'nexus_operation_failed_event_attributes\x18\x38 \x01(\x0b\x32<.temporal.api.history.v1.NexusOperationFailedEventAttributesH\x00\x12s\n)nexus_operation_canceled_event_attributes\x18\x39 \x01(\x0b\x32>.temporal.api.history.v1.NexusOperationCanceledEventAttributesH\x00\x12t\n*nexus_operation_timed_out_event_attributes\x18: \x01(\x0b\x32>.temporal.api.history.v1.NexusOperationTimedOutEventAttributesH\x00\x12\x82\x01\n1nexus_operation_cancel_requested_event_attributes\x18; \x01(\x0b\x32\x45.temporal.api.history.v1.NexusOperationCancelRequestedEventAttributesH\x00\x12\x86\x01\n3workflow_execution_options_updated_event_attributes\x18< \x01(\x0b\x32G.temporal.api.history.v1.WorkflowExecutionOptionsUpdatedEventAttributesH\x00\x12\x91\x01\n9nexus_operation_cancel_request_completed_event_attributes\x18= \x01(\x0b\x32L.temporal.api.history.v1.NexusOperationCancelRequestCompletedEventAttributesH\x00\x12\x8b\x01\n6nexus_operation_cancel_request_failed_event_attributes\x18> \x01(\x0b\x32I.temporal.api.history.v1.NexusOperationCancelRequestFailedEventAttributesH\x00\x12u\n*workflow_execution_paused_event_attributes\x18? \x01(\x0b\x32?.temporal.api.history.v1.WorkflowExecutionPausedEventAttributesH\x00\x12y\n,workflow_execution_unpaused_event_attributes\x18@ \x01(\x0b\x32\x41.temporal.api.history.v1.WorkflowExecutionUnpausedEventAttributesH\x00\x12\x9b\x01\n>workflow_execution_time_skipping_transitioned_event_attributes\x18\x41 \x01(\x0b\x32Q.temporal.api.history.v1.WorkflowExecutionTimeSkippingTransitionedEventAttributesH\x00\x42\x0c\n\nattributes"@\n\x07History\x12\x35\n\x06\x65vents\x18\x01 \x03(\x0b\x32%.temporal.api.history.v1.HistoryEventB\x8e\x01\n\x1aio.temporal.api.history.v1B\x0cMessageProtoP\x01Z%go.temporal.io/api/history/v1;history\xaa\x02\x19Temporalio.Api.History.V1\xea\x02\x1cTemporalio::Api::History::V1b\x06proto3' + b'\n%temporal/api/history/v1/message.proto\x12\x17temporal.api.history.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a&temporal/api/enums/v1/event_type.proto\x1a(temporal/api/enums/v1/failed_cause.proto\x1a"temporal/api/enums/v1/update.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a$temporal/api/common/v1/message.proto\x1a(temporal/api/deployment/v1/message.proto\x1a%temporal/api/failure/v1/message.proto\x1a\'temporal/api/taskqueue/v1/message.proto\x1a$temporal/api/update/v1/message.proto\x1a&temporal/api/workflow/v1/message.proto\x1a\x30temporal/api/sdk/v1/task_complete_metadata.proto\x1a\'temporal/api/sdk/v1/user_metadata.proto\x1a,temporal/api/sdk/v1/event_group_marker.proto"\xda\x12\n\'WorkflowExecutionStartedEventAttributes\x12;\n\rworkflow_type\x18\x01 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12!\n\x19parent_workflow_namespace\x18\x02 \x01(\t\x12$\n\x1cparent_workflow_namespace_id\x18\x1b \x01(\t\x12L\n\x19parent_workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12!\n\x19parent_initiated_event_id\x18\x04 \x01(\x03\x12\x38\n\ntask_queue\x18\x05 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x06 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12=\n\x1aworkflow_execution_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12"\n\x1a\x63ontinued_execution_run_id\x18\n \x01(\t\x12@\n\tinitiator\x18\x0b \x01(\x0e\x32-.temporal.api.enums.v1.ContinueAsNewInitiator\x12;\n\x11\x63ontinued_failure\x18\x0c \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12@\n\x16last_completion_result\x18\r \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12!\n\x19original_execution_run_id\x18\x0e \x01(\t\x12\x10\n\x08identity\x18\x0f \x01(\t\x12\x1e\n\x16\x66irst_execution_run_id\x18\x10 \x01(\t\x12\x39\n\x0cretry_policy\x18\x11 \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x0f\n\x07\x61ttempt\x18\x12 \x01(\x05\x12\x46\n"workflow_execution_expiration_time\x18\x13 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x15\n\rcron_schedule\x18\x14 \x01(\t\x12>\n\x1b\x66irst_workflow_task_backoff\x18\x15 \x01(\x0b\x32\x19.google.protobuf.Duration\x12*\n\x04memo\x18\x16 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x17 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x45\n\x16prev_auto_reset_points\x18\x18 \x01(\x0b\x32%.temporal.api.workflow.v1.ResetPoints\x12.\n\x06header\x18\x19 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12&\n\x1eparent_initiated_event_version\x18\x1a \x01(\x03\x12\x13\n\x0bworkflow_id\x18\x1c \x01(\t\x12L\n\x14source_version_stamp\x18\x1d \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12>\n\x14\x63ompletion_callbacks\x18\x1e \x03(\x0b\x32 .temporal.api.common.v1.Callback\x12J\n\x17root_workflow_execution\x18\x1f \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x1e\n\x12inherited_build_id\x18 \x01(\tB\x02\x18\x01\x12I\n\x13versioning_override\x18! \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12\x33\n\'parent_pinned_worker_deployment_version\x18" \x01(\tB\x02\x18\x01\x12\x32\n\x08priority\x18# \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12U\n\x18inherited_pinned_version\x18% \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12Y\n\x1binherited_auto_upgrade_info\x18\' \x01(\x0b\x32\x34.temporal.api.deployment.v1.InheritedAutoUpgradeInfo\x12 \n\x18\x65\x61ger_execution_accepted\x18& \x01(\x08\x12^\n\x1f\x64\x65\x63lined_target_version_upgrade\x18( \x01(\x0b\x32\x35.temporal.api.history.v1.DeclinedTargetVersionUpgrade\x12H\n\x14time_skipping_config\x18) \x01(\x0b\x32*.temporal.api.common.v1.TimeSkippingConfig\x12]\n\x1ftime_skipping_state_propagation\x18+ \x01(\x0b\x32\x34.temporal.api.common.v1.TimeSkippingStatePropagationJ\x04\x08$\x10%J\x04\x08*\x10+R parent_pinned_deployment_versionR\x18initial_skipped_duration"\x88\x01\n\x1c\x44\x65\x63linedTargetVersionUpgrade\x12O\n\x12\x64\x65ployment_version\x18\x01 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x17\n\x0frevision_number\x18\x02 \x01(\x03"\xa5\x01\n)WorkflowExecutionCompletedEventAttributes\x12\x30\n\x06result\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03\x12\x1c\n\x14new_execution_run_id\x18\x03 \x01(\t"\xdb\x01\n&WorkflowExecutionFailedEventAttributes\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x36\n\x0bretry_state\x18\x02 \x01(\x0e\x32!.temporal.api.enums.v1.RetryState\x12(\n workflow_task_completed_event_id\x18\x03 \x01(\x03\x12\x1c\n\x14new_execution_run_id\x18\x04 \x01(\t"\x80\x01\n(WorkflowExecutionTimedOutEventAttributes\x12\x36\n\x0bretry_state\x18\x01 \x01(\x0e\x32!.temporal.api.enums.v1.RetryState\x12\x1c\n\x14new_execution_run_id\x18\x02 \x01(\t"\xa5\x07\n.WorkflowExecutionContinuedAsNewEventAttributes\x12\x1c\n\x14new_execution_run_id\x18\x01 \x01(\t\x12;\n\rworkflow_type\x18\x02 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x03 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x04 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x37\n\x14workflow_run_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12(\n workflow_task_completed_event_id\x18\x07 \x01(\x03\x12\x39\n\x16\x62\x61\x63koff_start_interval\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12@\n\tinitiator\x18\t \x01(\x0e\x32-.temporal.api.enums.v1.ContinueAsNewInitiator\x12\x35\n\x07\x66\x61ilure\x18\n \x01(\x0b\x32 .temporal.api.failure.v1.FailureB\x02\x18\x01\x12@\n\x16last_completion_result\x18\x0b \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12.\n\x06header\x18\x0c \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12*\n\x04memo\x18\r \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x0e \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x1c\n\x10inherit_build_id\x18\x0f \x01(\x08\x42\x02\x18\x01\x12[\n\x1binitial_versioning_behavior\x18\x10 \x01(\x0e\x32\x36.temporal.api.enums.v1.ContinueAsNewVersioningBehavior"\xac\x01\n$WorkflowTaskScheduledEventAttributes\x12\x38\n\ntask_queue\x18\x01 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12\x39\n\x16start_to_close_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x0f\n\x07\x61ttempt\x18\x03 \x01(\x05"\xa0\x03\n"WorkflowTaskStartedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\nrequest_id\x18\x03 \x01(\t\x12\x1f\n\x17suggest_continue_as_new\x18\x04 \x01(\x08\x12Z\n\x1fsuggest_continue_as_new_reasons\x18\x08 \x03(\x0e\x32\x31.temporal.api.enums.v1.SuggestContinueAsNewReason\x12\x30\n(target_worker_deployment_version_changed\x18\t \x01(\x08\x12\x1a\n\x12history_size_bytes\x18\x05 \x01(\x03\x12\x46\n\x0eworker_version\x18\x06 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12%\n\x19\x62uild_id_redirect_counter\x18\x07 \x01(\x03\x42\x02\x18\x01"\x82\x05\n$WorkflowTaskCompletedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x18\n\x10started_event_id\x18\x02 \x01(\x03\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x1b\n\x0f\x62inary_checksum\x18\x04 \x01(\tB\x02\x18\x01\x12\x46\n\x0eworker_version\x18\x05 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12H\n\x0csdk_metadata\x18\x06 \x01(\x0b\x32\x32.temporal.api.sdk.v1.WorkflowTaskCompletedMetadata\x12\x43\n\x11metering_metadata\x18\r \x01(\x0b\x32(.temporal.api.common.v1.MeteringMetadata\x12>\n\ndeployment\x18\x07 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12\x46\n\x13versioning_behavior\x18\x08 \x01(\x0e\x32).temporal.api.enums.v1.VersioningBehavior\x12%\n\x19worker_deployment_version\x18\t \x01(\tB\x02\x18\x01\x12\x1e\n\x16worker_deployment_name\x18\n \x01(\t\x12O\n\x12\x64\x65ployment_version\x18\x0b \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion"\x95\x01\n#WorkflowTaskTimedOutEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x18\n\x10started_event_id\x18\x02 \x01(\x03\x12\x38\n\x0ctimeout_type\x18\x03 \x01(\x0e\x32".temporal.api.enums.v1.TimeoutType"\x87\x03\n!WorkflowTaskFailedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x18\n\x10started_event_id\x18\x02 \x01(\x03\x12=\n\x05\x63\x61use\x18\x03 \x01(\x0e\x32..temporal.api.enums.v1.WorkflowTaskFailedCause\x12\x31\n\x07\x66\x61ilure\x18\x04 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x13\n\x0b\x62\x61se_run_id\x18\x06 \x01(\t\x12\x12\n\nnew_run_id\x18\x07 \x01(\t\x12\x1a\n\x12\x66ork_event_version\x18\x08 \x01(\x03\x12\x1b\n\x0f\x62inary_checksum\x18\t \x01(\tB\x02\x18\x01\x12\x46\n\x0eworker_version\x18\n \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01"\xc2\x05\n$ActivityTaskScheduledEventAttributes\x12\x13\n\x0b\x61\x63tivity_id\x18\x01 \x01(\t\x12;\n\ractivity_type\x18\x02 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12\x38\n\ntask_queue\x18\x04 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12.\n\x06header\x18\x05 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12/\n\x05input\x18\x06 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12<\n\x19schedule_to_close_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12(\n workflow_task_completed_event_id\x18\x0b \x01(\x03\x12\x39\n\x0cretry_policy\x18\x0c \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12!\n\x15use_workflow_build_id\x18\r \x01(\x08\x42\x02\x18\x01\x12\x32\n\x08priority\x18\x0e \x01(\x0b\x32 .temporal.api.common.v1.PriorityJ\x04\x08\x03\x10\x04"\x9e\x02\n"ActivityTaskStartedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\nrequest_id\x18\x03 \x01(\t\x12\x0f\n\x07\x61ttempt\x18\x04 \x01(\x05\x12\x36\n\x0clast_failure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x46\n\x0eworker_version\x18\x06 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12%\n\x19\x62uild_id_redirect_counter\x18\x07 \x01(\x03\x42\x02\x18\x01"\xe8\x01\n$ActivityTaskCompletedEventAttributes\x12\x30\n\x06result\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x1a\n\x12scheduled_event_id\x18\x02 \x01(\x03\x12\x18\n\x10started_event_id\x18\x03 \x01(\x03\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x46\n\x0eworker_version\x18\x05 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01"\x9e\x02\n!ActivityTaskFailedEventAttributes\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x1a\n\x12scheduled_event_id\x18\x02 \x01(\x03\x12\x18\n\x10started_event_id\x18\x03 \x01(\x03\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x36\n\x0bretry_state\x18\x05 \x01(\x0e\x32!.temporal.api.enums.v1.RetryState\x12\x46\n\x0eworker_version\x18\x06 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01"\xc6\x01\n#ActivityTaskTimedOutEventAttributes\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x1a\n\x12scheduled_event_id\x18\x02 \x01(\x03\x12\x18\n\x10started_event_id\x18\x03 \x01(\x03\x12\x36\n\x0bretry_state\x18\x04 \x01(\x0e\x32!.temporal.api.enums.v1.RetryState"r\n*ActivityTaskCancelRequestedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03"\x92\x02\n#ActivityTaskCanceledEventAttributes\x12\x31\n\x07\x64\x65tails\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12(\n latest_cancel_requested_event_id\x18\x02 \x01(\x03\x12\x1a\n\x12scheduled_event_id\x18\x03 \x01(\x03\x12\x18\n\x10started_event_id\x18\x04 \x01(\x03\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x46\n\x0eworker_version\x18\x06 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01"\x93\x01\n\x1bTimerStartedEventAttributes\x12\x10\n\x08timer_id\x18\x01 \x01(\t\x12\x38\n\x15start_to_fire_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration\x12(\n workflow_task_completed_event_id\x18\x03 \x01(\x03"G\n\x19TimerFiredEventAttributes\x12\x10\n\x08timer_id\x18\x01 \x01(\t\x12\x18\n\x10started_event_id\x18\x02 \x01(\x03"\x86\x01\n\x1cTimerCanceledEventAttributes\x12\x10\n\x08timer_id\x18\x01 \x01(\t\x12\x18\n\x10started_event_id\x18\x02 \x01(\x03\x12(\n workflow_task_completed_event_id\x18\x03 \x01(\x03\x12\x10\n\x08identity\x18\x04 \x01(\t"\xc7\x01\n/WorkflowExecutionCancelRequestedEventAttributes\x12\r\n\x05\x63\x61use\x18\x01 \x01(\t\x12#\n\x1b\x65xternal_initiated_event_id\x18\x02 \x01(\x03\x12N\n\x1b\x65xternal_workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x10\n\x08identity\x18\x04 \x01(\t"\x87\x01\n(WorkflowExecutionCanceledEventAttributes\x12(\n workflow_task_completed_event_id\x18\x01 \x01(\x03\x12\x31\n\x07\x64\x65tails\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads"\xe9\x02\n\x1dMarkerRecordedEventAttributes\x12\x13\n\x0bmarker_name\x18\x01 \x01(\t\x12T\n\x07\x64\x65tails\x18\x02 \x03(\x0b\x32\x43.temporal.api.history.v1.MarkerRecordedEventAttributes.DetailsEntry\x12(\n workflow_task_completed_event_id\x18\x03 \x01(\x03\x12.\n\x06header\x18\x04 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x31\n\x07\x66\x61ilure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x1aP\n\x0c\x44\x65tailsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12/\n\x05value\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads:\x02\x38\x01"\xbf\x02\n(WorkflowExecutionSignaledEventAttributes\x12\x13\n\x0bsignal_name\x18\x01 \x01(\t\x12/\n\x05input\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x03 \x01(\t\x12.\n\x06header\x18\x04 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\'\n\x1bskip_generate_workflow_task\x18\x05 \x01(\x08\x42\x02\x18\x01\x12N\n\x1b\x65xternal_workflow_execution\x18\x06 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x12\n\nrequest_id\x18\x07 \x01(\t"\x81\x01\n*WorkflowExecutionTerminatedEventAttributes\x12\x0e\n\x06reason\x18\x01 \x01(\t\x12\x31\n\x07\x64\x65tails\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x03 \x01(\t"\x9c\x02\n>RequestCancelExternalWorkflowExecutionInitiatedEventAttributes\x12(\n workflow_task_completed_event_id\x18\x01 \x01(\x03\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x07 \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x13\n\x07\x63ontrol\x18\x04 \x01(\tB\x02\x18\x01\x12\x1b\n\x13\x63hild_workflow_only\x18\x05 \x01(\x08\x12\x0e\n\x06reason\x18\x06 \x01(\t"\xda\x02\n;RequestCancelExternalWorkflowExecutionFailedEventAttributes\x12P\n\x05\x63\x61use\x18\x01 \x01(\x0e\x32\x41.temporal.api.enums.v1.CancelExternalWorkflowExecutionFailedCause\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03\x12\x11\n\tnamespace\x18\x03 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x07 \x01(\t\x12\x45\n\x12workflow_execution\x18\x04 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x1a\n\x12initiated_event_id\x18\x05 \x01(\x03\x12\x13\n\x07\x63ontrol\x18\x06 \x01(\tB\x02\x18\x01"\xc5\x01\n7ExternalWorkflowExecutionCancelRequestedEventAttributes\x12\x1a\n\x12initiated_event_id\x18\x01 \x01(\x03\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x04 \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution"\xfb\x02\n7SignalExternalWorkflowExecutionInitiatedEventAttributes\x12(\n workflow_task_completed_event_id\x18\x01 \x01(\x03\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\t \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x13\n\x0bsignal_name\x18\x04 \x01(\t\x12/\n\x05input\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x13\n\x07\x63ontrol\x18\x06 \x01(\tB\x02\x18\x01\x12\x1b\n\x13\x63hild_workflow_only\x18\x07 \x01(\x08\x12.\n\x06header\x18\x08 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header"\xd3\x02\n4SignalExternalWorkflowExecutionFailedEventAttributes\x12P\n\x05\x63\x61use\x18\x01 \x01(\x0e\x32\x41.temporal.api.enums.v1.SignalExternalWorkflowExecutionFailedCause\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03\x12\x11\n\tnamespace\x18\x03 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x07 \x01(\t\x12\x45\n\x12workflow_execution\x18\x04 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x1a\n\x12initiated_event_id\x18\x05 \x01(\x03\x12\x13\n\x07\x63ontrol\x18\x06 \x01(\tB\x02\x18\x01"\xd3\x01\n0ExternalWorkflowExecutionSignaledEventAttributes\x12\x1a\n\x12initiated_event_id\x18\x01 \x01(\x03\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x05 \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x13\n\x07\x63ontrol\x18\x04 \x01(\tB\x02\x18\x01"\x9e\x01\n-UpsertWorkflowSearchAttributesEventAttributes\x12(\n workflow_task_completed_event_id\x18\x01 \x01(\x03\x12\x43\n\x11search_attributes\x18\x02 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes"\x8a\x01\n)WorkflowPropertiesModifiedEventAttributes\x12(\n workflow_task_completed_event_id\x18\x01 \x01(\x03\x12\x33\n\rupserted_memo\x18\x02 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo"\xfc\t\n3StartChildWorkflowExecutionInitiatedEventAttributes\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x12 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x04 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12=\n\x1aworkflow_execution_timeout\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x45\n\x13parent_close_policy\x18\t \x01(\x0e\x32(.temporal.api.enums.v1.ParentClosePolicy\x12\x13\n\x07\x63ontrol\x18\n \x01(\tB\x02\x18\x01\x12(\n workflow_task_completed_event_id\x18\x0b \x01(\x03\x12N\n\x18workflow_id_reuse_policy\x18\x0c \x01(\x0e\x32,.temporal.api.enums.v1.WorkflowIdReusePolicy\x12\x39\n\x0cretry_policy\x18\r \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x15\n\rcron_schedule\x18\x0e \x01(\t\x12.\n\x06header\x18\x0f \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12*\n\x04memo\x18\x10 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x11 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x1c\n\x10inherit_build_id\x18\x13 \x01(\x08\x42\x02\x18\x01\x12\x32\n\x08priority\x18\x14 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12H\n\x14time_skipping_config\x18\x15 \x01(\x0b\x32*.temporal.api.common.v1.TimeSkippingConfig\x12]\n\x1ftime_skipping_state_propagation\x18\x17 \x01(\x0b\x32\x34.temporal.api.common.v1.TimeSkippingStatePropagation\x12I\n\x13versioning_override\x18\x18 \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverrideJ\x04\x08\x16\x10\x17R\x18initial_skipped_duration"\xd6\x02\n0StartChildWorkflowExecutionFailedEventAttributes\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x08 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12L\n\x05\x63\x61use\x18\x04 \x01(\x0e\x32=.temporal.api.enums.v1.StartChildWorkflowExecutionFailedCause\x12\x13\n\x07\x63ontrol\x18\x05 \x01(\tB\x02\x18\x01\x12\x1a\n\x12initiated_event_id\x18\x06 \x01(\x03\x12(\n workflow_task_completed_event_id\x18\x07 \x01(\x03"\xa7\x02\n,ChildWorkflowExecutionStartedEventAttributes\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x06 \x01(\t\x12\x1a\n\x12initiated_event_id\x18\x02 \x01(\x03\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x04 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12.\n\x06header\x18\x05 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header"\xc5\x02\n.ChildWorkflowExecutionCompletedEventAttributes\x12\x30\n\x06result\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x07 \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x04 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x1a\n\x12initiated_event_id\x18\x05 \x01(\x03\x12\x18\n\x10started_event_id\x18\x06 \x01(\x03"\xfb\x02\n+ChildWorkflowExecutionFailedEventAttributes\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x08 \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x04 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x1a\n\x12initiated_event_id\x18\x05 \x01(\x03\x12\x18\n\x10started_event_id\x18\x06 \x01(\x03\x12\x36\n\x0bretry_state\x18\x07 \x01(\x0e\x32!.temporal.api.enums.v1.RetryState"\xc5\x02\n-ChildWorkflowExecutionCanceledEventAttributes\x12\x31\n\x07\x64\x65tails\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x07 \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x04 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x1a\n\x12initiated_event_id\x18\x05 \x01(\x03\x12\x18\n\x10started_event_id\x18\x06 \x01(\x03"\xca\x02\n-ChildWorkflowExecutionTimedOutEventAttributes\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x07 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x1a\n\x12initiated_event_id\x18\x04 \x01(\x03\x12\x18\n\x10started_event_id\x18\x05 \x01(\x03\x12\x36\n\x0bretry_state\x18\x06 \x01(\x0e\x32!.temporal.api.enums.v1.RetryState"\x94\x02\n/ChildWorkflowExecutionTerminatedEventAttributes\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0cnamespace_id\x18\x06 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x1a\n\x12initiated_event_id\x18\x04 \x01(\x03\x12\x18\n\x10started_event_id\x18\x05 \x01(\x03"\xda\x05\n.WorkflowExecutionOptionsUpdatedEventAttributes\x12I\n\x13versioning_override\x18\x01 \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12!\n\x19unset_versioning_override\x18\x02 \x01(\x08\x12\x1b\n\x13\x61ttached_request_id\x18\x03 \x01(\t\x12G\n\x1d\x61ttached_completion_callbacks\x18\x04 \x03(\x0b\x32 .temporal.api.common.v1.Callback\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x32\n\x08priority\x18\x06 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12H\n\x14time_skipping_config\x18\x07 \x01(\x0b\x32*.temporal.api.common.v1.TimeSkippingConfig\x12$\n\x1ctime_skipping_config_updated\x18\t \x01(\x08\x12\x84\x01\n\x17workflow_update_options\x18\x08 \x03(\x0b\x32\x63.temporal.api.history.v1.WorkflowExecutionOptionsUpdatedEventAttributes.WorkflowUpdateOptionsUpdate\x1a\x96\x01\n\x1bWorkflowUpdateOptionsUpdate\x12\x11\n\tupdate_id\x18\x01 \x01(\t\x12\x1b\n\x13\x61ttached_request_id\x18\x02 \x01(\t\x12G\n\x1d\x61ttached_completion_callbacks\x18\x03 \x03(\x0b\x32 .temporal.api.common.v1.Callback"\xc0\x02\n3WorkflowPropertiesModifiedExternallyEventAttributes\x12\x16\n\x0enew_task_queue\x18\x01 \x01(\t\x12<\n\x19new_workflow_task_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration\x12;\n\x18new_workflow_run_timeout\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x41\n\x1enew_workflow_execution_timeout\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x33\n\rupserted_memo\x18\x05 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo"\x90\x01\n3ActivityPropertiesModifiedExternallyEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12=\n\x10new_retry_policy\x18\x02 \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy"\xdc\x01\n.WorkflowExecutionUpdateAcceptedEventAttributes\x12\x1c\n\x14protocol_instance_id\x18\x01 \x01(\t\x12#\n\x1b\x61\x63\x63\x65pted_request_message_id\x18\x02 \x01(\t\x12,\n$accepted_request_sequencing_event_id\x18\x03 \x01(\x03\x12\x39\n\x10\x61\x63\x63\x65pted_request\x18\x04 \x01(\x0b\x32\x1f.temporal.api.update.v1.Request"\xaa\x01\n/WorkflowExecutionUpdateCompletedEventAttributes\x12*\n\x04meta\x18\x01 \x01(\x0b\x32\x1c.temporal.api.update.v1.Meta\x12\x19\n\x11\x61\x63\x63\x65pted_event_id\x18\x03 \x01(\x03\x12\x30\n\x07outcome\x18\x02 \x01(\x0b\x32\x1f.temporal.api.update.v1.Outcome"\x8f\x02\n.WorkflowExecutionUpdateRejectedEventAttributes\x12\x1c\n\x14protocol_instance_id\x18\x01 \x01(\t\x12#\n\x1brejected_request_message_id\x18\x02 \x01(\t\x12,\n$rejected_request_sequencing_event_id\x18\x03 \x01(\x03\x12\x39\n\x10rejected_request\x18\x04 \x01(\x0b\x32\x1f.temporal.api.update.v1.Request\x12\x31\n\x07\x66\x61ilure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.Failure"\xa4\x01\n.WorkflowExecutionUpdateAdmittedEventAttributes\x12\x30\n\x07request\x18\x01 \x01(\x0b\x32\x1f.temporal.api.update.v1.Request\x12@\n\x06origin\x18\x02 \x01(\x0e\x32\x30.temporal.api.enums.v1.UpdateAdmittedEventOrigin"^\n&WorkflowExecutionPausedEventAttributes\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x12\n\nrequest_id\x18\x03 \x01(\t"`\n(WorkflowExecutionUnpausedEventAttributes\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x12\n\nrequest_id\x18\x03 \x01(\t"\xc5\x01\n8WorkflowExecutionTimeSkippingTransitionedEventAttributes\x12/\n\x0btarget_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12#\n\x1b\x64isabled_after_fast_forward\x18\x02 \x01(\x08\x12\x33\n\x0fwall_clock_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xb4\x04\n&NexusOperationScheduledEventAttributes\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\x11\n\toperation\x18\x03 \x01(\t\x12.\n\x05input\x18\x04 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12<\n\x19schedule_to_close_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x66\n\x0cnexus_header\x18\x06 \x03(\x0b\x32P.temporal.api.history.v1.NexusOperationScheduledEventAttributes.NexusHeaderEntry\x12(\n workflow_task_completed_event_id\x18\x07 \x01(\x03\x12\x12\n\nrequest_id\x18\x08 \x01(\t\x12\x13\n\x0b\x65ndpoint_id\x18\t \x01(\t\x12<\n\x19schedule_to_start_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\x0b \x01(\x0b\x32\x19.google.protobuf.Duration\x1a\x32\n\x10NexusHeaderEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"\x89\x01\n$NexusOperationStartedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x18\n\x0coperation_id\x18\x03 \x01(\tB\x02\x18\x01\x12\x12\n\nrequest_id\x18\x04 \x01(\t\x12\x17\n\x0foperation_token\x18\x05 \x01(\t"\x89\x01\n&NexusOperationCompletedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12/\n\x06result\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\x12\n\nrequest_id\x18\x03 \x01(\t"\x88\x01\n#NexusOperationFailedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x31\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x12\n\nrequest_id\x18\x03 \x01(\t"\x8a\x01\n%NexusOperationTimedOutEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x31\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x12\n\nrequest_id\x18\x03 \x01(\t"\x8a\x01\n%NexusOperationCanceledEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12\x31\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x12\n\nrequest_id\x18\x03 \x01(\t"t\n,NexusOperationCancelRequestedEventAttributes\x12\x1a\n\x12scheduled_event_id\x18\x01 \x01(\x03\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03"\x97\x01\n3NexusOperationCancelRequestCompletedEventAttributes\x12\x1a\n\x12requested_event_id\x18\x01 \x01(\x03\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03\x12\x1a\n\x12scheduled_event_id\x18\x03 \x01(\x03"\xc7\x01\n0NexusOperationCancelRequestFailedEventAttributes\x12\x1a\n\x12requested_event_id\x18\x01 \x01(\x03\x12(\n workflow_task_completed_event_id\x18\x02 \x01(\x03\x12\x31\n\x07\x66\x61ilure\x18\x03 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x1a\n\x12scheduled_event_id\x18\x04 \x01(\x03"\xca?\n\x0cHistoryEvent\x12\x10\n\x08\x65vent_id\x18\x01 \x01(\x03\x12.\n\nevent_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x34\n\nevent_type\x18\x03 \x01(\x0e\x32 .temporal.api.enums.v1.EventType\x12\x0f\n\x07version\x18\x04 \x01(\x03\x12\x0f\n\x07task_id\x18\x05 \x01(\x03\x12\x1a\n\x11worker_may_ignore\x18\xac\x02 \x01(\x08\x12\x39\n\ruser_metadata\x18\xad\x02 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12,\n\x05links\x18\xae\x02 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x12\x35\n\tprincipal\x18\xaf\x02 \x01(\x0b\x32!.temporal.api.common.v1.Principal\x12\x43\n\x13\x65vent_group_markers\x18\xb0\x02 \x03(\x0b\x32%.temporal.api.sdk.v1.EventGroupMarker\x12w\n+workflow_execution_started_event_attributes\x18\x06 \x01(\x0b\x32@.temporal.api.history.v1.WorkflowExecutionStartedEventAttributesH\x00\x12{\n-workflow_execution_completed_event_attributes\x18\x07 \x01(\x0b\x32\x42.temporal.api.history.v1.WorkflowExecutionCompletedEventAttributesH\x00\x12u\n*workflow_execution_failed_event_attributes\x18\x08 \x01(\x0b\x32?.temporal.api.history.v1.WorkflowExecutionFailedEventAttributesH\x00\x12z\n-workflow_execution_timed_out_event_attributes\x18\t \x01(\x0b\x32\x41.temporal.api.history.v1.WorkflowExecutionTimedOutEventAttributesH\x00\x12q\n(workflow_task_scheduled_event_attributes\x18\n \x01(\x0b\x32=.temporal.api.history.v1.WorkflowTaskScheduledEventAttributesH\x00\x12m\n&workflow_task_started_event_attributes\x18\x0b \x01(\x0b\x32;.temporal.api.history.v1.WorkflowTaskStartedEventAttributesH\x00\x12q\n(workflow_task_completed_event_attributes\x18\x0c \x01(\x0b\x32=.temporal.api.history.v1.WorkflowTaskCompletedEventAttributesH\x00\x12p\n(workflow_task_timed_out_event_attributes\x18\r \x01(\x0b\x32<.temporal.api.history.v1.WorkflowTaskTimedOutEventAttributesH\x00\x12k\n%workflow_task_failed_event_attributes\x18\x0e \x01(\x0b\x32:.temporal.api.history.v1.WorkflowTaskFailedEventAttributesH\x00\x12q\n(activity_task_scheduled_event_attributes\x18\x0f \x01(\x0b\x32=.temporal.api.history.v1.ActivityTaskScheduledEventAttributesH\x00\x12m\n&activity_task_started_event_attributes\x18\x10 \x01(\x0b\x32;.temporal.api.history.v1.ActivityTaskStartedEventAttributesH\x00\x12q\n(activity_task_completed_event_attributes\x18\x11 \x01(\x0b\x32=.temporal.api.history.v1.ActivityTaskCompletedEventAttributesH\x00\x12k\n%activity_task_failed_event_attributes\x18\x12 \x01(\x0b\x32:.temporal.api.history.v1.ActivityTaskFailedEventAttributesH\x00\x12p\n(activity_task_timed_out_event_attributes\x18\x13 \x01(\x0b\x32<.temporal.api.history.v1.ActivityTaskTimedOutEventAttributesH\x00\x12^\n\x1etimer_started_event_attributes\x18\x14 \x01(\x0b\x32\x34.temporal.api.history.v1.TimerStartedEventAttributesH\x00\x12Z\n\x1ctimer_fired_event_attributes\x18\x15 \x01(\x0b\x32\x32.temporal.api.history.v1.TimerFiredEventAttributesH\x00\x12~\n/activity_task_cancel_requested_event_attributes\x18\x16 \x01(\x0b\x32\x43.temporal.api.history.v1.ActivityTaskCancelRequestedEventAttributesH\x00\x12o\n\'activity_task_canceled_event_attributes\x18\x17 \x01(\x0b\x32<.temporal.api.history.v1.ActivityTaskCanceledEventAttributesH\x00\x12`\n\x1ftimer_canceled_event_attributes\x18\x18 \x01(\x0b\x32\x35.temporal.api.history.v1.TimerCanceledEventAttributesH\x00\x12\x62\n marker_recorded_event_attributes\x18\x19 \x01(\x0b\x32\x36.temporal.api.history.v1.MarkerRecordedEventAttributesH\x00\x12y\n,workflow_execution_signaled_event_attributes\x18\x1a \x01(\x0b\x32\x41.temporal.api.history.v1.WorkflowExecutionSignaledEventAttributesH\x00\x12}\n.workflow_execution_terminated_event_attributes\x18\x1b \x01(\x0b\x32\x43.temporal.api.history.v1.WorkflowExecutionTerminatedEventAttributesH\x00\x12\x88\x01\n4workflow_execution_cancel_requested_event_attributes\x18\x1c \x01(\x0b\x32H.temporal.api.history.v1.WorkflowExecutionCancelRequestedEventAttributesH\x00\x12y\n,workflow_execution_canceled_event_attributes\x18\x1d \x01(\x0b\x32\x41.temporal.api.history.v1.WorkflowExecutionCanceledEventAttributesH\x00\x12\xa8\x01\nErequest_cancel_external_workflow_execution_initiated_event_attributes\x18\x1e \x01(\x0b\x32W.temporal.api.history.v1.RequestCancelExternalWorkflowExecutionInitiatedEventAttributesH\x00\x12\xa2\x01\nBrequest_cancel_external_workflow_execution_failed_event_attributes\x18\x1f \x01(\x0b\x32T.temporal.api.history.v1.RequestCancelExternalWorkflowExecutionFailedEventAttributesH\x00\x12\x99\x01\n=external_workflow_execution_cancel_requested_event_attributes\x18 \x01(\x0b\x32P.temporal.api.history.v1.ExternalWorkflowExecutionCancelRequestedEventAttributesH\x00\x12\x87\x01\n4workflow_execution_continued_as_new_event_attributes\x18! \x01(\x0b\x32G.temporal.api.history.v1.WorkflowExecutionContinuedAsNewEventAttributesH\x00\x12\x91\x01\n9start_child_workflow_execution_initiated_event_attributes\x18" \x01(\x0b\x32L.temporal.api.history.v1.StartChildWorkflowExecutionInitiatedEventAttributesH\x00\x12\x8b\x01\n6start_child_workflow_execution_failed_event_attributes\x18# \x01(\x0b\x32I.temporal.api.history.v1.StartChildWorkflowExecutionFailedEventAttributesH\x00\x12\x82\x01\n1child_workflow_execution_started_event_attributes\x18$ \x01(\x0b\x32\x45.temporal.api.history.v1.ChildWorkflowExecutionStartedEventAttributesH\x00\x12\x86\x01\n3child_workflow_execution_completed_event_attributes\x18% \x01(\x0b\x32G.temporal.api.history.v1.ChildWorkflowExecutionCompletedEventAttributesH\x00\x12\x80\x01\n0child_workflow_execution_failed_event_attributes\x18& \x01(\x0b\x32\x44.temporal.api.history.v1.ChildWorkflowExecutionFailedEventAttributesH\x00\x12\x84\x01\n2child_workflow_execution_canceled_event_attributes\x18\' \x01(\x0b\x32\x46.temporal.api.history.v1.ChildWorkflowExecutionCanceledEventAttributesH\x00\x12\x85\x01\n3child_workflow_execution_timed_out_event_attributes\x18( \x01(\x0b\x32\x46.temporal.api.history.v1.ChildWorkflowExecutionTimedOutEventAttributesH\x00\x12\x88\x01\n4child_workflow_execution_terminated_event_attributes\x18) \x01(\x0b\x32H.temporal.api.history.v1.ChildWorkflowExecutionTerminatedEventAttributesH\x00\x12\x99\x01\n=signal_external_workflow_execution_initiated_event_attributes\x18* \x01(\x0b\x32P.temporal.api.history.v1.SignalExternalWorkflowExecutionInitiatedEventAttributesH\x00\x12\x93\x01\n:signal_external_workflow_execution_failed_event_attributes\x18+ \x01(\x0b\x32M.temporal.api.history.v1.SignalExternalWorkflowExecutionFailedEventAttributesH\x00\x12\x8a\x01\n5external_workflow_execution_signaled_event_attributes\x18, \x01(\x0b\x32I.temporal.api.history.v1.ExternalWorkflowExecutionSignaledEventAttributesH\x00\x12\x84\x01\n2upsert_workflow_search_attributes_event_attributes\x18- \x01(\x0b\x32\x46.temporal.api.history.v1.UpsertWorkflowSearchAttributesEventAttributesH\x00\x12\x86\x01\n3workflow_execution_update_accepted_event_attributes\x18. \x01(\x0b\x32G.temporal.api.history.v1.WorkflowExecutionUpdateAcceptedEventAttributesH\x00\x12\x86\x01\n3workflow_execution_update_rejected_event_attributes\x18/ \x01(\x0b\x32G.temporal.api.history.v1.WorkflowExecutionUpdateRejectedEventAttributesH\x00\x12\x88\x01\n4workflow_execution_update_completed_event_attributes\x18\x30 \x01(\x0b\x32H.temporal.api.history.v1.WorkflowExecutionUpdateCompletedEventAttributesH\x00\x12\x90\x01\n8workflow_properties_modified_externally_event_attributes\x18\x31 \x01(\x0b\x32L.temporal.api.history.v1.WorkflowPropertiesModifiedExternallyEventAttributesH\x00\x12\x90\x01\n8activity_properties_modified_externally_event_attributes\x18\x32 \x01(\x0b\x32L.temporal.api.history.v1.ActivityPropertiesModifiedExternallyEventAttributesH\x00\x12{\n-workflow_properties_modified_event_attributes\x18\x33 \x01(\x0b\x32\x42.temporal.api.history.v1.WorkflowPropertiesModifiedEventAttributesH\x00\x12\x86\x01\n3workflow_execution_update_admitted_event_attributes\x18\x34 \x01(\x0b\x32G.temporal.api.history.v1.WorkflowExecutionUpdateAdmittedEventAttributesH\x00\x12u\n*nexus_operation_scheduled_event_attributes\x18\x35 \x01(\x0b\x32?.temporal.api.history.v1.NexusOperationScheduledEventAttributesH\x00\x12q\n(nexus_operation_started_event_attributes\x18\x36 \x01(\x0b\x32=.temporal.api.history.v1.NexusOperationStartedEventAttributesH\x00\x12u\n*nexus_operation_completed_event_attributes\x18\x37 \x01(\x0b\x32?.temporal.api.history.v1.NexusOperationCompletedEventAttributesH\x00\x12o\n\'nexus_operation_failed_event_attributes\x18\x38 \x01(\x0b\x32<.temporal.api.history.v1.NexusOperationFailedEventAttributesH\x00\x12s\n)nexus_operation_canceled_event_attributes\x18\x39 \x01(\x0b\x32>.temporal.api.history.v1.NexusOperationCanceledEventAttributesH\x00\x12t\n*nexus_operation_timed_out_event_attributes\x18: \x01(\x0b\x32>.temporal.api.history.v1.NexusOperationTimedOutEventAttributesH\x00\x12\x82\x01\n1nexus_operation_cancel_requested_event_attributes\x18; \x01(\x0b\x32\x45.temporal.api.history.v1.NexusOperationCancelRequestedEventAttributesH\x00\x12\x86\x01\n3workflow_execution_options_updated_event_attributes\x18< \x01(\x0b\x32G.temporal.api.history.v1.WorkflowExecutionOptionsUpdatedEventAttributesH\x00\x12\x91\x01\n9nexus_operation_cancel_request_completed_event_attributes\x18= \x01(\x0b\x32L.temporal.api.history.v1.NexusOperationCancelRequestCompletedEventAttributesH\x00\x12\x8b\x01\n6nexus_operation_cancel_request_failed_event_attributes\x18> \x01(\x0b\x32I.temporal.api.history.v1.NexusOperationCancelRequestFailedEventAttributesH\x00\x12u\n*workflow_execution_paused_event_attributes\x18? \x01(\x0b\x32?.temporal.api.history.v1.WorkflowExecutionPausedEventAttributesH\x00\x12y\n,workflow_execution_unpaused_event_attributes\x18@ \x01(\x0b\x32\x41.temporal.api.history.v1.WorkflowExecutionUnpausedEventAttributesH\x00\x12\x9b\x01\n>workflow_execution_time_skipping_transitioned_event_attributes\x18\x41 \x01(\x0b\x32Q.temporal.api.history.v1.WorkflowExecutionTimeSkippingTransitionedEventAttributesH\x00\x42\x0c\n\nattributes"@\n\x07History\x12\x35\n\x06\x65vents\x18\x01 \x03(\x0b\x32%.temporal.api.history.v1.HistoryEventB\x8e\x01\n\x1aio.temporal.api.history.v1B\x0cMessageProtoP\x01Z%go.temporal.io/api/history/v1;history\xaa\x02\x19Temporalio.Api.History.V1\xea\x02\x1cTemporalio::Api::History::V1b\x06proto3' ) @@ -1207,140 +1210,140 @@ _NEXUSOPERATIONSTARTEDEVENTATTRIBUTES.fields_by_name[ "operation_id" ]._serialized_options = b"\030\001" - _WORKFLOWEXECUTIONSTARTEDEVENTATTRIBUTES._serialized_start = 617 - _WORKFLOWEXECUTIONSTARTEDEVENTATTRIBUTES._serialized_end = 2947 - _DECLINEDTARGETVERSIONUPGRADE._serialized_start = 2950 - _DECLINEDTARGETVERSIONUPGRADE._serialized_end = 3086 - _WORKFLOWEXECUTIONCOMPLETEDEVENTATTRIBUTES._serialized_start = 3089 - _WORKFLOWEXECUTIONCOMPLETEDEVENTATTRIBUTES._serialized_end = 3254 - _WORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_start = 3257 - _WORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_end = 3476 - _WORKFLOWEXECUTIONTIMEDOUTEVENTATTRIBUTES._serialized_start = 3479 - _WORKFLOWEXECUTIONTIMEDOUTEVENTATTRIBUTES._serialized_end = 3607 - _WORKFLOWEXECUTIONCONTINUEDASNEWEVENTATTRIBUTES._serialized_start = 3610 - _WORKFLOWEXECUTIONCONTINUEDASNEWEVENTATTRIBUTES._serialized_end = 4543 - _WORKFLOWTASKSCHEDULEDEVENTATTRIBUTES._serialized_start = 4546 - _WORKFLOWTASKSCHEDULEDEVENTATTRIBUTES._serialized_end = 4718 - _WORKFLOWTASKSTARTEDEVENTATTRIBUTES._serialized_start = 4721 - _WORKFLOWTASKSTARTEDEVENTATTRIBUTES._serialized_end = 5137 - _WORKFLOWTASKCOMPLETEDEVENTATTRIBUTES._serialized_start = 5140 - _WORKFLOWTASKCOMPLETEDEVENTATTRIBUTES._serialized_end = 5782 - _WORKFLOWTASKTIMEDOUTEVENTATTRIBUTES._serialized_start = 5785 - _WORKFLOWTASKTIMEDOUTEVENTATTRIBUTES._serialized_end = 5934 - _WORKFLOWTASKFAILEDEVENTATTRIBUTES._serialized_start = 5937 - _WORKFLOWTASKFAILEDEVENTATTRIBUTES._serialized_end = 6328 - _ACTIVITYTASKSCHEDULEDEVENTATTRIBUTES._serialized_start = 6331 - _ACTIVITYTASKSCHEDULEDEVENTATTRIBUTES._serialized_end = 7037 - _ACTIVITYTASKSTARTEDEVENTATTRIBUTES._serialized_start = 7040 - _ACTIVITYTASKSTARTEDEVENTATTRIBUTES._serialized_end = 7326 - _ACTIVITYTASKCOMPLETEDEVENTATTRIBUTES._serialized_start = 7329 - _ACTIVITYTASKCOMPLETEDEVENTATTRIBUTES._serialized_end = 7561 - _ACTIVITYTASKFAILEDEVENTATTRIBUTES._serialized_start = 7564 - _ACTIVITYTASKFAILEDEVENTATTRIBUTES._serialized_end = 7850 - _ACTIVITYTASKTIMEDOUTEVENTATTRIBUTES._serialized_start = 7853 - _ACTIVITYTASKTIMEDOUTEVENTATTRIBUTES._serialized_end = 8051 - _ACTIVITYTASKCANCELREQUESTEDEVENTATTRIBUTES._serialized_start = 8053 - _ACTIVITYTASKCANCELREQUESTEDEVENTATTRIBUTES._serialized_end = 8167 - _ACTIVITYTASKCANCELEDEVENTATTRIBUTES._serialized_start = 8170 - _ACTIVITYTASKCANCELEDEVENTATTRIBUTES._serialized_end = 8444 - _TIMERSTARTEDEVENTATTRIBUTES._serialized_start = 8447 - _TIMERSTARTEDEVENTATTRIBUTES._serialized_end = 8594 - _TIMERFIREDEVENTATTRIBUTES._serialized_start = 8596 - _TIMERFIREDEVENTATTRIBUTES._serialized_end = 8667 - _TIMERCANCELEDEVENTATTRIBUTES._serialized_start = 8670 - _TIMERCANCELEDEVENTATTRIBUTES._serialized_end = 8804 - _WORKFLOWEXECUTIONCANCELREQUESTEDEVENTATTRIBUTES._serialized_start = 8807 - _WORKFLOWEXECUTIONCANCELREQUESTEDEVENTATTRIBUTES._serialized_end = 9006 - _WORKFLOWEXECUTIONCANCELEDEVENTATTRIBUTES._serialized_start = 9009 - _WORKFLOWEXECUTIONCANCELEDEVENTATTRIBUTES._serialized_end = 9144 - _MARKERRECORDEDEVENTATTRIBUTES._serialized_start = 9147 - _MARKERRECORDEDEVENTATTRIBUTES._serialized_end = 9508 - _MARKERRECORDEDEVENTATTRIBUTES_DETAILSENTRY._serialized_start = 9428 - _MARKERRECORDEDEVENTATTRIBUTES_DETAILSENTRY._serialized_end = 9508 - _WORKFLOWEXECUTIONSIGNALEDEVENTATTRIBUTES._serialized_start = 9511 - _WORKFLOWEXECUTIONSIGNALEDEVENTATTRIBUTES._serialized_end = 9830 - _WORKFLOWEXECUTIONTERMINATEDEVENTATTRIBUTES._serialized_start = 9833 - _WORKFLOWEXECUTIONTERMINATEDEVENTATTRIBUTES._serialized_end = 9962 - _REQUESTCANCELEXTERNALWORKFLOWEXECUTIONINITIATEDEVENTATTRIBUTES._serialized_start = 9965 + _WORKFLOWEXECUTIONSTARTEDEVENTATTRIBUTES._serialized_start = 663 + _WORKFLOWEXECUTIONSTARTEDEVENTATTRIBUTES._serialized_end = 3057 + _DECLINEDTARGETVERSIONUPGRADE._serialized_start = 3060 + _DECLINEDTARGETVERSIONUPGRADE._serialized_end = 3196 + _WORKFLOWEXECUTIONCOMPLETEDEVENTATTRIBUTES._serialized_start = 3199 + _WORKFLOWEXECUTIONCOMPLETEDEVENTATTRIBUTES._serialized_end = 3364 + _WORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_start = 3367 + _WORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_end = 3586 + _WORKFLOWEXECUTIONTIMEDOUTEVENTATTRIBUTES._serialized_start = 3589 + _WORKFLOWEXECUTIONTIMEDOUTEVENTATTRIBUTES._serialized_end = 3717 + _WORKFLOWEXECUTIONCONTINUEDASNEWEVENTATTRIBUTES._serialized_start = 3720 + _WORKFLOWEXECUTIONCONTINUEDASNEWEVENTATTRIBUTES._serialized_end = 4653 + _WORKFLOWTASKSCHEDULEDEVENTATTRIBUTES._serialized_start = 4656 + _WORKFLOWTASKSCHEDULEDEVENTATTRIBUTES._serialized_end = 4828 + _WORKFLOWTASKSTARTEDEVENTATTRIBUTES._serialized_start = 4831 + _WORKFLOWTASKSTARTEDEVENTATTRIBUTES._serialized_end = 5247 + _WORKFLOWTASKCOMPLETEDEVENTATTRIBUTES._serialized_start = 5250 + _WORKFLOWTASKCOMPLETEDEVENTATTRIBUTES._serialized_end = 5892 + _WORKFLOWTASKTIMEDOUTEVENTATTRIBUTES._serialized_start = 5895 + _WORKFLOWTASKTIMEDOUTEVENTATTRIBUTES._serialized_end = 6044 + _WORKFLOWTASKFAILEDEVENTATTRIBUTES._serialized_start = 6047 + _WORKFLOWTASKFAILEDEVENTATTRIBUTES._serialized_end = 6438 + _ACTIVITYTASKSCHEDULEDEVENTATTRIBUTES._serialized_start = 6441 + _ACTIVITYTASKSCHEDULEDEVENTATTRIBUTES._serialized_end = 7147 + _ACTIVITYTASKSTARTEDEVENTATTRIBUTES._serialized_start = 7150 + _ACTIVITYTASKSTARTEDEVENTATTRIBUTES._serialized_end = 7436 + _ACTIVITYTASKCOMPLETEDEVENTATTRIBUTES._serialized_start = 7439 + _ACTIVITYTASKCOMPLETEDEVENTATTRIBUTES._serialized_end = 7671 + _ACTIVITYTASKFAILEDEVENTATTRIBUTES._serialized_start = 7674 + _ACTIVITYTASKFAILEDEVENTATTRIBUTES._serialized_end = 7960 + _ACTIVITYTASKTIMEDOUTEVENTATTRIBUTES._serialized_start = 7963 + _ACTIVITYTASKTIMEDOUTEVENTATTRIBUTES._serialized_end = 8161 + _ACTIVITYTASKCANCELREQUESTEDEVENTATTRIBUTES._serialized_start = 8163 + _ACTIVITYTASKCANCELREQUESTEDEVENTATTRIBUTES._serialized_end = 8277 + _ACTIVITYTASKCANCELEDEVENTATTRIBUTES._serialized_start = 8280 + _ACTIVITYTASKCANCELEDEVENTATTRIBUTES._serialized_end = 8554 + _TIMERSTARTEDEVENTATTRIBUTES._serialized_start = 8557 + _TIMERSTARTEDEVENTATTRIBUTES._serialized_end = 8704 + _TIMERFIREDEVENTATTRIBUTES._serialized_start = 8706 + _TIMERFIREDEVENTATTRIBUTES._serialized_end = 8777 + _TIMERCANCELEDEVENTATTRIBUTES._serialized_start = 8780 + _TIMERCANCELEDEVENTATTRIBUTES._serialized_end = 8914 + _WORKFLOWEXECUTIONCANCELREQUESTEDEVENTATTRIBUTES._serialized_start = 8917 + _WORKFLOWEXECUTIONCANCELREQUESTEDEVENTATTRIBUTES._serialized_end = 9116 + _WORKFLOWEXECUTIONCANCELEDEVENTATTRIBUTES._serialized_start = 9119 + _WORKFLOWEXECUTIONCANCELEDEVENTATTRIBUTES._serialized_end = 9254 + _MARKERRECORDEDEVENTATTRIBUTES._serialized_start = 9257 + _MARKERRECORDEDEVENTATTRIBUTES._serialized_end = 9618 + _MARKERRECORDEDEVENTATTRIBUTES_DETAILSENTRY._serialized_start = 9538 + _MARKERRECORDEDEVENTATTRIBUTES_DETAILSENTRY._serialized_end = 9618 + _WORKFLOWEXECUTIONSIGNALEDEVENTATTRIBUTES._serialized_start = 9621 + _WORKFLOWEXECUTIONSIGNALEDEVENTATTRIBUTES._serialized_end = 9940 + _WORKFLOWEXECUTIONTERMINATEDEVENTATTRIBUTES._serialized_start = 9943 + _WORKFLOWEXECUTIONTERMINATEDEVENTATTRIBUTES._serialized_end = 10072 + _REQUESTCANCELEXTERNALWORKFLOWEXECUTIONINITIATEDEVENTATTRIBUTES._serialized_start = 10075 _REQUESTCANCELEXTERNALWORKFLOWEXECUTIONINITIATEDEVENTATTRIBUTES._serialized_end = ( - 10249 + 10359 ) _REQUESTCANCELEXTERNALWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_start = ( - 10252 + 10362 ) - _REQUESTCANCELEXTERNALWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_end = 10598 - _EXTERNALWORKFLOWEXECUTIONCANCELREQUESTEDEVENTATTRIBUTES._serialized_start = 10601 - _EXTERNALWORKFLOWEXECUTIONCANCELREQUESTEDEVENTATTRIBUTES._serialized_end = 10798 - _SIGNALEXTERNALWORKFLOWEXECUTIONINITIATEDEVENTATTRIBUTES._serialized_start = 10801 - _SIGNALEXTERNALWORKFLOWEXECUTIONINITIATEDEVENTATTRIBUTES._serialized_end = 11180 - _SIGNALEXTERNALWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_start = 11183 - _SIGNALEXTERNALWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_end = 11522 - _EXTERNALWORKFLOWEXECUTIONSIGNALEDEVENTATTRIBUTES._serialized_start = 11525 - _EXTERNALWORKFLOWEXECUTIONSIGNALEDEVENTATTRIBUTES._serialized_end = 11736 - _UPSERTWORKFLOWSEARCHATTRIBUTESEVENTATTRIBUTES._serialized_start = 11739 - _UPSERTWORKFLOWSEARCHATTRIBUTESEVENTATTRIBUTES._serialized_end = 11897 - _WORKFLOWPROPERTIESMODIFIEDEVENTATTRIBUTES._serialized_start = 11900 - _WORKFLOWPROPERTIESMODIFIEDEVENTATTRIBUTES._serialized_end = 12038 - _STARTCHILDWORKFLOWEXECUTIONINITIATEDEVENTATTRIBUTES._serialized_start = 12041 - _STARTCHILDWORKFLOWEXECUTIONINITIATEDEVENTATTRIBUTES._serialized_end = 13178 - _STARTCHILDWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_start = 13181 - _STARTCHILDWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_end = 13523 - _CHILDWORKFLOWEXECUTIONSTARTEDEVENTATTRIBUTES._serialized_start = 13526 - _CHILDWORKFLOWEXECUTIONSTARTEDEVENTATTRIBUTES._serialized_end = 13821 - _CHILDWORKFLOWEXECUTIONCOMPLETEDEVENTATTRIBUTES._serialized_start = 13824 - _CHILDWORKFLOWEXECUTIONCOMPLETEDEVENTATTRIBUTES._serialized_end = 14149 - _CHILDWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_start = 14152 - _CHILDWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_end = 14531 - _CHILDWORKFLOWEXECUTIONCANCELEDEVENTATTRIBUTES._serialized_start = 14534 - _CHILDWORKFLOWEXECUTIONCANCELEDEVENTATTRIBUTES._serialized_end = 14859 - _CHILDWORKFLOWEXECUTIONTIMEDOUTEVENTATTRIBUTES._serialized_start = 14862 - _CHILDWORKFLOWEXECUTIONTIMEDOUTEVENTATTRIBUTES._serialized_end = 15192 - _CHILDWORKFLOWEXECUTIONTERMINATEDEVENTATTRIBUTES._serialized_start = 15195 - _CHILDWORKFLOWEXECUTIONTERMINATEDEVENTATTRIBUTES._serialized_end = 15471 - _WORKFLOWEXECUTIONOPTIONSUPDATEDEVENTATTRIBUTES._serialized_start = 15474 - _WORKFLOWEXECUTIONOPTIONSUPDATEDEVENTATTRIBUTES._serialized_end = 16168 - _WORKFLOWEXECUTIONOPTIONSUPDATEDEVENTATTRIBUTES_WORKFLOWUPDATEOPTIONSUPDATE._serialized_start = 16018 - _WORKFLOWEXECUTIONOPTIONSUPDATEDEVENTATTRIBUTES_WORKFLOWUPDATEOPTIONSUPDATE._serialized_end = 16168 - _WORKFLOWPROPERTIESMODIFIEDEXTERNALLYEVENTATTRIBUTES._serialized_start = 16171 - _WORKFLOWPROPERTIESMODIFIEDEXTERNALLYEVENTATTRIBUTES._serialized_end = 16491 - _ACTIVITYPROPERTIESMODIFIEDEXTERNALLYEVENTATTRIBUTES._serialized_start = 16494 - _ACTIVITYPROPERTIESMODIFIEDEXTERNALLYEVENTATTRIBUTES._serialized_end = 16638 - _WORKFLOWEXECUTIONUPDATEACCEPTEDEVENTATTRIBUTES._serialized_start = 16641 - _WORKFLOWEXECUTIONUPDATEACCEPTEDEVENTATTRIBUTES._serialized_end = 16861 - _WORKFLOWEXECUTIONUPDATECOMPLETEDEVENTATTRIBUTES._serialized_start = 16864 - _WORKFLOWEXECUTIONUPDATECOMPLETEDEVENTATTRIBUTES._serialized_end = 17034 - _WORKFLOWEXECUTIONUPDATEREJECTEDEVENTATTRIBUTES._serialized_start = 17037 - _WORKFLOWEXECUTIONUPDATEREJECTEDEVENTATTRIBUTES._serialized_end = 17308 - _WORKFLOWEXECUTIONUPDATEADMITTEDEVENTATTRIBUTES._serialized_start = 17311 - _WORKFLOWEXECUTIONUPDATEADMITTEDEVENTATTRIBUTES._serialized_end = 17475 - _WORKFLOWEXECUTIONPAUSEDEVENTATTRIBUTES._serialized_start = 17477 - _WORKFLOWEXECUTIONPAUSEDEVENTATTRIBUTES._serialized_end = 17571 - _WORKFLOWEXECUTIONUNPAUSEDEVENTATTRIBUTES._serialized_start = 17573 - _WORKFLOWEXECUTIONUNPAUSEDEVENTATTRIBUTES._serialized_end = 17669 - _WORKFLOWEXECUTIONTIMESKIPPINGTRANSITIONEDEVENTATTRIBUTES._serialized_start = 17672 - _WORKFLOWEXECUTIONTIMESKIPPINGTRANSITIONEDEVENTATTRIBUTES._serialized_end = 17862 - _NEXUSOPERATIONSCHEDULEDEVENTATTRIBUTES._serialized_start = 17865 - _NEXUSOPERATIONSCHEDULEDEVENTATTRIBUTES._serialized_end = 18429 - _NEXUSOPERATIONSCHEDULEDEVENTATTRIBUTES_NEXUSHEADERENTRY._serialized_start = 18379 - _NEXUSOPERATIONSCHEDULEDEVENTATTRIBUTES_NEXUSHEADERENTRY._serialized_end = 18429 - _NEXUSOPERATIONSTARTEDEVENTATTRIBUTES._serialized_start = 18432 - _NEXUSOPERATIONSTARTEDEVENTATTRIBUTES._serialized_end = 18569 - _NEXUSOPERATIONCOMPLETEDEVENTATTRIBUTES._serialized_start = 18572 - _NEXUSOPERATIONCOMPLETEDEVENTATTRIBUTES._serialized_end = 18709 - _NEXUSOPERATIONFAILEDEVENTATTRIBUTES._serialized_start = 18712 - _NEXUSOPERATIONFAILEDEVENTATTRIBUTES._serialized_end = 18848 - _NEXUSOPERATIONTIMEDOUTEVENTATTRIBUTES._serialized_start = 18851 - _NEXUSOPERATIONTIMEDOUTEVENTATTRIBUTES._serialized_end = 18989 - _NEXUSOPERATIONCANCELEDEVENTATTRIBUTES._serialized_start = 18992 - _NEXUSOPERATIONCANCELEDEVENTATTRIBUTES._serialized_end = 19130 - _NEXUSOPERATIONCANCELREQUESTEDEVENTATTRIBUTES._serialized_start = 19132 - _NEXUSOPERATIONCANCELREQUESTEDEVENTATTRIBUTES._serialized_end = 19248 - _NEXUSOPERATIONCANCELREQUESTCOMPLETEDEVENTATTRIBUTES._serialized_start = 19251 - _NEXUSOPERATIONCANCELREQUESTCOMPLETEDEVENTATTRIBUTES._serialized_end = 19402 - _NEXUSOPERATIONCANCELREQUESTFAILEDEVENTATTRIBUTES._serialized_start = 19405 - _NEXUSOPERATIONCANCELREQUESTFAILEDEVENTATTRIBUTES._serialized_end = 19604 - _HISTORYEVENT._serialized_start = 19607 - _HISTORYEVENT._serialized_end = 27676 - _HISTORY._serialized_start = 27678 - _HISTORY._serialized_end = 27742 + _REQUESTCANCELEXTERNALWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_end = 10708 + _EXTERNALWORKFLOWEXECUTIONCANCELREQUESTEDEVENTATTRIBUTES._serialized_start = 10711 + _EXTERNALWORKFLOWEXECUTIONCANCELREQUESTEDEVENTATTRIBUTES._serialized_end = 10908 + _SIGNALEXTERNALWORKFLOWEXECUTIONINITIATEDEVENTATTRIBUTES._serialized_start = 10911 + _SIGNALEXTERNALWORKFLOWEXECUTIONINITIATEDEVENTATTRIBUTES._serialized_end = 11290 + _SIGNALEXTERNALWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_start = 11293 + _SIGNALEXTERNALWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_end = 11632 + _EXTERNALWORKFLOWEXECUTIONSIGNALEDEVENTATTRIBUTES._serialized_start = 11635 + _EXTERNALWORKFLOWEXECUTIONSIGNALEDEVENTATTRIBUTES._serialized_end = 11846 + _UPSERTWORKFLOWSEARCHATTRIBUTESEVENTATTRIBUTES._serialized_start = 11849 + _UPSERTWORKFLOWSEARCHATTRIBUTESEVENTATTRIBUTES._serialized_end = 12007 + _WORKFLOWPROPERTIESMODIFIEDEVENTATTRIBUTES._serialized_start = 12010 + _WORKFLOWPROPERTIESMODIFIEDEVENTATTRIBUTES._serialized_end = 12148 + _STARTCHILDWORKFLOWEXECUTIONINITIATEDEVENTATTRIBUTES._serialized_start = 12151 + _STARTCHILDWORKFLOWEXECUTIONINITIATEDEVENTATTRIBUTES._serialized_end = 13427 + _STARTCHILDWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_start = 13430 + _STARTCHILDWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_end = 13772 + _CHILDWORKFLOWEXECUTIONSTARTEDEVENTATTRIBUTES._serialized_start = 13775 + _CHILDWORKFLOWEXECUTIONSTARTEDEVENTATTRIBUTES._serialized_end = 14070 + _CHILDWORKFLOWEXECUTIONCOMPLETEDEVENTATTRIBUTES._serialized_start = 14073 + _CHILDWORKFLOWEXECUTIONCOMPLETEDEVENTATTRIBUTES._serialized_end = 14398 + _CHILDWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_start = 14401 + _CHILDWORKFLOWEXECUTIONFAILEDEVENTATTRIBUTES._serialized_end = 14780 + _CHILDWORKFLOWEXECUTIONCANCELEDEVENTATTRIBUTES._serialized_start = 14783 + _CHILDWORKFLOWEXECUTIONCANCELEDEVENTATTRIBUTES._serialized_end = 15108 + _CHILDWORKFLOWEXECUTIONTIMEDOUTEVENTATTRIBUTES._serialized_start = 15111 + _CHILDWORKFLOWEXECUTIONTIMEDOUTEVENTATTRIBUTES._serialized_end = 15441 + _CHILDWORKFLOWEXECUTIONTERMINATEDEVENTATTRIBUTES._serialized_start = 15444 + _CHILDWORKFLOWEXECUTIONTERMINATEDEVENTATTRIBUTES._serialized_end = 15720 + _WORKFLOWEXECUTIONOPTIONSUPDATEDEVENTATTRIBUTES._serialized_start = 15723 + _WORKFLOWEXECUTIONOPTIONSUPDATEDEVENTATTRIBUTES._serialized_end = 16453 + _WORKFLOWEXECUTIONOPTIONSUPDATEDEVENTATTRIBUTES_WORKFLOWUPDATEOPTIONSUPDATE._serialized_start = 16303 + _WORKFLOWEXECUTIONOPTIONSUPDATEDEVENTATTRIBUTES_WORKFLOWUPDATEOPTIONSUPDATE._serialized_end = 16453 + _WORKFLOWPROPERTIESMODIFIEDEXTERNALLYEVENTATTRIBUTES._serialized_start = 16456 + _WORKFLOWPROPERTIESMODIFIEDEXTERNALLYEVENTATTRIBUTES._serialized_end = 16776 + _ACTIVITYPROPERTIESMODIFIEDEXTERNALLYEVENTATTRIBUTES._serialized_start = 16779 + _ACTIVITYPROPERTIESMODIFIEDEXTERNALLYEVENTATTRIBUTES._serialized_end = 16923 + _WORKFLOWEXECUTIONUPDATEACCEPTEDEVENTATTRIBUTES._serialized_start = 16926 + _WORKFLOWEXECUTIONUPDATEACCEPTEDEVENTATTRIBUTES._serialized_end = 17146 + _WORKFLOWEXECUTIONUPDATECOMPLETEDEVENTATTRIBUTES._serialized_start = 17149 + _WORKFLOWEXECUTIONUPDATECOMPLETEDEVENTATTRIBUTES._serialized_end = 17319 + _WORKFLOWEXECUTIONUPDATEREJECTEDEVENTATTRIBUTES._serialized_start = 17322 + _WORKFLOWEXECUTIONUPDATEREJECTEDEVENTATTRIBUTES._serialized_end = 17593 + _WORKFLOWEXECUTIONUPDATEADMITTEDEVENTATTRIBUTES._serialized_start = 17596 + _WORKFLOWEXECUTIONUPDATEADMITTEDEVENTATTRIBUTES._serialized_end = 17760 + _WORKFLOWEXECUTIONPAUSEDEVENTATTRIBUTES._serialized_start = 17762 + _WORKFLOWEXECUTIONPAUSEDEVENTATTRIBUTES._serialized_end = 17856 + _WORKFLOWEXECUTIONUNPAUSEDEVENTATTRIBUTES._serialized_start = 17858 + _WORKFLOWEXECUTIONUNPAUSEDEVENTATTRIBUTES._serialized_end = 17954 + _WORKFLOWEXECUTIONTIMESKIPPINGTRANSITIONEDEVENTATTRIBUTES._serialized_start = 17957 + _WORKFLOWEXECUTIONTIMESKIPPINGTRANSITIONEDEVENTATTRIBUTES._serialized_end = 18154 + _NEXUSOPERATIONSCHEDULEDEVENTATTRIBUTES._serialized_start = 18157 + _NEXUSOPERATIONSCHEDULEDEVENTATTRIBUTES._serialized_end = 18721 + _NEXUSOPERATIONSCHEDULEDEVENTATTRIBUTES_NEXUSHEADERENTRY._serialized_start = 18671 + _NEXUSOPERATIONSCHEDULEDEVENTATTRIBUTES_NEXUSHEADERENTRY._serialized_end = 18721 + _NEXUSOPERATIONSTARTEDEVENTATTRIBUTES._serialized_start = 18724 + _NEXUSOPERATIONSTARTEDEVENTATTRIBUTES._serialized_end = 18861 + _NEXUSOPERATIONCOMPLETEDEVENTATTRIBUTES._serialized_start = 18864 + _NEXUSOPERATIONCOMPLETEDEVENTATTRIBUTES._serialized_end = 19001 + _NEXUSOPERATIONFAILEDEVENTATTRIBUTES._serialized_start = 19004 + _NEXUSOPERATIONFAILEDEVENTATTRIBUTES._serialized_end = 19140 + _NEXUSOPERATIONTIMEDOUTEVENTATTRIBUTES._serialized_start = 19143 + _NEXUSOPERATIONTIMEDOUTEVENTATTRIBUTES._serialized_end = 19281 + _NEXUSOPERATIONCANCELEDEVENTATTRIBUTES._serialized_start = 19284 + _NEXUSOPERATIONCANCELEDEVENTATTRIBUTES._serialized_end = 19422 + _NEXUSOPERATIONCANCELREQUESTEDEVENTATTRIBUTES._serialized_start = 19424 + _NEXUSOPERATIONCANCELREQUESTEDEVENTATTRIBUTES._serialized_end = 19540 + _NEXUSOPERATIONCANCELREQUESTCOMPLETEDEVENTATTRIBUTES._serialized_start = 19543 + _NEXUSOPERATIONCANCELREQUESTCOMPLETEDEVENTATTRIBUTES._serialized_end = 19694 + _NEXUSOPERATIONCANCELREQUESTFAILEDEVENTATTRIBUTES._serialized_start = 19697 + _NEXUSOPERATIONCANCELREQUESTFAILEDEVENTATTRIBUTES._serialized_end = 19896 + _HISTORYEVENT._serialized_start = 19899 + _HISTORYEVENT._serialized_end = 28037 + _HISTORY._serialized_start = 28039 + _HISTORY._serialized_end = 28103 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/history/v1/message_pb2.pyi b/temporalio/api/history/v1/message_pb2.pyi index 26a4aa8a7..783e9d3b6 100644 --- a/temporalio/api/history/v1/message_pb2.pyi +++ b/temporalio/api/history/v1/message_pb2.pyi @@ -20,6 +20,7 @@ import temporalio.api.enums.v1.failed_cause_pb2 import temporalio.api.enums.v1.update_pb2 import temporalio.api.enums.v1.workflow_pb2 import temporalio.api.failure.v1.message_pb2 +import temporalio.api.sdk.v1.event_group_marker_pb2 import temporalio.api.sdk.v1.task_complete_metadata_pb2 import temporalio.api.sdk.v1.user_metadata_pb2 import temporalio.api.taskqueue.v1.message_pb2 @@ -78,7 +79,7 @@ class WorkflowExecutionStartedEventAttributes(google.protobuf.message.Message): EAGER_EXECUTION_ACCEPTED_FIELD_NUMBER: builtins.int DECLINED_TARGET_VERSION_UPGRADE_FIELD_NUMBER: builtins.int TIME_SKIPPING_CONFIG_FIELD_NUMBER: builtins.int - INITIAL_SKIPPED_DURATION_FIELD_NUMBER: builtins.int + TIME_SKIPPING_STATE_PROPAGATION_FIELD_NUMBER: builtins.int @property def workflow_type(self) -> temporalio.api.common.v1.message_pb2.WorkflowType: ... parent_workflow_namespace: builtins.str @@ -305,7 +306,7 @@ class WorkflowExecutionStartedEventAttributes(google.protobuf.message.Message): @property def time_skipping_config( self, - ) -> temporalio.api.workflow.v1.message_pb2.TimeSkippingConfig: + ) -> temporalio.api.common.v1.message_pb2.TimeSkippingConfig: """Initial time-skipping configuration for this workflow execution, recorded at start time. This may have been set explicitly via the start workflow request, or propagated from a parent/previous execution. @@ -314,9 +315,11 @@ class WorkflowExecutionStartedEventAttributes(google.protobuf.message.Message): will be reflected in the WorkflowExecutionOptionsUpdatedEvent. """ @property - def initial_skipped_duration(self) -> google.protobuf.duration_pb2.Duration: - """The time skipped by the previous execution that started this workflow. - It can happen in cases of child workflows and continue-as-new workflows. + def time_skipping_state_propagation( + self, + ) -> temporalio.api.common.v1.message_pb2.TimeSkippingStatePropagation: + """The time-skipping state propagated from a previous run of this workflow. This can be nil + if no time skipping has occurred or there is no previous run. """ def __init__( self, @@ -374,9 +377,10 @@ class WorkflowExecutionStartedEventAttributes(google.protobuf.message.Message): eager_execution_accepted: builtins.bool = ..., declined_target_version_upgrade: global___DeclinedTargetVersionUpgrade | None = ..., - time_skipping_config: temporalio.api.workflow.v1.message_pb2.TimeSkippingConfig + time_skipping_config: temporalio.api.common.v1.message_pb2.TimeSkippingConfig + | None = ..., + time_skipping_state_propagation: temporalio.api.common.v1.message_pb2.TimeSkippingStatePropagation | None = ..., - initial_skipped_duration: google.protobuf.duration_pb2.Duration | None = ..., ) -> None: ... def HasField( self, @@ -393,8 +397,6 @@ class WorkflowExecutionStartedEventAttributes(google.protobuf.message.Message): b"inherited_auto_upgrade_info", "inherited_pinned_version", b"inherited_pinned_version", - "initial_skipped_duration", - b"initial_skipped_duration", "input", b"input", "last_completion_result", @@ -419,6 +421,8 @@ class WorkflowExecutionStartedEventAttributes(google.protobuf.message.Message): b"task_queue", "time_skipping_config", b"time_skipping_config", + "time_skipping_state_propagation", + b"time_skipping_state_propagation", "versioning_override", b"versioning_override", "workflow_execution_expiration_time", @@ -464,8 +468,6 @@ class WorkflowExecutionStartedEventAttributes(google.protobuf.message.Message): b"inherited_build_id", "inherited_pinned_version", b"inherited_pinned_version", - "initial_skipped_duration", - b"initial_skipped_duration", "initiator", b"initiator", "input", @@ -504,6 +506,8 @@ class WorkflowExecutionStartedEventAttributes(google.protobuf.message.Message): b"task_queue", "time_skipping_config", b"time_skipping_config", + "time_skipping_state_propagation", + b"time_skipping_state_propagation", "versioning_override", b"versioning_override", "workflow_execution_expiration_time", @@ -2643,7 +2647,8 @@ class StartChildWorkflowExecutionInitiatedEventAttributes( INHERIT_BUILD_ID_FIELD_NUMBER: builtins.int PRIORITY_FIELD_NUMBER: builtins.int TIME_SKIPPING_CONFIG_FIELD_NUMBER: builtins.int - INITIAL_SKIPPED_DURATION_FIELD_NUMBER: builtins.int + TIME_SKIPPING_STATE_PROPAGATION_FIELD_NUMBER: builtins.int + VERSIONING_OVERRIDE_FIELD_NUMBER: builtins.int namespace: builtins.str """Namespace of the child workflow. SDKs and UI tools should use `namespace` field but server must use `namespace_id` only. @@ -2700,11 +2705,22 @@ class StartChildWorkflowExecutionInitiatedEventAttributes( @property def time_skipping_config( self, - ) -> temporalio.api.workflow.v1.message_pb2.TimeSkippingConfig: + ) -> temporalio.api.common.v1.message_pb2.TimeSkippingConfig: """The propagated time-skipping configuration for the child workflow.""" @property - def initial_skipped_duration(self) -> google.protobuf.duration_pb2.Duration: - """Propagate the duration skipped to the child workflow.""" + def time_skipping_state_propagation( + self, + ) -> temporalio.api.common.v1.message_pb2.TimeSkippingStatePropagation: + """The time-skipping state propagated from the parent workflow. This can be nil if no time skipping + has occurred or there is no previous run. + """ + @property + def versioning_override( + self, + ) -> temporalio.api.workflow.v1.message_pb2.VersioningOverride: + """Versioning override requested for the child workflow. If present, this explicit override + takes precedence over versioning behavior inherited from the parent workflow. + """ def __init__( self, *, @@ -2729,17 +2745,18 @@ class StartChildWorkflowExecutionInitiatedEventAttributes( | None = ..., inherit_build_id: builtins.bool = ..., priority: temporalio.api.common.v1.message_pb2.Priority | None = ..., - time_skipping_config: temporalio.api.workflow.v1.message_pb2.TimeSkippingConfig + time_skipping_config: temporalio.api.common.v1.message_pb2.TimeSkippingConfig + | None = ..., + time_skipping_state_propagation: temporalio.api.common.v1.message_pb2.TimeSkippingStatePropagation + | None = ..., + versioning_override: temporalio.api.workflow.v1.message_pb2.VersioningOverride | None = ..., - initial_skipped_duration: google.protobuf.duration_pb2.Duration | None = ..., ) -> None: ... def HasField( self, field_name: typing_extensions.Literal[ "header", b"header", - "initial_skipped_duration", - b"initial_skipped_duration", "input", b"input", "memo", @@ -2754,6 +2771,10 @@ class StartChildWorkflowExecutionInitiatedEventAttributes( b"task_queue", "time_skipping_config", b"time_skipping_config", + "time_skipping_state_propagation", + b"time_skipping_state_propagation", + "versioning_override", + b"versioning_override", "workflow_execution_timeout", b"workflow_execution_timeout", "workflow_run_timeout", @@ -2775,8 +2796,6 @@ class StartChildWorkflowExecutionInitiatedEventAttributes( b"header", "inherit_build_id", b"inherit_build_id", - "initial_skipped_duration", - b"initial_skipped_duration", "input", b"input", "memo", @@ -2797,6 +2816,10 @@ class StartChildWorkflowExecutionInitiatedEventAttributes( b"task_queue", "time_skipping_config", b"time_skipping_config", + "time_skipping_state_propagation", + b"time_skipping_state_propagation", + "versioning_override", + b"versioning_override", "workflow_execution_timeout", b"workflow_execution_timeout", "workflow_id", @@ -3368,6 +3391,7 @@ class WorkflowExecutionOptionsUpdatedEventAttributes(google.protobuf.message.Mes IDENTITY_FIELD_NUMBER: builtins.int PRIORITY_FIELD_NUMBER: builtins.int TIME_SKIPPING_CONFIG_FIELD_NUMBER: builtins.int + TIME_SKIPPING_CONFIG_UPDATED_FIELD_NUMBER: builtins.int WORKFLOW_UPDATE_OPTIONS_FIELD_NUMBER: builtins.int @property def versioning_override( @@ -3399,8 +3423,12 @@ class WorkflowExecutionOptionsUpdatedEventAttributes(google.protobuf.message.Mes @property def time_skipping_config( self, - ) -> temporalio.api.workflow.v1.message_pb2.TimeSkippingConfig: - """If set, the time-skipping configuration was changed. Contains the full updated configuration.""" + ) -> temporalio.api.common.v1.message_pb2.TimeSkippingConfig: + """TimeSkippingConfig override upserted in this event. Represents the full config.""" + time_skipping_config_updated: builtins.bool + """Indicates the time skipping config was updated by the recent call to update + workflow execution options. + """ @property def workflow_update_options( self, @@ -3421,8 +3449,9 @@ class WorkflowExecutionOptionsUpdatedEventAttributes(google.protobuf.message.Mes | None = ..., identity: builtins.str = ..., priority: temporalio.api.common.v1.message_pb2.Priority | None = ..., - time_skipping_config: temporalio.api.workflow.v1.message_pb2.TimeSkippingConfig + time_skipping_config: temporalio.api.common.v1.message_pb2.TimeSkippingConfig | None = ..., + time_skipping_config_updated: builtins.bool = ..., workflow_update_options: collections.abc.Iterable[ global___WorkflowExecutionOptionsUpdatedEventAttributes.WorkflowUpdateOptionsUpdate ] @@ -3452,6 +3481,8 @@ class WorkflowExecutionOptionsUpdatedEventAttributes(google.protobuf.message.Mes b"priority", "time_skipping_config", b"time_skipping_config", + "time_skipping_config_updated", + b"time_skipping_config_updated", "unset_versioning_override", b"unset_versioning_override", "versioning_override", @@ -3819,21 +3850,21 @@ global___WorkflowExecutionUnpausedEventAttributes = ( class WorkflowExecutionTimeSkippingTransitionedEventAttributes( google.protobuf.message.Message ): - """Attributes for an event indicating that time skipping state changed for a workflow execution, - either time was advanced or time skipping was disabled automatically due to a bound being reached. + """Attributes for an event indicating that time skipping state changed for a workflow execution: + either time was advanced, or time skipping was stopped automatically due to the fast_forward completing. The worker_may_ignore field in HistoryEvent should always be set true for this event. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor TARGET_TIME_FIELD_NUMBER: builtins.int - DISABLED_AFTER_BOUND_FIELD_NUMBER: builtins.int + DISABLED_AFTER_FAST_FORWARD_FIELD_NUMBER: builtins.int WALL_CLOCK_TIME_FIELD_NUMBER: builtins.int @property def target_time(self) -> google.protobuf.timestamp_pb2.Timestamp: - """The virtual time after time skipping was applied.""" - disabled_after_bound: builtins.bool - """when true, time skipping was disabled automatically due to a bound being reached. + """The virtual time point that time skipping advanced to.""" + disabled_after_fast_forward: builtins.bool + """When true, time skipping has been stopped automatically due to a call to fast_forward completing. (-- api-linter: core::0140::prepositions=disabled aip.dev/not-precedent: "after" is used to indicate temporal ordering. --) """ @@ -3844,7 +3875,7 @@ class WorkflowExecutionTimeSkippingTransitionedEventAttributes( self, *, target_time: google.protobuf.timestamp_pb2.Timestamp | None = ..., - disabled_after_bound: builtins.bool = ..., + disabled_after_fast_forward: builtins.bool = ..., wall_clock_time: google.protobuf.timestamp_pb2.Timestamp | None = ..., ) -> None: ... def HasField( @@ -3856,8 +3887,8 @@ class WorkflowExecutionTimeSkippingTransitionedEventAttributes( def ClearField( self, field_name: typing_extensions.Literal[ - "disabled_after_bound", - b"disabled_after_bound", + "disabled_after_fast_forward", + b"disabled_after_fast_forward", "target_time", b"target_time", "wall_clock_time", @@ -4358,6 +4389,7 @@ class HistoryEvent(google.protobuf.message.Message): USER_METADATA_FIELD_NUMBER: builtins.int LINKS_FIELD_NUMBER: builtins.int PRINCIPAL_FIELD_NUMBER: builtins.int + EVENT_GROUP_MARKERS_FIELD_NUMBER: builtins.int WORKFLOW_EXECUTION_STARTED_EVENT_ATTRIBUTES_FIELD_NUMBER: builtins.int WORKFLOW_EXECUTION_COMPLETED_EVENT_ATTRIBUTES_FIELD_NUMBER: builtins.int WORKFLOW_EXECUTION_FAILED_EVENT_ATTRIBUTES_FIELD_NUMBER: builtins.int @@ -4469,6 +4501,13 @@ class HistoryEvent(google.protobuf.message.Message): def principal(self) -> temporalio.api.common.v1.message_pb2.Principal: """Server-computed authenticated caller identity associated with this event.""" @property + def event_group_markers( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ + temporalio.api.sdk.v1.event_group_marker_pb2.EventGroupMarker + ]: + """Event group markers attached to this event.""" + @property def workflow_execution_started_event_attributes( self, ) -> global___WorkflowExecutionStartedEventAttributes: ... @@ -4720,6 +4759,10 @@ class HistoryEvent(google.protobuf.message.Message): links: collections.abc.Iterable[temporalio.api.common.v1.message_pb2.Link] | None = ..., principal: temporalio.api.common.v1.message_pb2.Principal | None = ..., + event_group_markers: collections.abc.Iterable[ + temporalio.api.sdk.v1.event_group_marker_pb2.EventGroupMarker + ] + | None = ..., workflow_execution_started_event_attributes: global___WorkflowExecutionStartedEventAttributes | None = ..., workflow_execution_completed_event_attributes: global___WorkflowExecutionCompletedEventAttributes @@ -5006,6 +5049,8 @@ class HistoryEvent(google.protobuf.message.Message): b"child_workflow_execution_terminated_event_attributes", "child_workflow_execution_timed_out_event_attributes", b"child_workflow_execution_timed_out_event_attributes", + "event_group_markers", + b"event_group_markers", "event_id", b"event_id", "event_time", diff --git a/temporalio/api/namespace/v1/message_pb2.py b/temporalio/api/namespace/v1/message_pb2.py index 4c7018307..43a3773f2 100644 --- a/temporalio/api/namespace/v1/message_pb2.py +++ b/temporalio/api/namespace/v1/message_pb2.py @@ -22,7 +22,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\'temporal/api/namespace/v1/message.proto\x12\x19temporal.api.namespace.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a%temporal/api/enums/v1/namespace.proto"\xe8\x06\n\rNamespaceInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x34\n\x05state\x18\x02 \x01(\x0e\x32%.temporal.api.enums.v1.NamespaceState\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x13\n\x0bowner_email\x18\x04 \x01(\t\x12@\n\x04\x64\x61ta\x18\x05 \x03(\x0b\x32\x32.temporal.api.namespace.v1.NamespaceInfo.DataEntry\x12\n\n\x02id\x18\x06 \x01(\t\x12K\n\x0c\x63\x61pabilities\x18\x07 \x01(\x0b\x32\x35.temporal.api.namespace.v1.NamespaceInfo.Capabilities\x12?\n\x06limits\x18\x08 \x01(\x0b\x32/.temporal.api.namespace.v1.NamespaceInfo.Limits\x12\x1a\n\x12supports_schedules\x18\x64 \x01(\x08\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\xfb\x02\n\x0c\x43\x61pabilities\x12\x1c\n\x14\x65\x61ger_workflow_start\x18\x01 \x01(\x08\x12\x13\n\x0bsync_update\x18\x02 \x01(\x08\x12\x14\n\x0c\x61sync_update\x18\x03 \x01(\x08\x12\x19\n\x11worker_heartbeats\x18\x04 \x01(\x08\x12*\n"reported_problems_search_attribute\x18\x05 \x01(\x08\x12\x16\n\x0eworkflow_pause\x18\x06 \x01(\x08\x12\x1d\n\x15standalone_activities\x18\x07 \x01(\x08\x12(\n worker_poll_complete_on_shutdown\x18\x08 \x01(\x08\x12\x1a\n\x12poller_autoscaling\x18\t \x01(\x08\x12\x17\n\x0fworker_commands\x18\n \x01(\x08\x12"\n\x1astandalone_nexus_operation\x18\x0b \x01(\x08\x12!\n\x19workflow_update_callbacks\x18\x0c \x01(\x08\x1a\x46\n\x06Limits\x12\x1d\n\x15\x62lob_size_limit_error\x18\x01 \x01(\x03\x12\x1d\n\x15memo_size_limit_error\x18\x02 \x01(\x03"\x9e\x04\n\x0fNamespaceConfig\x12\x43\n workflow_execution_retention_ttl\x18\x01 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x0c\x62\x61\x64_binaries\x18\x02 \x01(\x0b\x32&.temporal.api.namespace.v1.BadBinaries\x12\x44\n\x16history_archival_state\x18\x03 \x01(\x0e\x32$.temporal.api.enums.v1.ArchivalState\x12\x1c\n\x14history_archival_uri\x18\x04 \x01(\t\x12G\n\x19visibility_archival_state\x18\x05 \x01(\x0e\x32$.temporal.api.enums.v1.ArchivalState\x12\x1f\n\x17visibility_archival_uri\x18\x06 \x01(\t\x12u\n\x1f\x63ustom_search_attribute_aliases\x18\x07 \x03(\x0b\x32L.temporal.api.namespace.v1.NamespaceConfig.CustomSearchAttributeAliasesEntry\x1a\x43\n!CustomSearchAttributeAliasesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"\xb0\x01\n\x0b\x42\x61\x64\x42inaries\x12\x46\n\x08\x62inaries\x18\x01 \x03(\x0b\x32\x34.temporal.api.namespace.v1.BadBinaries.BinariesEntry\x1aY\n\rBinariesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x37\n\x05value\x18\x02 \x01(\x0b\x32(.temporal.api.namespace.v1.BadBinaryInfo:\x02\x38\x01"b\n\rBadBinaryInfo\x12\x0e\n\x06reason\x18\x01 \x01(\t\x12\x10\n\x08operator\x18\x02 \x01(\t\x12/\n\x0b\x63reate_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xea\x01\n\x13UpdateNamespaceInfo\x12\x13\n\x0b\x64\x65scription\x18\x01 \x01(\t\x12\x13\n\x0bowner_email\x18\x02 \x01(\t\x12\x46\n\x04\x64\x61ta\x18\x03 \x03(\x0b\x32\x38.temporal.api.namespace.v1.UpdateNamespaceInfo.DataEntry\x12\x34\n\x05state\x18\x04 \x01(\x0e\x32%.temporal.api.enums.v1.NamespaceState\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"*\n\x0fNamespaceFilter\x12\x17\n\x0finclude_deleted\x18\x01 \x01(\x08\x42\x98\x01\n\x1cio.temporal.api.namespace.v1B\x0cMessageProtoP\x01Z)go.temporal.io/api/namespace/v1;namespace\xaa\x02\x1bTemporalio.Api.Namespace.V1\xea\x02\x1eTemporalio::Api::Namespace::V1b\x06proto3' + b'\n\'temporal/api/namespace/v1/message.proto\x12\x19temporal.api.namespace.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a%temporal/api/enums/v1/namespace.proto"\xf6\x08\n\rNamespaceInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x34\n\x05state\x18\x02 \x01(\x0e\x32%.temporal.api.enums.v1.NamespaceState\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x13\n\x0bowner_email\x18\x04 \x01(\t\x12@\n\x04\x64\x61ta\x18\x05 \x03(\x0b\x32\x32.temporal.api.namespace.v1.NamespaceInfo.DataEntry\x12\n\n\x02id\x18\x06 \x01(\t\x12K\n\x0c\x63\x61pabilities\x18\x07 \x01(\x0b\x32\x35.temporal.api.namespace.v1.NamespaceInfo.Capabilities\x12?\n\x06limits\x18\x08 \x01(\x0b\x32/.temporal.api.namespace.v1.NamespaceInfo.Limits\x12\x1a\n\x12supports_schedules\x18\x64 \x01(\x08\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\xd6\x04\n\x0c\x43\x61pabilities\x12\x1c\n\x14\x65\x61ger_workflow_start\x18\x01 \x01(\x08\x12\x13\n\x0bsync_update\x18\x02 \x01(\x08\x12\x14\n\x0c\x61sync_update\x18\x03 \x01(\x08\x12\x19\n\x11worker_heartbeats\x18\x04 \x01(\x08\x12*\n"reported_problems_search_attribute\x18\x05 \x01(\x08\x12\x16\n\x0eworkflow_pause\x18\x06 \x01(\x08\x12\x1d\n\x15standalone_activities\x18\x07 \x01(\x08\x12(\n worker_poll_complete_on_shutdown\x18\x08 \x01(\x08\x12\x1a\n\x12poller_autoscaling\x18\t \x01(\x08\x12\x17\n\x0fworker_commands\x18\n \x01(\x08\x12"\n\x1astandalone_nexus_operation\x18\x0b \x01(\x08\x12!\n\x19workflow_update_callbacks\x18\x0c \x01(\x08\x12&\n\x1epoller_autoscaling_auto_enroll\x18\r \x01(\x08\x12+\n#workflow_task_completion_pagination\x18\x0e \x01(\x08\x12\'\n\x1fstandalone_activity_start_delay\x18\x0f \x01(\x08\x12,\n$standalone_activity_batch_operations\x18\x10 \x01(\x08\x12-\n%standalone_activity_operator_commands\x18\x11 \x01(\x08\x1ay\n\x06Limits\x12\x1d\n\x15\x62lob_size_limit_error\x18\x01 \x01(\x03\x12\x1d\n\x15memo_size_limit_error\x18\x02 \x01(\x03\x12\x31\n)workflow_task_completion_size_limit_error\x18\x03 \x01(\x03"\x9e\x04\n\x0fNamespaceConfig\x12\x43\n workflow_execution_retention_ttl\x18\x01 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x0c\x62\x61\x64_binaries\x18\x02 \x01(\x0b\x32&.temporal.api.namespace.v1.BadBinaries\x12\x44\n\x16history_archival_state\x18\x03 \x01(\x0e\x32$.temporal.api.enums.v1.ArchivalState\x12\x1c\n\x14history_archival_uri\x18\x04 \x01(\t\x12G\n\x19visibility_archival_state\x18\x05 \x01(\x0e\x32$.temporal.api.enums.v1.ArchivalState\x12\x1f\n\x17visibility_archival_uri\x18\x06 \x01(\t\x12u\n\x1f\x63ustom_search_attribute_aliases\x18\x07 \x03(\x0b\x32L.temporal.api.namespace.v1.NamespaceConfig.CustomSearchAttributeAliasesEntry\x1a\x43\n!CustomSearchAttributeAliasesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"\xb0\x01\n\x0b\x42\x61\x64\x42inaries\x12\x46\n\x08\x62inaries\x18\x01 \x03(\x0b\x32\x34.temporal.api.namespace.v1.BadBinaries.BinariesEntry\x1aY\n\rBinariesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x37\n\x05value\x18\x02 \x01(\x0b\x32(.temporal.api.namespace.v1.BadBinaryInfo:\x02\x38\x01"b\n\rBadBinaryInfo\x12\x0e\n\x06reason\x18\x01 \x01(\t\x12\x10\n\x08operator\x18\x02 \x01(\t\x12/\n\x0b\x63reate_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xea\x01\n\x13UpdateNamespaceInfo\x12\x13\n\x0b\x64\x65scription\x18\x01 \x01(\t\x12\x13\n\x0bowner_email\x18\x02 \x01(\t\x12\x46\n\x04\x64\x61ta\x18\x03 \x03(\x0b\x32\x38.temporal.api.namespace.v1.UpdateNamespaceInfo.DataEntry\x12\x34\n\x05state\x18\x04 \x01(\x0e\x32%.temporal.api.enums.v1.NamespaceState\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"*\n\x0fNamespaceFilter\x12\x17\n\x0finclude_deleted\x18\x01 \x01(\x08\x42\x98\x01\n\x1cio.temporal.api.namespace.v1B\x0cMessageProtoP\x01Z)go.temporal.io/api/namespace/v1;namespace\xaa\x02\x1bTemporalio.Api.Namespace.V1\xea\x02\x1eTemporalio::Api::Namespace::V1b\x06proto3' ) @@ -178,27 +178,27 @@ _UPDATENAMESPACEINFO_DATAENTRY._options = None _UPDATENAMESPACEINFO_DATAENTRY._serialized_options = b"8\001" _NAMESPACEINFO._serialized_start = 175 - _NAMESPACEINFO._serialized_end = 1047 + _NAMESPACEINFO._serialized_end = 1317 _NAMESPACEINFO_DATAENTRY._serialized_start = 550 _NAMESPACEINFO_DATAENTRY._serialized_end = 593 _NAMESPACEINFO_CAPABILITIES._serialized_start = 596 - _NAMESPACEINFO_CAPABILITIES._serialized_end = 975 - _NAMESPACEINFO_LIMITS._serialized_start = 977 - _NAMESPACEINFO_LIMITS._serialized_end = 1047 - _NAMESPACECONFIG._serialized_start = 1050 - _NAMESPACECONFIG._serialized_end = 1592 - _NAMESPACECONFIG_CUSTOMSEARCHATTRIBUTEALIASESENTRY._serialized_start = 1525 - _NAMESPACECONFIG_CUSTOMSEARCHATTRIBUTEALIASESENTRY._serialized_end = 1592 - _BADBINARIES._serialized_start = 1595 - _BADBINARIES._serialized_end = 1771 - _BADBINARIES_BINARIESENTRY._serialized_start = 1682 - _BADBINARIES_BINARIESENTRY._serialized_end = 1771 - _BADBINARYINFO._serialized_start = 1773 - _BADBINARYINFO._serialized_end = 1871 - _UPDATENAMESPACEINFO._serialized_start = 1874 - _UPDATENAMESPACEINFO._serialized_end = 2108 + _NAMESPACEINFO_CAPABILITIES._serialized_end = 1194 + _NAMESPACEINFO_LIMITS._serialized_start = 1196 + _NAMESPACEINFO_LIMITS._serialized_end = 1317 + _NAMESPACECONFIG._serialized_start = 1320 + _NAMESPACECONFIG._serialized_end = 1862 + _NAMESPACECONFIG_CUSTOMSEARCHATTRIBUTEALIASESENTRY._serialized_start = 1795 + _NAMESPACECONFIG_CUSTOMSEARCHATTRIBUTEALIASESENTRY._serialized_end = 1862 + _BADBINARIES._serialized_start = 1865 + _BADBINARIES._serialized_end = 2041 + _BADBINARIES_BINARIESENTRY._serialized_start = 1952 + _BADBINARIES_BINARIESENTRY._serialized_end = 2041 + _BADBINARYINFO._serialized_start = 2043 + _BADBINARYINFO._serialized_end = 2141 + _UPDATENAMESPACEINFO._serialized_start = 2144 + _UPDATENAMESPACEINFO._serialized_end = 2378 _UPDATENAMESPACEINFO_DATAENTRY._serialized_start = 550 _UPDATENAMESPACEINFO_DATAENTRY._serialized_end = 593 - _NAMESPACEFILTER._serialized_start = 2110 - _NAMESPACEFILTER._serialized_end = 2152 + _NAMESPACEFILTER._serialized_start = 2380 + _NAMESPACEFILTER._serialized_end = 2422 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/namespace/v1/message_pb2.pyi b/temporalio/api/namespace/v1/message_pb2.pyi index a01a58c72..06b1546b3 100644 --- a/temporalio/api/namespace/v1/message_pb2.pyi +++ b/temporalio/api/namespace/v1/message_pb2.pyi @@ -60,6 +60,11 @@ class NamespaceInfo(google.protobuf.message.Message): WORKER_COMMANDS_FIELD_NUMBER: builtins.int STANDALONE_NEXUS_OPERATION_FIELD_NUMBER: builtins.int WORKFLOW_UPDATE_CALLBACKS_FIELD_NUMBER: builtins.int + POLLER_AUTOSCALING_AUTO_ENROLL_FIELD_NUMBER: builtins.int + WORKFLOW_TASK_COMPLETION_PAGINATION_FIELD_NUMBER: builtins.int + STANDALONE_ACTIVITY_START_DELAY_FIELD_NUMBER: builtins.int + STANDALONE_ACTIVITY_BATCH_OPERATIONS_FIELD_NUMBER: builtins.int + STANDALONE_ACTIVITY_OPERATOR_COMMANDS_FIELD_NUMBER: builtins.int eager_workflow_start: builtins.bool """True if the namespace supports eager workflow start.""" sync_update: builtins.bool @@ -89,6 +94,16 @@ class NamespaceInfo(google.protobuf.message.Message): """True if the namespace supports standalone Nexus operations.""" workflow_update_callbacks: builtins.bool """True if the namespace supports attaching callbacks on workflow updates""" + poller_autoscaling_auto_enroll: builtins.bool + """When true, workers should use poller autoscaling by default unless explicitly configured otherwise.""" + workflow_task_completion_pagination: builtins.bool + """True if the namespace supports pagination of `RespondWorkflowTaskCompleted` request.""" + standalone_activity_start_delay: builtins.bool + """True if the namespace supports start delay for standalone activities.""" + standalone_activity_batch_operations: builtins.bool + """True if the namespace supports batch operations for standalone activities.""" + standalone_activity_operator_commands: builtins.bool + """True if the namespace supports standalone activity operator commands.""" def __init__( self, *, @@ -104,6 +119,11 @@ class NamespaceInfo(google.protobuf.message.Message): worker_commands: builtins.bool = ..., standalone_nexus_operation: builtins.bool = ..., workflow_update_callbacks: builtins.bool = ..., + poller_autoscaling_auto_enroll: builtins.bool = ..., + workflow_task_completion_pagination: builtins.bool = ..., + standalone_activity_start_delay: builtins.bool = ..., + standalone_activity_batch_operations: builtins.bool = ..., + standalone_activity_operator_commands: builtins.bool = ..., ) -> None: ... def ClearField( self, @@ -114,10 +134,18 @@ class NamespaceInfo(google.protobuf.message.Message): b"eager_workflow_start", "poller_autoscaling", b"poller_autoscaling", + "poller_autoscaling_auto_enroll", + b"poller_autoscaling_auto_enroll", "reported_problems_search_attribute", b"reported_problems_search_attribute", "standalone_activities", b"standalone_activities", + "standalone_activity_batch_operations", + b"standalone_activity_batch_operations", + "standalone_activity_operator_commands", + b"standalone_activity_operator_commands", + "standalone_activity_start_delay", + b"standalone_activity_start_delay", "standalone_nexus_operation", b"standalone_nexus_operation", "sync_update", @@ -130,6 +158,8 @@ class NamespaceInfo(google.protobuf.message.Message): b"worker_poll_complete_on_shutdown", "workflow_pause", b"workflow_pause", + "workflow_task_completion_pagination", + b"workflow_task_completion_pagination", "workflow_update_callbacks", b"workflow_update_callbacks", ], @@ -140,6 +170,7 @@ class NamespaceInfo(google.protobuf.message.Message): BLOB_SIZE_LIMIT_ERROR_FIELD_NUMBER: builtins.int MEMO_SIZE_LIMIT_ERROR_FIELD_NUMBER: builtins.int + WORKFLOW_TASK_COMPLETION_SIZE_LIMIT_ERROR_FIELD_NUMBER: builtins.int blob_size_limit_error: builtins.int """Maximum size in bytes for payload fields in workflow history events (e.g., workflow/activity inputs and results, failure details, signal payloads). @@ -147,11 +178,17 @@ class NamespaceInfo(google.protobuf.message.Message): """ memo_size_limit_error: builtins.int """Maximum total memo size in bytes per workflow execution.""" + workflow_task_completion_size_limit_error: builtins.int + """Maximum total size in bytes of a single RespondWorkflowTaskCompleted request. + Requests exceeding this fail the workflow task with + WORKFLOW_TASK_FAILED_CAUSE_REQUEST_TOO_LARGE. 0 means no explicit limit. + """ def __init__( self, *, blob_size_limit_error: builtins.int = ..., memo_size_limit_error: builtins.int = ..., + workflow_task_completion_size_limit_error: builtins.int = ..., ) -> None: ... def ClearField( self, @@ -160,6 +197,8 @@ class NamespaceInfo(google.protobuf.message.Message): b"blob_size_limit_error", "memo_size_limit_error", b"memo_size_limit_error", + "workflow_task_completion_size_limit_error", + b"workflow_task_completion_size_limit_error", ], ) -> None: ... diff --git a/temporalio/api/sdk/v1/__init__.py b/temporalio/api/sdk/v1/__init__.py index 0a72fe5cf..4f23aac6a 100644 --- a/temporalio/api/sdk/v1/__init__.py +++ b/temporalio/api/sdk/v1/__init__.py @@ -5,6 +5,7 @@ StackTraceFileSlice, StackTraceSDKInfo, ) +from .event_group_marker_pb2 import EventGroupMarker from .external_storage_pb2 import ExternalStorageReference from .task_complete_metadata_pb2 import WorkflowTaskCompletedMetadata from .user_metadata_pb2 import UserMetadata @@ -17,6 +18,7 @@ __all__ = [ "EnhancedStackTrace", + "EventGroupMarker", "ExternalStorageReference", "StackTrace", "StackTraceFileLocation", diff --git a/temporalio/api/sdk/v1/event_group_marker_pb2.py b/temporalio/api/sdk/v1/event_group_marker_pb2.py new file mode 100644 index 000000000..3475c8588 --- /dev/null +++ b/temporalio/api/sdk/v1/event_group_marker_pb2.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: temporal/api/sdk/v1/event_group_marker.proto +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from temporalio.api.common.v1 import ( + message_pb2 as temporal_dot_api_dot_common_dot_v1_dot_message__pb2, +) + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n,temporal/api/sdk/v1/event_group_marker.proto\x12\x13temporal.api.sdk.v1\x1a$temporal/api/common/v1/message.proto"\x92\x03\n\x10\x45ventGroupMarker\x12<\n\x05label\x18\x01 \x01(\x0b\x32+.temporal.api.sdk.v1.EventGroupMarker.LabelH\x00\x12K\n\rinbound_event\x18\x02 \x01(\x0b\x32\x32.temporal.api.sdk.v1.EventGroupMarker.InboundEventH\x00\x12M\n\x0einbound_update\x18\x03 \x01(\x0b\x32\x33.temporal.api.sdk.v1.EventGroupMarker.InboundUpdateH\x00\x1a\x43\n\x05Label\x12\n\n\x02id\x18\x01 \x01(\t\x12.\n\x05label\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload\x1a(\n\x0cInboundEvent\x12\x18\n\x10inbound_event_id\x18\x01 \x01(\x03\x1a*\n\rInboundUpdate\x12\x19\n\x11inbound_update_id\x18\x01 \x01(\tB\t\n\x07variantB\x83\x01\n\x16io.temporal.api.sdk.v1B\x15\x45ventGroupMarkerProtoP\x01Z\x1dgo.temporal.io/api/sdk/v1;sdk\xaa\x02\x15Temporalio.Api.Sdk.V1\xea\x02\x18Temporalio::Api::Sdk::V1b\x06proto3' +) + + +_EVENTGROUPMARKER = DESCRIPTOR.message_types_by_name["EventGroupMarker"] +_EVENTGROUPMARKER_LABEL = _EVENTGROUPMARKER.nested_types_by_name["Label"] +_EVENTGROUPMARKER_INBOUNDEVENT = _EVENTGROUPMARKER.nested_types_by_name["InboundEvent"] +_EVENTGROUPMARKER_INBOUNDUPDATE = _EVENTGROUPMARKER.nested_types_by_name[ + "InboundUpdate" +] +EventGroupMarker = _reflection.GeneratedProtocolMessageType( + "EventGroupMarker", + (_message.Message,), + { + "Label": _reflection.GeneratedProtocolMessageType( + "Label", + (_message.Message,), + { + "DESCRIPTOR": _EVENTGROUPMARKER_LABEL, + "__module__": "temporalio.api.sdk.v1.event_group_marker_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.sdk.v1.EventGroupMarker.Label) + }, + ), + "InboundEvent": _reflection.GeneratedProtocolMessageType( + "InboundEvent", + (_message.Message,), + { + "DESCRIPTOR": _EVENTGROUPMARKER_INBOUNDEVENT, + "__module__": "temporalio.api.sdk.v1.event_group_marker_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.sdk.v1.EventGroupMarker.InboundEvent) + }, + ), + "InboundUpdate": _reflection.GeneratedProtocolMessageType( + "InboundUpdate", + (_message.Message,), + { + "DESCRIPTOR": _EVENTGROUPMARKER_INBOUNDUPDATE, + "__module__": "temporalio.api.sdk.v1.event_group_marker_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.sdk.v1.EventGroupMarker.InboundUpdate) + }, + ), + "DESCRIPTOR": _EVENTGROUPMARKER, + "__module__": "temporalio.api.sdk.v1.event_group_marker_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.sdk.v1.EventGroupMarker) + }, +) +_sym_db.RegisterMessage(EventGroupMarker) +_sym_db.RegisterMessage(EventGroupMarker.Label) +_sym_db.RegisterMessage(EventGroupMarker.InboundEvent) +_sym_db.RegisterMessage(EventGroupMarker.InboundUpdate) + +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b"\n\026io.temporal.api.sdk.v1B\025EventGroupMarkerProtoP\001Z\035go.temporal.io/api/sdk/v1;sdk\252\002\025Temporalio.Api.Sdk.V1\352\002\030Temporalio::Api::Sdk::V1" + _EVENTGROUPMARKER._serialized_start = 108 + _EVENTGROUPMARKER._serialized_end = 510 + _EVENTGROUPMARKER_LABEL._serialized_start = 346 + _EVENTGROUPMARKER_LABEL._serialized_end = 413 + _EVENTGROUPMARKER_INBOUNDEVENT._serialized_start = 415 + _EVENTGROUPMARKER_INBOUNDEVENT._serialized_end = 455 + _EVENTGROUPMARKER_INBOUNDUPDATE._serialized_start = 457 + _EVENTGROUPMARKER_INBOUNDUPDATE._serialized_end = 499 +# @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/sdk/v1/event_group_marker_pb2.pyi b/temporalio/api/sdk/v1/event_group_marker_pb2.pyi new file mode 100644 index 000000000..63a5f53eb --- /dev/null +++ b/temporalio/api/sdk/v1/event_group_marker_pb2.pyi @@ -0,0 +1,159 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import sys + +import google.protobuf.descriptor +import google.protobuf.message + +import temporalio.api.common.v1.message_pb2 + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class EventGroupMarker(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class Label(google.protobuf.message.Message): + """A user-defined short-form string value to be used as the group's label.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ID_FIELD_NUMBER: builtins.int + LABEL_FIELD_NUMBER: builtins.int + id: builtins.str + """Opaque identifier assigned by the SDK.""" + @property + def label(self) -> temporalio.api.common.v1.message_pb2.Payload: + """This payload should be a "json/plain"-encoded payload that is a single + JSON string for use in user interfaces. User interface formatting may not + apply to this text when used in "label" situations. The payload data + section is limited to 400 bytes by default. + + Payload only needs to be set on the first use of a given Marker ID; + further references to an existing Marker ID reuse existing attributes of + the referenced Marker -- i.e. further label payloads are ignored. + + Note that it is valid to have distinct Markers (i.e. distinct Marker IDs) + in a given workflow execution that carry the same label, provided that + they have the distinct ID. + """ + def __init__( + self, + *, + id: builtins.str = ..., + label: temporalio.api.common.v1.message_pb2.Payload | None = ..., + ) -> None: ... + def HasField( + self, field_name: typing_extensions.Literal["label", b"label"] + ) -> builtins.bool: ... + def ClearField( + self, field_name: typing_extensions.Literal["id", b"id", "label", b"label"] + ) -> None: ... + + class InboundEvent(google.protobuf.message.Message): + """The event ID of an event in the present workflow that triggered implicit + creation of this group Marker. + + The target event's type must be one of the following: + - `WORKFLOW_EXECUTION_STARTED` + - `WORKFLOW_EXECUTION_SIGNALED` + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + INBOUND_EVENT_ID_FIELD_NUMBER: builtins.int + inbound_event_id: builtins.int + def __init__( + self, + *, + inbound_event_id: builtins.int = ..., + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "inbound_event_id", b"inbound_event_id" + ], + ) -> None: ... + + class InboundUpdate(google.protobuf.message.Message): + """The identifier of an inbound Update (request.meta.update_id) + whose handler triggered implicit creation of this group Marker. + + Used in place of `inbound_event_id` for Updates because the event ID of the + UpdateAccepted history event is not known until the Workflow Task is + completed and recorded by the server, which may be too late. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + INBOUND_UPDATE_ID_FIELD_NUMBER: builtins.int + inbound_update_id: builtins.str + def __init__( + self, + *, + inbound_update_id: builtins.str = ..., + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "inbound_update_id", b"inbound_update_id" + ], + ) -> None: ... + + LABEL_FIELD_NUMBER: builtins.int + INBOUND_EVENT_FIELD_NUMBER: builtins.int + INBOUND_UPDATE_FIELD_NUMBER: builtins.int + @property + def label(self) -> global___EventGroupMarker.Label: ... + @property + def inbound_event(self) -> global___EventGroupMarker.InboundEvent: ... + @property + def inbound_update(self) -> global___EventGroupMarker.InboundUpdate: ... + def __init__( + self, + *, + label: global___EventGroupMarker.Label | None = ..., + inbound_event: global___EventGroupMarker.InboundEvent | None = ..., + inbound_update: global___EventGroupMarker.InboundUpdate | None = ..., + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "inbound_event", + b"inbound_event", + "inbound_update", + b"inbound_update", + "label", + b"label", + "variant", + b"variant", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "inbound_event", + b"inbound_event", + "inbound_update", + b"inbound_update", + "label", + b"label", + "variant", + b"variant", + ], + ) -> None: ... + def WhichOneof( + self, oneof_group: typing_extensions.Literal["variant", b"variant"] + ) -> ( + typing_extensions.Literal["label", "inbound_event", "inbound_update"] | None + ): ... + +global___EventGroupMarker = EventGroupMarker diff --git a/temporalio/api/taskqueue/v1/__init__.py b/temporalio/api/taskqueue/v1/__init__.py index dac573696..98595069b 100644 --- a/temporalio/api/taskqueue/v1/__init__.py +++ b/temporalio/api/taskqueue/v1/__init__.py @@ -5,6 +5,7 @@ CompatibleVersionSet, ConfigMetadata, PollerGroupInfo, + PollerGroupsInfo, PollerInfo, PollerScalingDecision, RampByPercentage, @@ -34,6 +35,7 @@ "CompatibleVersionSet", "ConfigMetadata", "PollerGroupInfo", + "PollerGroupsInfo", "PollerInfo", "PollerScalingDecision", "RampByPercentage", diff --git a/temporalio/api/taskqueue/v1/message_pb2.py b/temporalio/api/taskqueue/v1/message_pb2.py index bf0eab1d0..e5caf07ac 100644 --- a/temporalio/api/taskqueue/v1/message_pb2.py +++ b/temporalio/api/taskqueue/v1/message_pb2.py @@ -29,7 +29,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\'temporal/api/taskqueue/v1/message.proto\x12\x19temporal.api.taskqueue.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\x1a&temporal/api/enums/v1/task_queue.proto\x1a$temporal/api/common/v1/message.proto\x1a(temporal/api/deployment/v1/message.proto"b\n\tTaskQueue\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x32\n\x04kind\x18\x02 \x01(\x0e\x32$.temporal.api.enums.v1.TaskQueueKind\x12\x13\n\x0bnormal_name\x18\x03 \x01(\t"O\n\x11TaskQueueMetadata\x12:\n\x14max_tasks_per_second\x18\x01 \x01(\x0b\x32\x1c.google.protobuf.DoubleValue"\xda\x02\n\x17TaskQueueVersioningInfo\x12W\n\x1a\x63urrent_deployment_version\x18\x07 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x1b\n\x0f\x63urrent_version\x18\x01 \x01(\tB\x02\x18\x01\x12W\n\x1aramping_deployment_version\x18\t \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x1b\n\x0framping_version\x18\x02 \x01(\tB\x02\x18\x01\x12"\n\x1aramping_version_percentage\x18\x03 \x01(\x02\x12/\n\x0bupdate_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"W\n\x19TaskQueueVersionSelection\x12\x11\n\tbuild_ids\x18\x01 \x03(\t\x12\x13\n\x0bunversioned\x18\x02 \x01(\x08\x12\x12\n\nall_active\x18\x03 \x01(\x08"\x95\x02\n\x14TaskQueueVersionInfo\x12R\n\ntypes_info\x18\x01 \x03(\x0b\x32>.temporal.api.taskqueue.v1.TaskQueueVersionInfo.TypesInfoEntry\x12I\n\x11task_reachability\x18\x02 \x01(\x0e\x32..temporal.api.enums.v1.BuildIdTaskReachability\x1a^\n\x0eTypesInfoEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12;\n\x05value\x18\x02 \x01(\x0b\x32,.temporal.api.taskqueue.v1.TaskQueueTypeInfo:\x02\x38\x01"\x85\x01\n\x11TaskQueueTypeInfo\x12\x36\n\x07pollers\x18\x01 \x03(\x0b\x32%.temporal.api.taskqueue.v1.PollerInfo\x12\x38\n\x05stats\x18\x02 \x01(\x0b\x32).temporal.api.taskqueue.v1.TaskQueueStats"\xa4\x01\n\x0eTaskQueueStats\x12!\n\x19\x61pproximate_backlog_count\x18\x01 \x01(\x03\x12:\n\x17\x61pproximate_backlog_age\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x16\n\x0etasks_add_rate\x18\x03 \x01(\x02\x12\x1b\n\x13tasks_dispatch_rate\x18\x04 \x01(\x02"\xac\x01\n\x0fTaskQueueStatus\x12\x1a\n\x12\x62\x61\x63klog_count_hint\x18\x01 \x01(\x03\x12\x12\n\nread_level\x18\x02 \x01(\x03\x12\x11\n\tack_level\x18\x03 \x01(\x03\x12\x17\n\x0frate_per_second\x18\x04 \x01(\x01\x12=\n\rtask_id_block\x18\x05 \x01(\x0b\x32&.temporal.api.taskqueue.v1.TaskIdBlock"/\n\x0bTaskIdBlock\x12\x10\n\x08start_id\x18\x01 \x01(\x03\x12\x0e\n\x06\x65nd_id\x18\x02 \x01(\x03"B\n\x1aTaskQueuePartitionMetadata\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x17\n\x0fowner_host_name\x18\x02 \x01(\t"\x9a\x02\n\nPollerInfo\x12\x34\n\x10last_access_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x17\n\x0frate_per_second\x18\x03 \x01(\x01\x12Z\n\x1bworker_version_capabilities\x18\x04 \x01(\x0b\x32\x31.temporal.api.common.v1.WorkerVersionCapabilitiesB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x05 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions"\x9a\x01\n\x19StickyExecutionAttributes\x12?\n\x11worker_task_queue\x18\x01 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12<\n\x19schedule_to_start_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration")\n\x14\x43ompatibleVersionSet\x12\x11\n\tbuild_ids\x18\x01 \x03(\t"j\n\x15TaskQueueReachability\x12\x12\n\ntask_queue\x18\x01 \x01(\t\x12=\n\x0creachability\x18\x02 \x03(\x0e\x32\'.temporal.api.enums.v1.TaskReachability"z\n\x13\x42uildIdReachability\x12\x10\n\x08\x62uild_id\x18\x01 \x01(\t\x12Q\n\x17task_queue_reachability\x18\x02 \x03(\x0b\x32\x30.temporal.api.taskqueue.v1.TaskQueueReachability"+\n\x10RampByPercentage\x12\x17\n\x0framp_percentage\x18\x01 \x01(\x02"\x80\x01\n\x15\x42uildIdAssignmentRule\x12\x17\n\x0ftarget_build_id\x18\x01 \x01(\t\x12\x46\n\x0fpercentage_ramp\x18\x03 \x01(\x0b\x32+.temporal.api.taskqueue.v1.RampByPercentageH\x00\x42\x06\n\x04ramp"Q\n\x1d\x43ompatibleBuildIdRedirectRule\x12\x17\n\x0fsource_build_id\x18\x01 \x01(\t\x12\x17\n\x0ftarget_build_id\x18\x02 \x01(\t"\x93\x01\n TimestampedBuildIdAssignmentRule\x12>\n\x04rule\x18\x01 \x01(\x0b\x32\x30.temporal.api.taskqueue.v1.BuildIdAssignmentRule\x12/\n\x0b\x63reate_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xa3\x01\n(TimestampedCompatibleBuildIdRedirectRule\x12\x46\n\x04rule\x18\x01 \x01(\x0b\x32\x38.temporal.api.taskqueue.v1.CompatibleBuildIdRedirectRule\x12/\n\x0b\x63reate_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"-\n\x0fPollerGroupInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0e\n\x06weight\x18\x02 \x01(\x02">\n\x15PollerScalingDecision\x12%\n\x1dpoll_request_delta_suggestion\x18\x01 \x01(\x05"(\n\tRateLimit\x12\x1b\n\x13requests_per_second\x18\x01 \x01(\x02"j\n\x0e\x43onfigMetadata\x12\x0e\n\x06reason\x18\x01 \x01(\t\x12\x17\n\x0fupdate_identity\x18\x02 \x01(\t\x12/\n\x0bupdate_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\x88\x01\n\x0fRateLimitConfig\x12\x38\n\nrate_limit\x18\x01 \x01(\x0b\x32$.temporal.api.taskqueue.v1.RateLimit\x12;\n\x08metadata\x18\x02 \x01(\x0b\x32).temporal.api.taskqueue.v1.ConfigMetadata"\xd9\x02\n\x0fTaskQueueConfig\x12\x44\n\x10queue_rate_limit\x18\x01 \x01(\x0b\x32*.temporal.api.taskqueue.v1.RateLimitConfig\x12T\n fairness_keys_rate_limit_default\x18\x02 \x01(\x0b\x32*.temporal.api.taskqueue.v1.RateLimitConfig\x12j\n\x19\x66\x61irness_weight_overrides\x18\x03 \x03(\x0b\x32G.temporal.api.taskqueue.v1.TaskQueueConfig.FairnessWeightOverridesEntry\x1a>\n\x1c\x46\x61irnessWeightOverridesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x02:\x02\x38\x01\x42\x98\x01\n\x1cio.temporal.api.taskqueue.v1B\x0cMessageProtoP\x01Z)go.temporal.io/api/taskqueue/v1;taskqueue\xaa\x02\x1bTemporalio.Api.TaskQueue.V1\xea\x02\x1eTemporalio::Api::TaskQueue::V1b\x06proto3' + b'\n\'temporal/api/taskqueue/v1/message.proto\x12\x19temporal.api.taskqueue.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\x1a&temporal/api/enums/v1/task_queue.proto\x1a$temporal/api/common/v1/message.proto\x1a(temporal/api/deployment/v1/message.proto"b\n\tTaskQueue\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x32\n\x04kind\x18\x02 \x01(\x0e\x32$.temporal.api.enums.v1.TaskQueueKind\x12\x13\n\x0bnormal_name\x18\x03 \x01(\t"O\n\x11TaskQueueMetadata\x12:\n\x14max_tasks_per_second\x18\x01 \x01(\x0b\x32\x1c.google.protobuf.DoubleValue"\xda\x02\n\x17TaskQueueVersioningInfo\x12W\n\x1a\x63urrent_deployment_version\x18\x07 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x1b\n\x0f\x63urrent_version\x18\x01 \x01(\tB\x02\x18\x01\x12W\n\x1aramping_deployment_version\x18\t \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x1b\n\x0framping_version\x18\x02 \x01(\tB\x02\x18\x01\x12"\n\x1aramping_version_percentage\x18\x03 \x01(\x02\x12/\n\x0bupdate_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"W\n\x19TaskQueueVersionSelection\x12\x11\n\tbuild_ids\x18\x01 \x03(\t\x12\x13\n\x0bunversioned\x18\x02 \x01(\x08\x12\x12\n\nall_active\x18\x03 \x01(\x08"\x95\x02\n\x14TaskQueueVersionInfo\x12R\n\ntypes_info\x18\x01 \x03(\x0b\x32>.temporal.api.taskqueue.v1.TaskQueueVersionInfo.TypesInfoEntry\x12I\n\x11task_reachability\x18\x02 \x01(\x0e\x32..temporal.api.enums.v1.BuildIdTaskReachability\x1a^\n\x0eTypesInfoEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12;\n\x05value\x18\x02 \x01(\x0b\x32,.temporal.api.taskqueue.v1.TaskQueueTypeInfo:\x02\x38\x01"\x85\x01\n\x11TaskQueueTypeInfo\x12\x36\n\x07pollers\x18\x01 \x03(\x0b\x32%.temporal.api.taskqueue.v1.PollerInfo\x12\x38\n\x05stats\x18\x02 \x01(\x0b\x32).temporal.api.taskqueue.v1.TaskQueueStats"\xc2\x01\n\x0eTaskQueueStats\x12!\n\x19\x61pproximate_backlog_count\x18\x01 \x01(\x03\x12:\n\x17\x61pproximate_backlog_age\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x16\n\x0etasks_add_rate\x18\x03 \x01(\x02\x12\x1b\n\x13tasks_dispatch_rate\x18\x04 \x01(\x02\x12\x1c\n\x14rate_limiting_active\x18\x05 \x01(\x08"\xac\x01\n\x0fTaskQueueStatus\x12\x1a\n\x12\x62\x61\x63klog_count_hint\x18\x01 \x01(\x03\x12\x12\n\nread_level\x18\x02 \x01(\x03\x12\x11\n\tack_level\x18\x03 \x01(\x03\x12\x17\n\x0frate_per_second\x18\x04 \x01(\x01\x12=\n\rtask_id_block\x18\x05 \x01(\x0b\x32&.temporal.api.taskqueue.v1.TaskIdBlock"/\n\x0bTaskIdBlock\x12\x10\n\x08start_id\x18\x01 \x01(\x03\x12\x0e\n\x06\x65nd_id\x18\x02 \x01(\x03"B\n\x1aTaskQueuePartitionMetadata\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x17\n\x0fowner_host_name\x18\x02 \x01(\t"\x9a\x02\n\nPollerInfo\x12\x34\n\x10last_access_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x17\n\x0frate_per_second\x18\x03 \x01(\x01\x12Z\n\x1bworker_version_capabilities\x18\x04 \x01(\x0b\x32\x31.temporal.api.common.v1.WorkerVersionCapabilitiesB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x05 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions"\x9a\x01\n\x19StickyExecutionAttributes\x12?\n\x11worker_task_queue\x18\x01 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12<\n\x19schedule_to_start_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration")\n\x14\x43ompatibleVersionSet\x12\x11\n\tbuild_ids\x18\x01 \x03(\t"j\n\x15TaskQueueReachability\x12\x12\n\ntask_queue\x18\x01 \x01(\t\x12=\n\x0creachability\x18\x02 \x03(\x0e\x32\'.temporal.api.enums.v1.TaskReachability"z\n\x13\x42uildIdReachability\x12\x10\n\x08\x62uild_id\x18\x01 \x01(\t\x12Q\n\x17task_queue_reachability\x18\x02 \x03(\x0b\x32\x30.temporal.api.taskqueue.v1.TaskQueueReachability"+\n\x10RampByPercentage\x12\x17\n\x0framp_percentage\x18\x01 \x01(\x02"\x80\x01\n\x15\x42uildIdAssignmentRule\x12\x17\n\x0ftarget_build_id\x18\x01 \x01(\t\x12\x46\n\x0fpercentage_ramp\x18\x03 \x01(\x0b\x32+.temporal.api.taskqueue.v1.RampByPercentageH\x00\x42\x06\n\x04ramp"Q\n\x1d\x43ompatibleBuildIdRedirectRule\x12\x17\n\x0fsource_build_id\x18\x01 \x01(\t\x12\x17\n\x0ftarget_build_id\x18\x02 \x01(\t"\x93\x01\n TimestampedBuildIdAssignmentRule\x12>\n\x04rule\x18\x01 \x01(\x0b\x32\x30.temporal.api.taskqueue.v1.BuildIdAssignmentRule\x12/\n\x0b\x63reate_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xa3\x01\n(TimestampedCompatibleBuildIdRedirectRule\x12\x46\n\x04rule\x18\x01 \x01(\x0b\x32\x38.temporal.api.taskqueue.v1.CompatibleBuildIdRedirectRule\x12/\n\x0b\x63reate_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"-\n\x0fPollerGroupInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0e\n\x06weight\x18\x02 \x01(\x02"f\n\x10PollerGroupsInfo\x12\x0f\n\x07version\x18\x01 \x01(\x03\x12\x41\n\rpoller_groups\x18\x02 \x03(\x0b\x32*.temporal.api.taskqueue.v1.PollerGroupInfo">\n\x15PollerScalingDecision\x12%\n\x1dpoll_request_delta_suggestion\x18\x01 \x01(\x05"(\n\tRateLimit\x12\x1b\n\x13requests_per_second\x18\x01 \x01(\x02"j\n\x0e\x43onfigMetadata\x12\x0e\n\x06reason\x18\x01 \x01(\t\x12\x17\n\x0fupdate_identity\x18\x02 \x01(\t\x12/\n\x0bupdate_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\x88\x01\n\x0fRateLimitConfig\x12\x38\n\nrate_limit\x18\x01 \x01(\x0b\x32$.temporal.api.taskqueue.v1.RateLimit\x12;\n\x08metadata\x18\x02 \x01(\x0b\x32).temporal.api.taskqueue.v1.ConfigMetadata"\xd9\x02\n\x0fTaskQueueConfig\x12\x44\n\x10queue_rate_limit\x18\x01 \x01(\x0b\x32*.temporal.api.taskqueue.v1.RateLimitConfig\x12T\n fairness_keys_rate_limit_default\x18\x02 \x01(\x0b\x32*.temporal.api.taskqueue.v1.RateLimitConfig\x12j\n\x19\x66\x61irness_weight_overrides\x18\x03 \x03(\x0b\x32G.temporal.api.taskqueue.v1.TaskQueueConfig.FairnessWeightOverridesEntry\x1a>\n\x1c\x46\x61irnessWeightOverridesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x02:\x02\x38\x01\x42\x98\x01\n\x1cio.temporal.api.taskqueue.v1B\x0cMessageProtoP\x01Z)go.temporal.io/api/taskqueue/v1;taskqueue\xaa\x02\x1bTemporalio.Api.TaskQueue.V1\xea\x02\x1eTemporalio::Api::TaskQueue::V1b\x06proto3' ) @@ -69,6 +69,7 @@ "TimestampedCompatibleBuildIdRedirectRule" ] _POLLERGROUPINFO = DESCRIPTOR.message_types_by_name["PollerGroupInfo"] +_POLLERGROUPSINFO = DESCRIPTOR.message_types_by_name["PollerGroupsInfo"] _POLLERSCALINGDECISION = DESCRIPTOR.message_types_by_name["PollerScalingDecision"] _RATELIMIT = DESCRIPTOR.message_types_by_name["RateLimit"] _CONFIGMETADATA = DESCRIPTOR.message_types_by_name["ConfigMetadata"] @@ -318,6 +319,17 @@ ) _sym_db.RegisterMessage(PollerGroupInfo) +PollerGroupsInfo = _reflection.GeneratedProtocolMessageType( + "PollerGroupsInfo", + (_message.Message,), + { + "DESCRIPTOR": _POLLERGROUPSINFO, + "__module__": "temporalio.api.taskqueue.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.taskqueue.v1.PollerGroupsInfo) + }, +) +_sym_db.RegisterMessage(PollerGroupsInfo) + PollerScalingDecision = _reflection.GeneratedProtocolMessageType( "PollerScalingDecision", (_message.Message,), @@ -417,45 +429,47 @@ _TASKQUEUETYPEINFO._serialized_start = 1187 _TASKQUEUETYPEINFO._serialized_end = 1320 _TASKQUEUESTATS._serialized_start = 1323 - _TASKQUEUESTATS._serialized_end = 1487 - _TASKQUEUESTATUS._serialized_start = 1490 - _TASKQUEUESTATUS._serialized_end = 1662 - _TASKIDBLOCK._serialized_start = 1664 - _TASKIDBLOCK._serialized_end = 1711 - _TASKQUEUEPARTITIONMETADATA._serialized_start = 1713 - _TASKQUEUEPARTITIONMETADATA._serialized_end = 1779 - _POLLERINFO._serialized_start = 1782 - _POLLERINFO._serialized_end = 2064 - _STICKYEXECUTIONATTRIBUTES._serialized_start = 2067 - _STICKYEXECUTIONATTRIBUTES._serialized_end = 2221 - _COMPATIBLEVERSIONSET._serialized_start = 2223 - _COMPATIBLEVERSIONSET._serialized_end = 2264 - _TASKQUEUEREACHABILITY._serialized_start = 2266 - _TASKQUEUEREACHABILITY._serialized_end = 2372 - _BUILDIDREACHABILITY._serialized_start = 2374 - _BUILDIDREACHABILITY._serialized_end = 2496 - _RAMPBYPERCENTAGE._serialized_start = 2498 - _RAMPBYPERCENTAGE._serialized_end = 2541 - _BUILDIDASSIGNMENTRULE._serialized_start = 2544 - _BUILDIDASSIGNMENTRULE._serialized_end = 2672 - _COMPATIBLEBUILDIDREDIRECTRULE._serialized_start = 2674 - _COMPATIBLEBUILDIDREDIRECTRULE._serialized_end = 2755 - _TIMESTAMPEDBUILDIDASSIGNMENTRULE._serialized_start = 2758 - _TIMESTAMPEDBUILDIDASSIGNMENTRULE._serialized_end = 2905 - _TIMESTAMPEDCOMPATIBLEBUILDIDREDIRECTRULE._serialized_start = 2908 - _TIMESTAMPEDCOMPATIBLEBUILDIDREDIRECTRULE._serialized_end = 3071 - _POLLERGROUPINFO._serialized_start = 3073 - _POLLERGROUPINFO._serialized_end = 3118 - _POLLERSCALINGDECISION._serialized_start = 3120 - _POLLERSCALINGDECISION._serialized_end = 3182 - _RATELIMIT._serialized_start = 3184 - _RATELIMIT._serialized_end = 3224 - _CONFIGMETADATA._serialized_start = 3226 - _CONFIGMETADATA._serialized_end = 3332 - _RATELIMITCONFIG._serialized_start = 3335 - _RATELIMITCONFIG._serialized_end = 3471 - _TASKQUEUECONFIG._serialized_start = 3474 - _TASKQUEUECONFIG._serialized_end = 3819 - _TASKQUEUECONFIG_FAIRNESSWEIGHTOVERRIDESENTRY._serialized_start = 3757 - _TASKQUEUECONFIG_FAIRNESSWEIGHTOVERRIDESENTRY._serialized_end = 3819 + _TASKQUEUESTATS._serialized_end = 1517 + _TASKQUEUESTATUS._serialized_start = 1520 + _TASKQUEUESTATUS._serialized_end = 1692 + _TASKIDBLOCK._serialized_start = 1694 + _TASKIDBLOCK._serialized_end = 1741 + _TASKQUEUEPARTITIONMETADATA._serialized_start = 1743 + _TASKQUEUEPARTITIONMETADATA._serialized_end = 1809 + _POLLERINFO._serialized_start = 1812 + _POLLERINFO._serialized_end = 2094 + _STICKYEXECUTIONATTRIBUTES._serialized_start = 2097 + _STICKYEXECUTIONATTRIBUTES._serialized_end = 2251 + _COMPATIBLEVERSIONSET._serialized_start = 2253 + _COMPATIBLEVERSIONSET._serialized_end = 2294 + _TASKQUEUEREACHABILITY._serialized_start = 2296 + _TASKQUEUEREACHABILITY._serialized_end = 2402 + _BUILDIDREACHABILITY._serialized_start = 2404 + _BUILDIDREACHABILITY._serialized_end = 2526 + _RAMPBYPERCENTAGE._serialized_start = 2528 + _RAMPBYPERCENTAGE._serialized_end = 2571 + _BUILDIDASSIGNMENTRULE._serialized_start = 2574 + _BUILDIDASSIGNMENTRULE._serialized_end = 2702 + _COMPATIBLEBUILDIDREDIRECTRULE._serialized_start = 2704 + _COMPATIBLEBUILDIDREDIRECTRULE._serialized_end = 2785 + _TIMESTAMPEDBUILDIDASSIGNMENTRULE._serialized_start = 2788 + _TIMESTAMPEDBUILDIDASSIGNMENTRULE._serialized_end = 2935 + _TIMESTAMPEDCOMPATIBLEBUILDIDREDIRECTRULE._serialized_start = 2938 + _TIMESTAMPEDCOMPATIBLEBUILDIDREDIRECTRULE._serialized_end = 3101 + _POLLERGROUPINFO._serialized_start = 3103 + _POLLERGROUPINFO._serialized_end = 3148 + _POLLERGROUPSINFO._serialized_start = 3150 + _POLLERGROUPSINFO._serialized_end = 3252 + _POLLERSCALINGDECISION._serialized_start = 3254 + _POLLERSCALINGDECISION._serialized_end = 3316 + _RATELIMIT._serialized_start = 3318 + _RATELIMIT._serialized_end = 3358 + _CONFIGMETADATA._serialized_start = 3360 + _CONFIGMETADATA._serialized_end = 3466 + _RATELIMITCONFIG._serialized_start = 3469 + _RATELIMITCONFIG._serialized_end = 3605 + _TASKQUEUECONFIG._serialized_start = 3608 + _TASKQUEUECONFIG._serialized_end = 3953 + _TASKQUEUECONFIG_FAIRNESSWEIGHTOVERRIDESENTRY._serialized_start = 3891 + _TASKQUEUECONFIG_FAIRNESSWEIGHTOVERRIDESENTRY._serialized_end = 3953 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/taskqueue/v1/message_pb2.pyi b/temporalio/api/taskqueue/v1/message_pb2.pyi index e614430d6..3b43a559f 100644 --- a/temporalio/api/taskqueue/v1/message_pb2.pyi +++ b/temporalio/api/taskqueue/v1/message_pb2.pyi @@ -316,6 +316,7 @@ class TaskQueueStats(google.protobuf.message.Message): APPROXIMATE_BACKLOG_AGE_FIELD_NUMBER: builtins.int TASKS_ADD_RATE_FIELD_NUMBER: builtins.int TASKS_DISPATCH_RATE_FIELD_NUMBER: builtins.int + RATE_LIMITING_ACTIVE_FIELD_NUMBER: builtins.int approximate_backlog_count: builtins.int """The approximate number of tasks backlogged in this task queue. May count expired tasks but eventually converges to the right value. Can be relied upon for scaling decisions. @@ -365,6 +366,12 @@ class TaskQueueStats(google.protobuf.message.Message): workflow goes to a normal queue, and the rest workflow tasks go to the Sticky queue associated with a specific worker instance. """ + rate_limiting_active: builtins.bool + """Whether rate limiting blocked any dispatches within the recent observation window (approximately + 30 seconds). When true, adding more workers will not increase throughput — the bottleneck is the + rate limit, not worker count. This field is useful for auto-scaling systems to avoid unnecessary + scale-up. + """ def __init__( self, *, @@ -372,6 +379,7 @@ class TaskQueueStats(google.protobuf.message.Message): approximate_backlog_age: google.protobuf.duration_pb2.Duration | None = ..., tasks_add_rate: builtins.float = ..., tasks_dispatch_rate: builtins.float = ..., + rate_limiting_active: builtins.bool = ..., ) -> None: ... def HasField( self, @@ -386,6 +394,8 @@ class TaskQueueStats(google.protobuf.message.Message): b"approximate_backlog_age", "approximate_backlog_count", b"approximate_backlog_count", + "rate_limiting_active", + b"rate_limiting_active", "tasks_add_rate", b"tasks_add_rate", "tasks_dispatch_rate", @@ -914,6 +924,42 @@ class PollerGroupInfo(google.protobuf.message.Message): global___PollerGroupInfo = PollerGroupInfo +class PollerGroupsInfo(google.protobuf.message.Message): + """A versioned snapshot of the poller groups the client should use for future polls to a task + queue. The version is monotonically increasing so that a client can ignore a snapshot that is + older than the one it has already applied. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VERSION_FIELD_NUMBER: builtins.int + POLLER_GROUPS_FIELD_NUMBER: builtins.int + version: builtins.int + """Monotonically increasing version of this snapshot. A client should ignore any snapshot whose + version is not greater than the one it last applied. + """ + @property + def poller_groups( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ + global___PollerGroupInfo + ]: + """The weighted list of poller groups the client should use for future polls to this task queue.""" + def __init__( + self, + *, + version: builtins.int = ..., + poller_groups: collections.abc.Iterable[global___PollerGroupInfo] | None = ..., + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "poller_groups", b"poller_groups", "version", b"version" + ], + ) -> None: ... + +global___PollerGroupsInfo = PollerGroupsInfo + class PollerScalingDecision(google.protobuf.message.Message): """Attached to task responses to give hints to the SDK about how it may adjust its number of pollers. diff --git a/temporalio/api/worker/v1/__init__.py b/temporalio/api/worker/v1/__init__.py index 5c1afdf4f..8b6e343e5 100644 --- a/temporalio/api/worker/v1/__init__.py +++ b/temporalio/api/worker/v1/__init__.py @@ -1,6 +1,7 @@ from .message_pb2 import ( CancelActivityCommand, CancelActivityResult, + EnvironmentInfo, PluginInfo, StorageDriverInfo, WorkerCommand, @@ -16,6 +17,7 @@ __all__ = [ "CancelActivityCommand", "CancelActivityResult", + "EnvironmentInfo", "PluginInfo", "StorageDriverInfo", "WorkerCommand", diff --git a/temporalio/api/worker/v1/message_pb2.py b/temporalio/api/worker/v1/message_pb2.py index 4de9c5b24..78b58817a 100644 --- a/temporalio/api/worker/v1/message_pb2.py +++ b/temporalio/api/worker/v1/message_pb2.py @@ -25,7 +25,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n$temporal/api/worker/v1/message.proto\x12\x16temporal.api.worker.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a(temporal/api/deployment/v1/message.proto\x1a"temporal/api/enums/v1/common.proto"\x82\x01\n\x10WorkerPollerInfo\x12\x17\n\x0f\x63urrent_pollers\x18\x01 \x01(\x05\x12=\n\x19last_successful_poll_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x16\n\x0eis_autoscaling\x18\x03 \x01(\x08"\xf1\x01\n\x0fWorkerSlotsInfo\x12\x1f\n\x17\x63urrent_available_slots\x18\x01 \x01(\x05\x12\x1a\n\x12\x63urrent_used_slots\x18\x02 \x01(\x05\x12\x1a\n\x12slot_supplier_kind\x18\x03 \x01(\t\x12\x1d\n\x15total_processed_tasks\x18\x04 \x01(\x05\x12\x1a\n\x12total_failed_tasks\x18\x05 \x01(\x05\x12%\n\x1dlast_interval_processed_tasks\x18\x06 \x01(\x05\x12#\n\x1blast_interval_failure_tasks\x18\x07 \x01(\x05"\x94\x01\n\x0eWorkerHostInfo\x12\x11\n\thost_name\x18\x01 \x01(\t\x12\x1b\n\x13worker_grouping_key\x18\x05 \x01(\t\x12\x12\n\nprocess_id\x18\x02 \x01(\t\x12\x1e\n\x16\x63urrent_host_cpu_usage\x18\x03 \x01(\x02\x12\x1e\n\x16\x63urrent_host_mem_usage\x18\x04 \x01(\x02"\x8b\n\n\x0fWorkerHeartbeat\x12\x1b\n\x13worker_instance_key\x18\x01 \x01(\t\x12\x17\n\x0fworker_identity\x18\x02 \x01(\t\x12\x39\n\thost_info\x18\x03 \x01(\x0b\x32&.temporal.api.worker.v1.WorkerHostInfo\x12\x12\n\ntask_queue\x18\x04 \x01(\t\x12O\n\x12\x64\x65ployment_version\x18\x05 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x10\n\x08sdk_name\x18\x06 \x01(\t\x12\x13\n\x0bsdk_version\x18\x07 \x01(\t\x12\x33\n\x06status\x18\x08 \x01(\x0e\x32#.temporal.api.enums.v1.WorkerStatus\x12.\n\nstart_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x32\n\x0eheartbeat_time\x18\n \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12?\n\x1c\x65lapsed_since_last_heartbeat\x18\x0b \x01(\x0b\x32\x19.google.protobuf.Duration\x12I\n\x18workflow_task_slots_info\x18\x0c \x01(\x0b\x32\'.temporal.api.worker.v1.WorkerSlotsInfo\x12I\n\x18\x61\x63tivity_task_slots_info\x18\r \x01(\x0b\x32\'.temporal.api.worker.v1.WorkerSlotsInfo\x12\x46\n\x15nexus_task_slots_info\x18\x0e \x01(\x0b\x32\'.temporal.api.worker.v1.WorkerSlotsInfo\x12J\n\x19local_activity_slots_info\x18\x0f \x01(\x0b\x32\'.temporal.api.worker.v1.WorkerSlotsInfo\x12\x46\n\x14workflow_poller_info\x18\x10 \x01(\x0b\x32(.temporal.api.worker.v1.WorkerPollerInfo\x12M\n\x1bworkflow_sticky_poller_info\x18\x11 \x01(\x0b\x32(.temporal.api.worker.v1.WorkerPollerInfo\x12\x46\n\x14\x61\x63tivity_poller_info\x18\x12 \x01(\x0b\x32(.temporal.api.worker.v1.WorkerPollerInfo\x12\x43\n\x11nexus_poller_info\x18\x13 \x01(\x0b\x32(.temporal.api.worker.v1.WorkerPollerInfo\x12\x1e\n\x16total_sticky_cache_hit\x18\x14 \x01(\x05\x12\x1f\n\x17total_sticky_cache_miss\x18\x15 \x01(\x05\x12!\n\x19\x63urrent_sticky_cache_size\x18\x16 \x01(\x05\x12\x33\n\x07plugins\x18\x17 \x03(\x0b\x32".temporal.api.worker.v1.PluginInfo\x12:\n\x07\x64rivers\x18\x18 \x03(\x0b\x32).temporal.api.worker.v1.StorageDriverInfo"O\n\nWorkerInfo\x12\x41\n\x10worker_heartbeat\x18\x01 \x01(\x0b\x32\'.temporal.api.worker.v1.WorkerHeartbeat"\xec\x03\n\x0eWorkerListInfo\x12\x1b\n\x13worker_instance_key\x18\x01 \x01(\t\x12\x17\n\x0fworker_identity\x18\x02 \x01(\t\x12\x12\n\ntask_queue\x18\x03 \x01(\t\x12O\n\x12\x64\x65ployment_version\x18\x04 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x10\n\x08sdk_name\x18\x05 \x01(\t\x12\x13\n\x0bsdk_version\x18\x06 \x01(\t\x12\x33\n\x06status\x18\x07 \x01(\x0e\x32#.temporal.api.enums.v1.WorkerStatus\x12.\n\nstart_time\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x11\n\thost_name\x18\t \x01(\t\x12\x1b\n\x13worker_grouping_key\x18\n \x01(\t\x12\x12\n\nprocess_id\x18\x0b \x01(\t\x12\x33\n\x07plugins\x18\x0c \x03(\x0b\x32".temporal.api.worker.v1.PluginInfo\x12:\n\x07\x64rivers\x18\r \x03(\x0b\x32).temporal.api.worker.v1.StorageDriverInfo"+\n\nPluginInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t"!\n\x11StorageDriverInfo\x12\x0c\n\x04type\x18\x01 \x01(\t"a\n\rWorkerCommand\x12H\n\x0f\x63\x61ncel_activity\x18\x01 \x01(\x0b\x32-.temporal.api.worker.v1.CancelActivityCommandH\x00\x42\x06\n\x04type"+\n\x15\x43\x61ncelActivityCommand\x12\x12\n\ntask_token\x18\x01 \x01(\x0c"f\n\x13WorkerCommandResult\x12G\n\x0f\x63\x61ncel_activity\x18\x01 \x01(\x0b\x32,.temporal.api.worker.v1.CancelActivityResultH\x00\x42\x06\n\x04type"\x16\n\x14\x43\x61ncelActivityResultB\x89\x01\n\x19io.temporal.api.worker.v1B\x0cMessageProtoP\x01Z#go.temporal.io/api/worker/v1;worker\xaa\x02\x18Temporalio.Api.Worker.V1\xea\x02\x1bTemporalio::Api::Worker::V1b\x06proto3' + b'\n$temporal/api/worker/v1/message.proto\x12\x16temporal.api.worker.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a(temporal/api/deployment/v1/message.proto\x1a"temporal/api/enums/v1/common.proto"\x82\x01\n\x10WorkerPollerInfo\x12\x17\n\x0f\x63urrent_pollers\x18\x01 \x01(\x05\x12=\n\x19last_successful_poll_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x16\n\x0eis_autoscaling\x18\x03 \x01(\x08"\xf1\x01\n\x0fWorkerSlotsInfo\x12\x1f\n\x17\x63urrent_available_slots\x18\x01 \x01(\x05\x12\x1a\n\x12\x63urrent_used_slots\x18\x02 \x01(\x05\x12\x1a\n\x12slot_supplier_kind\x18\x03 \x01(\t\x12\x1d\n\x15total_processed_tasks\x18\x04 \x01(\x05\x12\x1a\n\x12total_failed_tasks\x18\x05 \x01(\x05\x12%\n\x1dlast_interval_processed_tasks\x18\x06 \x01(\x05\x12#\n\x1blast_interval_failure_tasks\x18\x07 \x01(\x05"\x94\x01\n\x0eWorkerHostInfo\x12\x11\n\thost_name\x18\x01 \x01(\t\x12\x1b\n\x13worker_grouping_key\x18\x05 \x01(\t\x12\x12\n\nprocess_id\x18\x02 \x01(\t\x12\x1e\n\x16\x63urrent_host_cpu_usage\x18\x03 \x01(\x02\x12\x1e\n\x16\x63urrent_host_mem_usage\x18\x04 \x01(\x02"\xc9\n\n\x0fWorkerHeartbeat\x12\x1b\n\x13worker_instance_key\x18\x01 \x01(\t\x12\x17\n\x0fworker_identity\x18\x02 \x01(\t\x12\x39\n\thost_info\x18\x03 \x01(\x0b\x32&.temporal.api.worker.v1.WorkerHostInfo\x12\x12\n\ntask_queue\x18\x04 \x01(\t\x12O\n\x12\x64\x65ployment_version\x18\x05 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x10\n\x08sdk_name\x18\x06 \x01(\t\x12\x13\n\x0bsdk_version\x18\x07 \x01(\t\x12\x33\n\x06status\x18\x08 \x01(\x0e\x32#.temporal.api.enums.v1.WorkerStatus\x12.\n\nstart_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x32\n\x0eheartbeat_time\x18\n \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12?\n\x1c\x65lapsed_since_last_heartbeat\x18\x0b \x01(\x0b\x32\x19.google.protobuf.Duration\x12I\n\x18workflow_task_slots_info\x18\x0c \x01(\x0b\x32\'.temporal.api.worker.v1.WorkerSlotsInfo\x12I\n\x18\x61\x63tivity_task_slots_info\x18\r \x01(\x0b\x32\'.temporal.api.worker.v1.WorkerSlotsInfo\x12\x46\n\x15nexus_task_slots_info\x18\x0e \x01(\x0b\x32\'.temporal.api.worker.v1.WorkerSlotsInfo\x12J\n\x19local_activity_slots_info\x18\x0f \x01(\x0b\x32\'.temporal.api.worker.v1.WorkerSlotsInfo\x12\x46\n\x14workflow_poller_info\x18\x10 \x01(\x0b\x32(.temporal.api.worker.v1.WorkerPollerInfo\x12M\n\x1bworkflow_sticky_poller_info\x18\x11 \x01(\x0b\x32(.temporal.api.worker.v1.WorkerPollerInfo\x12\x46\n\x14\x61\x63tivity_poller_info\x18\x12 \x01(\x0b\x32(.temporal.api.worker.v1.WorkerPollerInfo\x12\x43\n\x11nexus_poller_info\x18\x13 \x01(\x0b\x32(.temporal.api.worker.v1.WorkerPollerInfo\x12\x1e\n\x16total_sticky_cache_hit\x18\x14 \x01(\x05\x12\x1f\n\x17total_sticky_cache_miss\x18\x15 \x01(\x05\x12!\n\x19\x63urrent_sticky_cache_size\x18\x16 \x01(\x05\x12\x33\n\x07plugins\x18\x17 \x03(\x0b\x32".temporal.api.worker.v1.PluginInfo\x12:\n\x07\x64rivers\x18\x18 \x03(\x0b\x32).temporal.api.worker.v1.StorageDriverInfo\x12<\n\x0b\x65nvironment\x18\x19 \x01(\x0b\x32\'.temporal.api.worker.v1.EnvironmentInfo"O\n\nWorkerInfo\x12\x41\n\x10worker_heartbeat\x18\x01 \x01(\x0b\x32\'.temporal.api.worker.v1.WorkerHeartbeat"\xec\x03\n\x0eWorkerListInfo\x12\x1b\n\x13worker_instance_key\x18\x01 \x01(\t\x12\x17\n\x0fworker_identity\x18\x02 \x01(\t\x12\x12\n\ntask_queue\x18\x03 \x01(\t\x12O\n\x12\x64\x65ployment_version\x18\x04 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x10\n\x08sdk_name\x18\x05 \x01(\t\x12\x13\n\x0bsdk_version\x18\x06 \x01(\t\x12\x33\n\x06status\x18\x07 \x01(\x0e\x32#.temporal.api.enums.v1.WorkerStatus\x12.\n\nstart_time\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x11\n\thost_name\x18\t \x01(\t\x12\x1b\n\x13worker_grouping_key\x18\n \x01(\t\x12\x12\n\nprocess_id\x18\x0b \x01(\t\x12\x33\n\x07plugins\x18\x0c \x03(\x0b\x32".temporal.api.worker.v1.PluginInfo\x12:\n\x07\x64rivers\x18\r \x03(\x0b\x32).temporal.api.worker.v1.StorageDriverInfo"+\n\nPluginInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t"!\n\x11StorageDriverInfo\x12\x0c\n\x04type\x18\x01 \x01(\t"\xa7\x11\n\x0f\x45nvironmentInfo\x12\x41\n\x08runtimes\x18\x01 \x03(\x0b\x32/.temporal.api.worker.v1.EnvironmentInfo.Runtime\x12X\n\x14hosting_environments\x18\x02 \x03(\x0b\x32:.temporal.api.worker.v1.EnvironmentInfo.HostingEnvironment\x12\x42\n\x08platform\x18\x03 \x01(\x0b\x32\x30.temporal.api.worker.v1.EnvironmentInfo.Platform\x1a\x94\x03\n\x07Runtime\x12I\n\x04type\x18\x01 \x01(\x0e\x32;.temporal.api.worker.v1.EnvironmentInfo.Runtime.RuntimeType\x12\x0f\n\x07version\x18\x02 \x01(\t"\xac\x02\n\x0bRuntimeType\x12\x1c\n\x18RUNTIME_TYPE_UNSPECIFIED\x10\x00\x12\x14\n\x10RUNTIME_TYPE_JVM\x10\x01\x12\x18\n\x14RUNTIME_TYPE_CPYTHON\x10\x02\x12\x15\n\x11RUNTIME_TYPE_NODE\x10\x03\x12\x14\n\x10RUNTIME_TYPE_BUN\x10\x04\x12\x16\n\x12RUNTIME_TYPE_CRUBY\x10\x05\x12\x13\n\x0fRUNTIME_TYPE_GO\x10\x06\x12!\n\x1dRUNTIME_TYPE_DOTNET_FRAMEWORK\x10\x07\x12\x1c\n\x18RUNTIME_TYPE_DOTNET_CORE\x10\x08\x12\x17\n\x13RUNTIME_TYPE_NATIVE\x10\t\x12\x1b\n\x17RUNTIME_TYPE_ROADRUNNER\x10\n\x1a\xd1\x04\n\x12HostingEnvironment\x12_\n\x04type\x18\x01 \x01(\x0e\x32Q.temporal.api.worker.v1.EnvironmentInfo.HostingEnvironment.HostingEnvironmentType\x12\x0f\n\x07version\x18\x02 \x01(\t"\xc8\x03\n\x16HostingEnvironmentType\x12(\n$HOSTING_ENVIRONMENT_TYPE_UNSPECIFIED\x10\x00\x12#\n\x1fHOSTING_ENVIRONMENT_TYPE_DOCKER\x10\x01\x12 \n\x1cHOSTING_ENVIRONMENT_TYPE_K8S\x10\x02\x12\'\n#HOSTING_ENVIRONMENT_TYPE_AWS_LAMBDA\x10\x03\x12$\n HOSTING_ENVIRONMENT_TYPE_AWS_ECS\x10\x04\x12-\n)HOSTING_ENVIRONMENT_TYPE_GOOGLE_CLOUD_RUN\x10\x06\x12.\n*HOSTING_ENVIRONMENT_TYPE_GOOGLE_APP_ENGINE\x10\x07\x12.\n*HOSTING_ENVIRONMENT_TYPE_AZURE_APP_SERVICE\x10\x08\x12,\n(HOSTING_ENVIRONMENT_TYPE_AZURE_FUNCTIONS\x10\t\x12\x31\n-HOSTING_ENVIRONMENT_TYPE_AZURE_CONTAINER_APPS\x10\n\x1a\xf1\x01\n\x08Platform\x12\x46\n\x05linux\x18\x01 \x01(\x0b\x32\x35.temporal.api.worker.v1.EnvironmentInfo.LinuxPlatformH\x00\x12\x46\n\x05macos\x18\x02 \x01(\x0b\x32\x35.temporal.api.worker.v1.EnvironmentInfo.MacOSPlatformH\x00\x12J\n\x07windows\x18\x03 \x01(\x0b\x32\x37.temporal.api.worker.v1.EnvironmentInfo.WindowsPlatformH\x00\x42\t\n\x07variant\x1a\xf3\x01\n\rLinuxPlatform\x12\x0f\n\x07version\x18\x01 \x01(\t\x12J\n\x0c\x61rchitecture\x18\x02 \x01(\x0e\x32\x34.temporal.api.worker.v1.EnvironmentInfo.Architecture\x12H\n\x04libc\x18\x03 \x01(\x0e\x32:.temporal.api.worker.v1.EnvironmentInfo.LinuxPlatform.Libc";\n\x04Libc\x12\x14\n\x10LIBC_UNSPECIFIED\x10\x00\x12\x0e\n\nLIBC_GLIBC\x10\x01\x12\r\n\tLIBC_MUSL\x10\x02\x1al\n\rMacOSPlatform\x12\x0f\n\x07version\x18\x01 \x01(\t\x12J\n\x0c\x61rchitecture\x18\x02 \x01(\x0e\x32\x34.temporal.api.worker.v1.EnvironmentInfo.Architecture\x1a\x91\x02\n\x0fWindowsPlatform\x12\x0f\n\x07version\x18\x01 \x01(\t\x12J\n\x0c\x61rchitecture\x18\x02 \x01(\x0e\x32\x34.temporal.api.worker.v1.EnvironmentInfo.Architecture\x12H\n\x03\x63rt\x18\x03 \x01(\x0e\x32;.temporal.api.worker.v1.EnvironmentInfo.WindowsPlatform.Crt"W\n\x03\x43rt\x12\x13\n\x0f\x43RT_UNSPECIFIED\x10\x00\x12\x0c\n\x08\x43RT_UCRT\x10\x01\x12\x0e\n\nCRT_MSVCRT\x10\x02\x12\r\n\tCRT_MINGW\x10\x03\x12\x0e\n\nCRT_CYGWIN\x10\x04"\\\n\x0c\x41rchitecture\x12\x1c\n\x18\x41RCHITECTURE_UNSPECIFIED\x10\x00\x12\x16\n\x12\x41RCHITECTURE_AMD64\x10\x01\x12\x16\n\x12\x41RCHITECTURE_ARM64\x10\x02"a\n\rWorkerCommand\x12H\n\x0f\x63\x61ncel_activity\x18\x01 \x01(\x0b\x32-.temporal.api.worker.v1.CancelActivityCommandH\x00\x42\x06\n\x04type"+\n\x15\x43\x61ncelActivityCommand\x12\x12\n\ntask_token\x18\x01 \x01(\x0c"f\n\x13WorkerCommandResult\x12G\n\x0f\x63\x61ncel_activity\x18\x01 \x01(\x0b\x32,.temporal.api.worker.v1.CancelActivityResultH\x00\x42\x06\n\x04type"\x16\n\x14\x43\x61ncelActivityResultB\x89\x01\n\x19io.temporal.api.worker.v1B\x0cMessageProtoP\x01Z#go.temporal.io/api/worker/v1;worker\xaa\x02\x18Temporalio.Api.Worker.V1\xea\x02\x1bTemporalio::Api::Worker::V1b\x06proto3' ) @@ -37,10 +37,34 @@ _WORKERLISTINFO = DESCRIPTOR.message_types_by_name["WorkerListInfo"] _PLUGININFO = DESCRIPTOR.message_types_by_name["PluginInfo"] _STORAGEDRIVERINFO = DESCRIPTOR.message_types_by_name["StorageDriverInfo"] +_ENVIRONMENTINFO = DESCRIPTOR.message_types_by_name["EnvironmentInfo"] +_ENVIRONMENTINFO_RUNTIME = _ENVIRONMENTINFO.nested_types_by_name["Runtime"] +_ENVIRONMENTINFO_HOSTINGENVIRONMENT = _ENVIRONMENTINFO.nested_types_by_name[ + "HostingEnvironment" +] +_ENVIRONMENTINFO_PLATFORM = _ENVIRONMENTINFO.nested_types_by_name["Platform"] +_ENVIRONMENTINFO_LINUXPLATFORM = _ENVIRONMENTINFO.nested_types_by_name["LinuxPlatform"] +_ENVIRONMENTINFO_MACOSPLATFORM = _ENVIRONMENTINFO.nested_types_by_name["MacOSPlatform"] +_ENVIRONMENTINFO_WINDOWSPLATFORM = _ENVIRONMENTINFO.nested_types_by_name[ + "WindowsPlatform" +] _WORKERCOMMAND = DESCRIPTOR.message_types_by_name["WorkerCommand"] _CANCELACTIVITYCOMMAND = DESCRIPTOR.message_types_by_name["CancelActivityCommand"] _WORKERCOMMANDRESULT = DESCRIPTOR.message_types_by_name["WorkerCommandResult"] _CANCELACTIVITYRESULT = DESCRIPTOR.message_types_by_name["CancelActivityResult"] +_ENVIRONMENTINFO_RUNTIME_RUNTIMETYPE = _ENVIRONMENTINFO_RUNTIME.enum_types_by_name[ + "RuntimeType" +] +_ENVIRONMENTINFO_HOSTINGENVIRONMENT_HOSTINGENVIRONMENTTYPE = ( + _ENVIRONMENTINFO_HOSTINGENVIRONMENT.enum_types_by_name["HostingEnvironmentType"] +) +_ENVIRONMENTINFO_LINUXPLATFORM_LIBC = _ENVIRONMENTINFO_LINUXPLATFORM.enum_types_by_name[ + "Libc" +] +_ENVIRONMENTINFO_WINDOWSPLATFORM_CRT = ( + _ENVIRONMENTINFO_WINDOWSPLATFORM.enum_types_by_name["Crt"] +) +_ENVIRONMENTINFO_ARCHITECTURE = _ENVIRONMENTINFO.enum_types_by_name["Architecture"] WorkerPollerInfo = _reflection.GeneratedProtocolMessageType( "WorkerPollerInfo", (_message.Message,), @@ -129,6 +153,77 @@ ) _sym_db.RegisterMessage(StorageDriverInfo) +EnvironmentInfo = _reflection.GeneratedProtocolMessageType( + "EnvironmentInfo", + (_message.Message,), + { + "Runtime": _reflection.GeneratedProtocolMessageType( + "Runtime", + (_message.Message,), + { + "DESCRIPTOR": _ENVIRONMENTINFO_RUNTIME, + "__module__": "temporalio.api.worker.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.worker.v1.EnvironmentInfo.Runtime) + }, + ), + "HostingEnvironment": _reflection.GeneratedProtocolMessageType( + "HostingEnvironment", + (_message.Message,), + { + "DESCRIPTOR": _ENVIRONMENTINFO_HOSTINGENVIRONMENT, + "__module__": "temporalio.api.worker.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.worker.v1.EnvironmentInfo.HostingEnvironment) + }, + ), + "Platform": _reflection.GeneratedProtocolMessageType( + "Platform", + (_message.Message,), + { + "DESCRIPTOR": _ENVIRONMENTINFO_PLATFORM, + "__module__": "temporalio.api.worker.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.worker.v1.EnvironmentInfo.Platform) + }, + ), + "LinuxPlatform": _reflection.GeneratedProtocolMessageType( + "LinuxPlatform", + (_message.Message,), + { + "DESCRIPTOR": _ENVIRONMENTINFO_LINUXPLATFORM, + "__module__": "temporalio.api.worker.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.worker.v1.EnvironmentInfo.LinuxPlatform) + }, + ), + "MacOSPlatform": _reflection.GeneratedProtocolMessageType( + "MacOSPlatform", + (_message.Message,), + { + "DESCRIPTOR": _ENVIRONMENTINFO_MACOSPLATFORM, + "__module__": "temporalio.api.worker.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.worker.v1.EnvironmentInfo.MacOSPlatform) + }, + ), + "WindowsPlatform": _reflection.GeneratedProtocolMessageType( + "WindowsPlatform", + (_message.Message,), + { + "DESCRIPTOR": _ENVIRONMENTINFO_WINDOWSPLATFORM, + "__module__": "temporalio.api.worker.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.worker.v1.EnvironmentInfo.WindowsPlatform) + }, + ), + "DESCRIPTOR": _ENVIRONMENTINFO, + "__module__": "temporalio.api.worker.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.worker.v1.EnvironmentInfo) + }, +) +_sym_db.RegisterMessage(EnvironmentInfo) +_sym_db.RegisterMessage(EnvironmentInfo.Runtime) +_sym_db.RegisterMessage(EnvironmentInfo.HostingEnvironment) +_sym_db.RegisterMessage(EnvironmentInfo.Platform) +_sym_db.RegisterMessage(EnvironmentInfo.LinuxPlatform) +_sym_db.RegisterMessage(EnvironmentInfo.MacOSPlatform) +_sym_db.RegisterMessage(EnvironmentInfo.WindowsPlatform) + WorkerCommand = _reflection.GeneratedProtocolMessageType( "WorkerCommand", (_message.Message,), @@ -183,21 +278,45 @@ _WORKERHOSTINFO._serialized_start = 585 _WORKERHOSTINFO._serialized_end = 733 _WORKERHEARTBEAT._serialized_start = 736 - _WORKERHEARTBEAT._serialized_end = 2027 - _WORKERINFO._serialized_start = 2029 - _WORKERINFO._serialized_end = 2108 - _WORKERLISTINFO._serialized_start = 2111 - _WORKERLISTINFO._serialized_end = 2603 - _PLUGININFO._serialized_start = 2605 - _PLUGININFO._serialized_end = 2648 - _STORAGEDRIVERINFO._serialized_start = 2650 - _STORAGEDRIVERINFO._serialized_end = 2683 - _WORKERCOMMAND._serialized_start = 2685 - _WORKERCOMMAND._serialized_end = 2782 - _CANCELACTIVITYCOMMAND._serialized_start = 2784 - _CANCELACTIVITYCOMMAND._serialized_end = 2827 - _WORKERCOMMANDRESULT._serialized_start = 2829 - _WORKERCOMMANDRESULT._serialized_end = 2931 - _CANCELACTIVITYRESULT._serialized_start = 2933 - _CANCELACTIVITYRESULT._serialized_end = 2955 + _WORKERHEARTBEAT._serialized_end = 2089 + _WORKERINFO._serialized_start = 2091 + _WORKERINFO._serialized_end = 2170 + _WORKERLISTINFO._serialized_start = 2173 + _WORKERLISTINFO._serialized_end = 2665 + _PLUGININFO._serialized_start = 2667 + _PLUGININFO._serialized_end = 2710 + _STORAGEDRIVERINFO._serialized_start = 2712 + _STORAGEDRIVERINFO._serialized_end = 2745 + _ENVIRONMENTINFO._serialized_start = 2748 + _ENVIRONMENTINFO._serialized_end = 4963 + _ENVIRONMENTINFO_RUNTIME._serialized_start = 2993 + _ENVIRONMENTINFO_RUNTIME._serialized_end = 3397 + _ENVIRONMENTINFO_RUNTIME_RUNTIMETYPE._serialized_start = 3097 + _ENVIRONMENTINFO_RUNTIME_RUNTIMETYPE._serialized_end = 3397 + _ENVIRONMENTINFO_HOSTINGENVIRONMENT._serialized_start = 3400 + _ENVIRONMENTINFO_HOSTINGENVIRONMENT._serialized_end = 3993 + _ENVIRONMENTINFO_HOSTINGENVIRONMENT_HOSTINGENVIRONMENTTYPE._serialized_start = 3537 + _ENVIRONMENTINFO_HOSTINGENVIRONMENT_HOSTINGENVIRONMENTTYPE._serialized_end = 3993 + _ENVIRONMENTINFO_PLATFORM._serialized_start = 3996 + _ENVIRONMENTINFO_PLATFORM._serialized_end = 4237 + _ENVIRONMENTINFO_LINUXPLATFORM._serialized_start = 4240 + _ENVIRONMENTINFO_LINUXPLATFORM._serialized_end = 4483 + _ENVIRONMENTINFO_LINUXPLATFORM_LIBC._serialized_start = 4424 + _ENVIRONMENTINFO_LINUXPLATFORM_LIBC._serialized_end = 4483 + _ENVIRONMENTINFO_MACOSPLATFORM._serialized_start = 4485 + _ENVIRONMENTINFO_MACOSPLATFORM._serialized_end = 4593 + _ENVIRONMENTINFO_WINDOWSPLATFORM._serialized_start = 4596 + _ENVIRONMENTINFO_WINDOWSPLATFORM._serialized_end = 4869 + _ENVIRONMENTINFO_WINDOWSPLATFORM_CRT._serialized_start = 4782 + _ENVIRONMENTINFO_WINDOWSPLATFORM_CRT._serialized_end = 4869 + _ENVIRONMENTINFO_ARCHITECTURE._serialized_start = 4871 + _ENVIRONMENTINFO_ARCHITECTURE._serialized_end = 4963 + _WORKERCOMMAND._serialized_start = 4965 + _WORKERCOMMAND._serialized_end = 5062 + _CANCELACTIVITYCOMMAND._serialized_start = 5064 + _CANCELACTIVITYCOMMAND._serialized_end = 5107 + _WORKERCOMMANDRESULT._serialized_start = 5109 + _WORKERCOMMANDRESULT._serialized_end = 5211 + _CANCELACTIVITYRESULT._serialized_start = 5213 + _CANCELACTIVITYRESULT._serialized_end = 5235 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/worker/v1/message_pb2.pyi b/temporalio/api/worker/v1/message_pb2.pyi index f78916ed5..9a630f998 100644 --- a/temporalio/api/worker/v1/message_pb2.pyi +++ b/temporalio/api/worker/v1/message_pb2.pyi @@ -6,17 +6,19 @@ isort:skip_file import builtins import collections.abc import sys +import typing import google.protobuf.descriptor import google.protobuf.duration_pb2 import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import google.protobuf.timestamp_pb2 import temporalio.api.deployment.v1.message_pb2 import temporalio.api.enums.v1.common_pb2 -if sys.version_info >= (3, 8): +if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions @@ -217,6 +219,7 @@ class WorkerHeartbeat(google.protobuf.message.Message): CURRENT_STICKY_CACHE_SIZE_FIELD_NUMBER: builtins.int PLUGINS_FIELD_NUMBER: builtins.int DRIVERS_FIELD_NUMBER: builtins.int + ENVIRONMENT_FIELD_NUMBER: builtins.int worker_instance_key: builtins.str """Worker identifier, should be unique for the namespace. It is distinct from worker identity, which is not necessarily namespace-unique. @@ -287,6 +290,9 @@ class WorkerHeartbeat(google.protobuf.message.Message): global___StorageDriverInfo ]: """Storage drivers in use by this SDK.""" + @property + def environment(self) -> global___EnvironmentInfo: + """Information about the environment this SDK is running in.""" def __init__( self, *, @@ -316,6 +322,7 @@ class WorkerHeartbeat(google.protobuf.message.Message): current_sticky_cache_size: builtins.int = ..., plugins: collections.abc.Iterable[global___PluginInfo] | None = ..., drivers: collections.abc.Iterable[global___StorageDriverInfo] | None = ..., + environment: global___EnvironmentInfo | None = ..., ) -> None: ... def HasField( self, @@ -328,6 +335,8 @@ class WorkerHeartbeat(google.protobuf.message.Message): b"deployment_version", "elapsed_since_last_heartbeat", b"elapsed_since_last_heartbeat", + "environment", + b"environment", "heartbeat_time", b"heartbeat_time", "host_info", @@ -363,6 +372,8 @@ class WorkerHeartbeat(google.protobuf.message.Message): b"drivers", "elapsed_since_last_heartbeat", b"elapsed_since_last_heartbeat", + "environment", + b"environment", "heartbeat_time", b"heartbeat_time", "host_info", @@ -593,6 +604,446 @@ class StorageDriverInfo(google.protobuf.message.Message): global___StorageDriverInfo = StorageDriverInfo +class EnvironmentInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _Architecture: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _ArchitectureEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ + EnvironmentInfo._Architecture.ValueType + ], + builtins.type, + ): # noqa: F821 + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + ARCHITECTURE_UNSPECIFIED: EnvironmentInfo._Architecture.ValueType # 0 + ARCHITECTURE_AMD64: EnvironmentInfo._Architecture.ValueType # 1 + ARCHITECTURE_ARM64: EnvironmentInfo._Architecture.ValueType # 2 + + class Architecture(_Architecture, metaclass=_ArchitectureEnumTypeWrapper): ... + ARCHITECTURE_UNSPECIFIED: EnvironmentInfo.Architecture.ValueType # 0 + ARCHITECTURE_AMD64: EnvironmentInfo.Architecture.ValueType # 1 + ARCHITECTURE_ARM64: EnvironmentInfo.Architecture.ValueType # 2 + + class Runtime(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _RuntimeType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _RuntimeTypeEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ + EnvironmentInfo.Runtime._RuntimeType.ValueType + ], + builtins.type, + ): # noqa: F821 + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + RUNTIME_TYPE_UNSPECIFIED: ( + EnvironmentInfo.Runtime._RuntimeType.ValueType + ) # 0 + """Should never actually be set, exists to follow convention of having a default. + SDKs should just leave `runtimes` empty if none can be determined. + """ + RUNTIME_TYPE_JVM: EnvironmentInfo.Runtime._RuntimeType.ValueType # 1 + RUNTIME_TYPE_CPYTHON: EnvironmentInfo.Runtime._RuntimeType.ValueType # 2 + RUNTIME_TYPE_NODE: EnvironmentInfo.Runtime._RuntimeType.ValueType # 3 + RUNTIME_TYPE_BUN: EnvironmentInfo.Runtime._RuntimeType.ValueType # 4 + RUNTIME_TYPE_CRUBY: EnvironmentInfo.Runtime._RuntimeType.ValueType # 5 + RUNTIME_TYPE_GO: EnvironmentInfo.Runtime._RuntimeType.ValueType # 6 + RUNTIME_TYPE_DOTNET_FRAMEWORK: ( + EnvironmentInfo.Runtime._RuntimeType.ValueType + ) # 7 + RUNTIME_TYPE_DOTNET_CORE: ( + EnvironmentInfo.Runtime._RuntimeType.ValueType + ) # 8 + RUNTIME_TYPE_NATIVE: EnvironmentInfo.Runtime._RuntimeType.ValueType # 9 + RUNTIME_TYPE_ROADRUNNER: ( + EnvironmentInfo.Runtime._RuntimeType.ValueType + ) # 10 + + class RuntimeType(_RuntimeType, metaclass=_RuntimeTypeEnumTypeWrapper): ... + RUNTIME_TYPE_UNSPECIFIED: EnvironmentInfo.Runtime.RuntimeType.ValueType # 0 + """Should never actually be set, exists to follow convention of having a default. + SDKs should just leave `runtimes` empty if none can be determined. + """ + RUNTIME_TYPE_JVM: EnvironmentInfo.Runtime.RuntimeType.ValueType # 1 + RUNTIME_TYPE_CPYTHON: EnvironmentInfo.Runtime.RuntimeType.ValueType # 2 + RUNTIME_TYPE_NODE: EnvironmentInfo.Runtime.RuntimeType.ValueType # 3 + RUNTIME_TYPE_BUN: EnvironmentInfo.Runtime.RuntimeType.ValueType # 4 + RUNTIME_TYPE_CRUBY: EnvironmentInfo.Runtime.RuntimeType.ValueType # 5 + RUNTIME_TYPE_GO: EnvironmentInfo.Runtime.RuntimeType.ValueType # 6 + RUNTIME_TYPE_DOTNET_FRAMEWORK: ( + EnvironmentInfo.Runtime.RuntimeType.ValueType + ) # 7 + RUNTIME_TYPE_DOTNET_CORE: EnvironmentInfo.Runtime.RuntimeType.ValueType # 8 + RUNTIME_TYPE_NATIVE: EnvironmentInfo.Runtime.RuntimeType.ValueType # 9 + RUNTIME_TYPE_ROADRUNNER: EnvironmentInfo.Runtime.RuntimeType.ValueType # 10 + + TYPE_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + type: global___EnvironmentInfo.Runtime.RuntimeType.ValueType + """The type of the runtime.""" + version: builtins.str + """The version of the runtime, if obtainable.""" + def __init__( + self, + *, + type: global___EnvironmentInfo.Runtime.RuntimeType.ValueType = ..., + version: builtins.str = ..., + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "type", b"type", "version", b"version" + ], + ) -> None: ... + + class HostingEnvironment(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _HostingEnvironmentType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _HostingEnvironmentTypeEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ + EnvironmentInfo.HostingEnvironment._HostingEnvironmentType.ValueType + ], + builtins.type, + ): # noqa: F821 + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + HOSTING_ENVIRONMENT_TYPE_UNSPECIFIED: ( + EnvironmentInfo.HostingEnvironment._HostingEnvironmentType.ValueType + ) # 0 + """Should never actually be set, exists to follow convention of having a default. + SDKs should just leave `hosting_environments` empty if none can be determined. + """ + HOSTING_ENVIRONMENT_TYPE_DOCKER: ( + EnvironmentInfo.HostingEnvironment._HostingEnvironmentType.ValueType + ) # 1 + """Should always be in the list if we're running inside a docker container""" + HOSTING_ENVIRONMENT_TYPE_K8S: ( + EnvironmentInfo.HostingEnvironment._HostingEnvironmentType.ValueType + ) # 2 + """Should always be in the list if we're running inside any k8s environment""" + HOSTING_ENVIRONMENT_TYPE_AWS_LAMBDA: ( + EnvironmentInfo.HostingEnvironment._HostingEnvironmentType.ValueType + ) # 3 + """Detect via `AWS_LAMBDA_FUNCTION_NAME`""" + HOSTING_ENVIRONMENT_TYPE_AWS_ECS: ( + EnvironmentInfo.HostingEnvironment._HostingEnvironmentType.ValueType + ) # 4 + """Detect via `ECS_CONTAINER_METADATA_URI_V4` or `ECS_CONTAINER_METADATA_URI`""" + HOSTING_ENVIRONMENT_TYPE_GOOGLE_CLOUD_RUN: ( + EnvironmentInfo.HostingEnvironment._HostingEnvironmentType.ValueType + ) # 6 + """Detect via `K_SERVICE`""" + HOSTING_ENVIRONMENT_TYPE_GOOGLE_APP_ENGINE: ( + EnvironmentInfo.HostingEnvironment._HostingEnvironmentType.ValueType + ) # 7 + """Detect via `GAE_SERVICE`""" + HOSTING_ENVIRONMENT_TYPE_AZURE_APP_SERVICE: ( + EnvironmentInfo.HostingEnvironment._HostingEnvironmentType.ValueType + ) # 8 + """Detect via `WEBSITE_SITE_NAME`""" + HOSTING_ENVIRONMENT_TYPE_AZURE_FUNCTIONS: ( + EnvironmentInfo.HostingEnvironment._HostingEnvironmentType.ValueType + ) # 9 + """Detect via `FUNCTIONS_EXTENSION_VERSION`""" + HOSTING_ENVIRONMENT_TYPE_AZURE_CONTAINER_APPS: ( + EnvironmentInfo.HostingEnvironment._HostingEnvironmentType.ValueType + ) # 10 + """Detect via `CONTAINER_APP_NAME`""" + + class HostingEnvironmentType( + _HostingEnvironmentType, metaclass=_HostingEnvironmentTypeEnumTypeWrapper + ): + """What kind of hosting environment we're running in. This list is about what can actually be + detected reliably and is unrelated to what SDKs can actually run in. + """ + + HOSTING_ENVIRONMENT_TYPE_UNSPECIFIED: ( + EnvironmentInfo.HostingEnvironment.HostingEnvironmentType.ValueType + ) # 0 + """Should never actually be set, exists to follow convention of having a default. + SDKs should just leave `hosting_environments` empty if none can be determined. + """ + HOSTING_ENVIRONMENT_TYPE_DOCKER: ( + EnvironmentInfo.HostingEnvironment.HostingEnvironmentType.ValueType + ) # 1 + """Should always be in the list if we're running inside a docker container""" + HOSTING_ENVIRONMENT_TYPE_K8S: ( + EnvironmentInfo.HostingEnvironment.HostingEnvironmentType.ValueType + ) # 2 + """Should always be in the list if we're running inside any k8s environment""" + HOSTING_ENVIRONMENT_TYPE_AWS_LAMBDA: ( + EnvironmentInfo.HostingEnvironment.HostingEnvironmentType.ValueType + ) # 3 + """Detect via `AWS_LAMBDA_FUNCTION_NAME`""" + HOSTING_ENVIRONMENT_TYPE_AWS_ECS: ( + EnvironmentInfo.HostingEnvironment.HostingEnvironmentType.ValueType + ) # 4 + """Detect via `ECS_CONTAINER_METADATA_URI_V4` or `ECS_CONTAINER_METADATA_URI`""" + HOSTING_ENVIRONMENT_TYPE_GOOGLE_CLOUD_RUN: ( + EnvironmentInfo.HostingEnvironment.HostingEnvironmentType.ValueType + ) # 6 + """Detect via `K_SERVICE`""" + HOSTING_ENVIRONMENT_TYPE_GOOGLE_APP_ENGINE: ( + EnvironmentInfo.HostingEnvironment.HostingEnvironmentType.ValueType + ) # 7 + """Detect via `GAE_SERVICE`""" + HOSTING_ENVIRONMENT_TYPE_AZURE_APP_SERVICE: ( + EnvironmentInfo.HostingEnvironment.HostingEnvironmentType.ValueType + ) # 8 + """Detect via `WEBSITE_SITE_NAME`""" + HOSTING_ENVIRONMENT_TYPE_AZURE_FUNCTIONS: ( + EnvironmentInfo.HostingEnvironment.HostingEnvironmentType.ValueType + ) # 9 + """Detect via `FUNCTIONS_EXTENSION_VERSION`""" + HOSTING_ENVIRONMENT_TYPE_AZURE_CONTAINER_APPS: ( + EnvironmentInfo.HostingEnvironment.HostingEnvironmentType.ValueType + ) # 10 + """Detect via `CONTAINER_APP_NAME`""" + + TYPE_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + type: ( + global___EnvironmentInfo.HostingEnvironment.HostingEnvironmentType.ValueType + ) + """The type of hosting environment.""" + version: builtins.str + """The version of the hosting environment, if obtainable.""" + def __init__( + self, + *, + type: global___EnvironmentInfo.HostingEnvironment.HostingEnvironmentType.ValueType = ..., + version: builtins.str = ..., + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "type", b"type", "version", b"version" + ], + ) -> None: ... + + class Platform(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LINUX_FIELD_NUMBER: builtins.int + MACOS_FIELD_NUMBER: builtins.int + WINDOWS_FIELD_NUMBER: builtins.int + @property + def linux(self) -> global___EnvironmentInfo.LinuxPlatform: ... + @property + def macos(self) -> global___EnvironmentInfo.MacOSPlatform: ... + @property + def windows(self) -> global___EnvironmentInfo.WindowsPlatform: ... + def __init__( + self, + *, + linux: global___EnvironmentInfo.LinuxPlatform | None = ..., + macos: global___EnvironmentInfo.MacOSPlatform | None = ..., + windows: global___EnvironmentInfo.WindowsPlatform | None = ..., + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "linux", + b"linux", + "macos", + b"macos", + "variant", + b"variant", + "windows", + b"windows", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "linux", + b"linux", + "macos", + b"macos", + "variant", + b"variant", + "windows", + b"windows", + ], + ) -> None: ... + def WhichOneof( + self, oneof_group: typing_extensions.Literal["variant", b"variant"] + ) -> typing_extensions.Literal["linux", "macos", "windows"] | None: ... + + class LinuxPlatform(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _Libc: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _LibcEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ + EnvironmentInfo.LinuxPlatform._Libc.ValueType + ], + builtins.type, + ): # noqa: F821 + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + LIBC_UNSPECIFIED: EnvironmentInfo.LinuxPlatform._Libc.ValueType # 0 + LIBC_GLIBC: EnvironmentInfo.LinuxPlatform._Libc.ValueType # 1 + LIBC_MUSL: EnvironmentInfo.LinuxPlatform._Libc.ValueType # 2 + + class Libc(_Libc, metaclass=_LibcEnumTypeWrapper): ... + LIBC_UNSPECIFIED: EnvironmentInfo.LinuxPlatform.Libc.ValueType # 0 + LIBC_GLIBC: EnvironmentInfo.LinuxPlatform.Libc.ValueType # 1 + LIBC_MUSL: EnvironmentInfo.LinuxPlatform.Libc.ValueType # 2 + + VERSION_FIELD_NUMBER: builtins.int + ARCHITECTURE_FIELD_NUMBER: builtins.int + LIBC_FIELD_NUMBER: builtins.int + version: builtins.str + """The Linux kernel or distribution version, if obtainable.""" + architecture: global___EnvironmentInfo.Architecture.ValueType + """The architecture of the worker process.""" + libc: global___EnvironmentInfo.LinuxPlatform.Libc.ValueType + """The libc used by the worker process.""" + def __init__( + self, + *, + version: builtins.str = ..., + architecture: global___EnvironmentInfo.Architecture.ValueType = ..., + libc: global___EnvironmentInfo.LinuxPlatform.Libc.ValueType = ..., + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "architecture", b"architecture", "libc", b"libc", "version", b"version" + ], + ) -> None: ... + + class MacOSPlatform(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VERSION_FIELD_NUMBER: builtins.int + ARCHITECTURE_FIELD_NUMBER: builtins.int + version: builtins.str + """The macOS version, if obtainable.""" + architecture: global___EnvironmentInfo.Architecture.ValueType + """The architecture of the worker process.""" + def __init__( + self, + *, + version: builtins.str = ..., + architecture: global___EnvironmentInfo.Architecture.ValueType = ..., + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "architecture", b"architecture", "version", b"version" + ], + ) -> None: ... + + class WindowsPlatform(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _Crt: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _CrtEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ + EnvironmentInfo.WindowsPlatform._Crt.ValueType + ], + builtins.type, + ): # noqa: F821 + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + CRT_UNSPECIFIED: EnvironmentInfo.WindowsPlatform._Crt.ValueType # 0 + CRT_UCRT: EnvironmentInfo.WindowsPlatform._Crt.ValueType # 1 + CRT_MSVCRT: EnvironmentInfo.WindowsPlatform._Crt.ValueType # 2 + CRT_MINGW: EnvironmentInfo.WindowsPlatform._Crt.ValueType # 3 + CRT_CYGWIN: EnvironmentInfo.WindowsPlatform._Crt.ValueType # 4 + + class Crt(_Crt, metaclass=_CrtEnumTypeWrapper): ... + CRT_UNSPECIFIED: EnvironmentInfo.WindowsPlatform.Crt.ValueType # 0 + CRT_UCRT: EnvironmentInfo.WindowsPlatform.Crt.ValueType # 1 + CRT_MSVCRT: EnvironmentInfo.WindowsPlatform.Crt.ValueType # 2 + CRT_MINGW: EnvironmentInfo.WindowsPlatform.Crt.ValueType # 3 + CRT_CYGWIN: EnvironmentInfo.WindowsPlatform.Crt.ValueType # 4 + + VERSION_FIELD_NUMBER: builtins.int + ARCHITECTURE_FIELD_NUMBER: builtins.int + CRT_FIELD_NUMBER: builtins.int + version: builtins.str + """The Windows version, if obtainable.""" + architecture: global___EnvironmentInfo.Architecture.ValueType + """The architecture of the worker process.""" + crt: global___EnvironmentInfo.WindowsPlatform.Crt.ValueType + """The C runtime used by the worker process, if obtainable.""" + def __init__( + self, + *, + version: builtins.str = ..., + architecture: global___EnvironmentInfo.Architecture.ValueType = ..., + crt: global___EnvironmentInfo.WindowsPlatform.Crt.ValueType = ..., + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "architecture", b"architecture", "crt", b"crt", "version", b"version" + ], + ) -> None: ... + + RUNTIMES_FIELD_NUMBER: builtins.int + HOSTING_ENVIRONMENTS_FIELD_NUMBER: builtins.int + PLATFORM_FIELD_NUMBER: builtins.int + @property + def runtimes( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ + global___EnvironmentInfo.Runtime + ]: + """The runtime(s) the SDK is operating in.""" + @property + def hosting_environments( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ + global___EnvironmentInfo.HostingEnvironment + ]: + """The hosting environment(s) the SDK is operating in. Repeated to allow for layering (ex: Docker inside k8s).""" + @property + def platform(self) -> global___EnvironmentInfo.Platform: + """The platform the SDK is operating on.""" + def __init__( + self, + *, + runtimes: collections.abc.Iterable[global___EnvironmentInfo.Runtime] + | None = ..., + hosting_environments: collections.abc.Iterable[ + global___EnvironmentInfo.HostingEnvironment + ] + | None = ..., + platform: global___EnvironmentInfo.Platform | None = ..., + ) -> None: ... + def HasField( + self, field_name: typing_extensions.Literal["platform", b"platform"] + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "hosting_environments", + b"hosting_environments", + "platform", + b"platform", + "runtimes", + b"runtimes", + ], + ) -> None: ... + +global___EnvironmentInfo = EnvironmentInfo + class WorkerCommand(google.protobuf.message.Message): """A command sent from the server to a worker.""" diff --git a/temporalio/api/workflow/v1/__init__.py b/temporalio/api/workflow/v1/__init__.py index 89878d551..ae647ab67 100644 --- a/temporalio/api/workflow/v1/__init__.py +++ b/temporalio/api/workflow/v1/__init__.py @@ -13,7 +13,6 @@ RequestIdInfo, ResetPointInfo, ResetPoints, - TimeSkippingConfig, VersioningOverride, WorkflowExecutionConfig, WorkflowExecutionExtendedInfo, @@ -38,7 +37,6 @@ "RequestIdInfo", "ResetPointInfo", "ResetPoints", - "TimeSkippingConfig", "VersioningOverride", "WorkflowExecutionConfig", "WorkflowExecutionExtendedInfo", diff --git a/temporalio/api/workflow/v1/message_pb2.py b/temporalio/api/workflow/v1/message_pb2.py index 70d909746..032f2b2b5 100644 --- a/temporalio/api/workflow/v1/message_pb2.py +++ b/temporalio/api/workflow/v1/message_pb2.py @@ -48,7 +48,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n&temporal/api/workflow/v1/message.proto\x12\x18temporal.api.workflow.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a google/protobuf/field_mask.proto\x1a&temporal/api/activity/v1/message.proto\x1a"temporal/api/enums/v1/common.proto\x1a&temporal/api/enums/v1/event_type.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a$temporal/api/common/v1/message.proto\x1a(temporal/api/deployment/v1/message.proto\x1a%temporal/api/failure/v1/message.proto\x1a\'temporal/api/taskqueue/v1/message.proto\x1a\'temporal/api/sdk/v1/user_metadata.proto"\xf0\t\n\x15WorkflowExecutionInfo\x12<\n\texecution\x18\x01 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x32\n\x04type\x18\x02 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12.\n\nstart_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nclose_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x06status\x18\x05 \x01(\x0e\x32..temporal.api.enums.v1.WorkflowExecutionStatus\x12\x16\n\x0ehistory_length\x18\x06 \x01(\x03\x12\x1b\n\x13parent_namespace_id\x18\x07 \x01(\t\x12\x43\n\x10parent_execution\x18\x08 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x32\n\x0e\x65xecution_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12*\n\x04memo\x18\n \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x0b \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12@\n\x11\x61uto_reset_points\x18\x0c \x01(\x0b\x32%.temporal.api.workflow.v1.ResetPoints\x12\x12\n\ntask_queue\x18\r \x01(\t\x12\x1e\n\x16state_transition_count\x18\x0e \x01(\x03\x12\x1a\n\x12history_size_bytes\x18\x0f \x01(\x03\x12X\n most_recent_worker_version_stamp\x18\x10 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12\x35\n\x12\x65xecution_duration\x18\x11 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x41\n\x0eroot_execution\x18\x12 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x1d\n\x11\x61ssigned_build_id\x18\x13 \x01(\tB\x02\x18\x01\x12\x1e\n\x12inherited_build_id\x18\x14 \x01(\tB\x02\x18\x01\x12\x14\n\x0c\x66irst_run_id\x18\x15 \x01(\t\x12R\n\x0fversioning_info\x18\x16 \x01(\x0b\x32\x39.temporal.api.workflow.v1.WorkflowExecutionVersioningInfo\x12\x1e\n\x16worker_deployment_name\x18\x17 \x01(\t\x12\x32\n\x08priority\x18\x18 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12#\n\x1b\x65xternal_payload_size_bytes\x18\x19 \x01(\x03\x12\x1e\n\x16\x65xternal_payload_count\x18\x1a \x01(\x03"\xc6\x04\n\x1dWorkflowExecutionExtendedInfo\x12=\n\x19\x65xecution_expiration_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x37\n\x13run_expiration_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x18\n\x10\x63\x61ncel_requested\x18\x03 \x01(\x08\x12\x33\n\x0flast_reset_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x37\n\x13original_start_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x14\n\x0creset_run_id\x18\x06 \x01(\t\x12\x65\n\x10request_id_infos\x18\x07 \x03(\x0b\x32K.temporal.api.workflow.v1.WorkflowExecutionExtendedInfo.RequestIdInfosEntry\x12H\n\npause_info\x18\x08 \x01(\x0b\x32\x34.temporal.api.workflow.v1.WorkflowExecutionPauseInfo\x1a^\n\x13RequestIdInfosEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x36\n\x05value\x18\x02 \x01(\x0b\x32\'.temporal.api.workflow.v1.RequestIdInfo:\x02\x38\x01"\xfb\x04\n\x1fWorkflowExecutionVersioningInfo\x12;\n\x08\x62\x65havior\x18\x01 \x01(\x0e\x32).temporal.api.enums.v1.VersioningBehavior\x12>\n\ndeployment\x18\x02 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12\x13\n\x07version\x18\x05 \x01(\tB\x02\x18\x01\x12O\n\x12\x64\x65ployment_version\x18\x07 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12I\n\x13versioning_override\x18\x03 \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12Q\n\x15\x64\x65ployment_transition\x18\x04 \x01(\x0b\x32..temporal.api.workflow.v1.DeploymentTransitionB\x02\x18\x01\x12Q\n\x12version_transition\x18\x06 \x01(\x0b\x32\x35.temporal.api.workflow.v1.DeploymentVersionTransition\x12\x17\n\x0frevision_number\x18\x08 \x01(\x03\x12k\n+continue_as_new_initial_versioning_behavior\x18\t \x01(\x0e\x32\x36.temporal.api.enums.v1.ContinueAsNewVersioningBehavior"R\n\x14\x44\x65ploymentTransition\x12:\n\ndeployment\x18\x01 \x01(\x0b\x32&.temporal.api.deployment.v1.Deployment"\x83\x01\n\x1b\x44\x65ploymentVersionTransition\x12\x13\n\x07version\x18\x01 \x01(\tB\x02\x18\x01\x12O\n\x12\x64\x65ployment_version\x18\x02 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion"\xc7\x02\n\x17WorkflowExecutionConfig\x12\x38\n\ntask_queue\x18\x01 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12=\n\x1aworkflow_execution_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12@\n\x1d\x64\x65\x66\x61ult_workflow_task_timeout\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\ruser_metadata\x18\x05 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata"\xbd\r\n\x13PendingActivityInfo\x12\x13\n\x0b\x61\x63tivity_id\x18\x01 \x01(\t\x12;\n\ractivity_type\x18\x02 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12:\n\x05state\x18\x03 \x01(\x0e\x32+.temporal.api.enums.v1.PendingActivityState\x12;\n\x11heartbeat_details\x18\x04 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x37\n\x13last_heartbeat_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x35\n\x11last_started_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07\x61ttempt\x18\x07 \x01(\x05\x12\x18\n\x10maximum_attempts\x18\x08 \x01(\x05\x12\x32\n\x0escheduled_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x33\n\x0f\x65xpiration_time\x18\n \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x36\n\x0clast_failure\x18\x0b \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x1c\n\x14last_worker_identity\x18\x0c \x01(\t\x12;\n\x15use_workflow_build_id\x18\r \x01(\x0b\x32\x16.google.protobuf.EmptyB\x02\x18\x01H\x00\x12\x32\n$last_independently_assigned_build_id\x18\x0e \x01(\tB\x02\x18\x01H\x00\x12Q\n\x19last_worker_version_stamp\x18\x0f \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12\x39\n\x16\x63urrent_retry_interval\x18\x10 \x01(\x0b\x32\x19.google.protobuf.Duration\x12>\n\x1alast_attempt_complete_time\x18\x11 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x1anext_attempt_schedule_time\x18\x12 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06paused\x18\x13 \x01(\x08\x12\x43\n\x0flast_deployment\x18\x14 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12*\n\x1elast_worker_deployment_version\x18\x15 \x01(\tB\x02\x18\x01\x12T\n\x17last_deployment_version\x18\x19 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x32\n\x08priority\x18\x16 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12K\n\npause_info\x18\x17 \x01(\x0b\x32\x37.temporal.api.workflow.v1.PendingActivityInfo.PauseInfo\x12\x43\n\x10\x61\x63tivity_options\x18\x18 \x01(\x0b\x32).temporal.api.activity.v1.ActivityOptions\x1a\xcf\x02\n\tPauseInfo\x12.\n\npause_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12P\n\x06manual\x18\x02 \x01(\x0b\x32>.temporal.api.workflow.v1.PendingActivityInfo.PauseInfo.ManualH\x00\x12L\n\x04rule\x18\x04 \x01(\x0b\x32<.temporal.api.workflow.v1.PendingActivityInfo.PauseInfo.RuleH\x00\x1a*\n\x06Manual\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t\x1a\x39\n\x04Rule\x12\x0f\n\x07rule_id\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\tB\x0b\n\tpaused_byB\x13\n\x11\x61ssigned_build_id"\xb9\x01\n\x19PendingChildExecutionInfo\x12\x13\n\x0bworkflow_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12\x1a\n\x12workflow_type_name\x18\x03 \x01(\t\x12\x14\n\x0cinitiated_id\x18\x04 \x01(\x03\x12\x45\n\x13parent_close_policy\x18\x05 \x01(\x0e\x32(.temporal.api.enums.v1.ParentClosePolicy"\x8d\x02\n\x17PendingWorkflowTaskInfo\x12>\n\x05state\x18\x01 \x01(\x0e\x32/.temporal.api.enums.v1.PendingWorkflowTaskState\x12\x32\n\x0escheduled_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12;\n\x17original_scheduled_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x0cstarted_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07\x61ttempt\x18\x05 \x01(\x05"G\n\x0bResetPoints\x12\x38\n\x06points\x18\x01 \x03(\x0b\x32(.temporal.api.workflow.v1.ResetPointInfo"\xef\x01\n\x0eResetPointInfo\x12\x10\n\x08\x62uild_id\x18\x07 \x01(\t\x12\x1b\n\x0f\x62inary_checksum\x18\x01 \x01(\tB\x02\x18\x01\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12(\n first_workflow_task_completed_id\x18\x03 \x01(\x03\x12/\n\x0b\x63reate_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12/\n\x0b\x65xpire_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x12\n\nresettable\x18\x06 \x01(\x08"\x85\x07\n\x18NewWorkflowExecutionInfo\x12\x13\n\x0bworkflow_id\x18\x01 \x01(\t\x12;\n\rworkflow_type\x18\x02 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x03 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x04 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12=\n\x1aworkflow_execution_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12N\n\x18workflow_id_reuse_policy\x18\x08 \x01(\x0e\x32,.temporal.api.enums.v1.WorkflowIdReusePolicy\x12\x39\n\x0cretry_policy\x18\t \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x15\n\rcron_schedule\x18\n \x01(\t\x12*\n\x04memo\x18\x0b \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x0c \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12.\n\x06header\x18\r \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x38\n\ruser_metadata\x18\x0e \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12I\n\x13versioning_override\x18\x0f \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12\x32\n\x08priority\x18\x10 \x01(\x0b\x32 .temporal.api.common.v1.Priority"\x82\x06\n\x0c\x43\x61llbackInfo\x12\x32\n\x08\x63\x61llback\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Callback\x12?\n\x07trigger\x18\x02 \x01(\x0b\x32..temporal.api.workflow.v1.CallbackInfo.Trigger\x12\x35\n\x11registration_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x33\n\x05state\x18\x04 \x01(\x0e\x32$.temporal.api.enums.v1.CallbackState\x12\x0f\n\x07\x61ttempt\x18\x05 \x01(\x05\x12>\n\x1alast_attempt_complete_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x14last_attempt_failure\x18\x07 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12>\n\x1anext_attempt_schedule_time\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x16\n\x0e\x62locked_reason\x18\t \x01(\t\x1a\x10\n\x0eWorkflowClosed\x1a\x35\n UpdateWorkflowExecutionCompleted\x12\x11\n\tupdate_id\x18\x01 \x01(\t\x1a\xde\x01\n\x07Trigger\x12P\n\x0fworkflow_closed\x18\x01 \x01(\x0b\x32\x35.temporal.api.workflow.v1.CallbackInfo.WorkflowClosedH\x00\x12v\n#update_workflow_execution_completed\x18\x02 \x01(\x0b\x32G.temporal.api.workflow.v1.CallbackInfo.UpdateWorkflowExecutionCompletedH\x00\x42\t\n\x07variant"\x8b\x06\n\x19PendingNexusOperationInfo\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\x11\n\toperation\x18\x03 \x01(\t\x12\x18\n\x0coperation_id\x18\x04 \x01(\tB\x02\x18\x01\x12<\n\x19schedule_to_close_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x32\n\x0escheduled_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12@\n\x05state\x18\x07 \x01(\x0e\x32\x31.temporal.api.enums.v1.PendingNexusOperationState\x12\x0f\n\x07\x61ttempt\x18\x08 \x01(\x05\x12>\n\x1alast_attempt_complete_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x14last_attempt_failure\x18\n \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12>\n\x1anext_attempt_schedule_time\x18\x0b \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12S\n\x11\x63\x61ncellation_info\x18\x0c \x01(\x0b\x32\x38.temporal.api.workflow.v1.NexusOperationCancellationInfo\x12\x1a\n\x12scheduled_event_id\x18\r \x01(\x03\x12\x16\n\x0e\x62locked_reason\x18\x0e \x01(\t\x12\x17\n\x0foperation_token\x18\x0f \x01(\t\x12<\n\x19schedule_to_start_timeout\x18\x10 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\x11 \x01(\x0b\x32\x19.google.protobuf.Duration"\x84\x03\n\x1eNexusOperationCancellationInfo\x12\x32\n\x0erequested_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x45\n\x05state\x18\x02 \x01(\x0e\x32\x36.temporal.api.enums.v1.NexusOperationCancellationState\x12\x0f\n\x07\x61ttempt\x18\x03 \x01(\x05\x12>\n\x1alast_attempt_complete_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x14last_attempt_failure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12>\n\x1anext_attempt_schedule_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x16\n\x0e\x62locked_reason\x18\x07 \x01(\t"\xe5\x01\n\x18WorkflowExecutionOptions\x12I\n\x13versioning_override\x18\x01 \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12\x32\n\x08priority\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12J\n\x14time_skipping_config\x18\x03 \x01(\x0b\x32,.temporal.api.workflow.v1.TimeSkippingConfig"\xd6\x01\n\x12TimeSkippingConfig\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x39\n\x14max_skipped_duration\x18\x04 \x01(\x0b\x32\x19.google.protobuf.DurationH\x00\x12\x39\n\x14max_elapsed_duration\x18\x05 \x01(\x0b\x32\x19.google.protobuf.DurationH\x00\x42\x07\n\x05\x62oundJ\x04\x08\x02\x10\x03J\x04\x08\x06\x10\x07R\x13\x64isable_propagationR\x0fmax_target_time"\xbd\x04\n\x12VersioningOverride\x12M\n\x06pinned\x18\x03 \x01(\x0b\x32;.temporal.api.workflow.v1.VersioningOverride.PinnedOverrideH\x00\x12\x16\n\x0c\x61uto_upgrade\x18\x04 \x01(\x08H\x00\x12?\n\x08\x62\x65havior\x18\x01 \x01(\x0e\x32).temporal.api.enums.v1.VersioningBehaviorB\x02\x18\x01\x12>\n\ndeployment\x18\x02 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12\x1a\n\x0epinned_version\x18\t \x01(\tB\x02\x18\x01\x1a\xad\x01\n\x0ePinnedOverride\x12U\n\x08\x62\x65havior\x18\x01 \x01(\x0e\x32\x43.temporal.api.workflow.v1.VersioningOverride.PinnedOverrideBehavior\x12\x44\n\x07version\x18\x02 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion"g\n\x16PinnedOverrideBehavior\x12(\n$PINNED_OVERRIDE_BEHAVIOR_UNSPECIFIED\x10\x00\x12#\n\x1fPINNED_OVERRIDE_BEHAVIOR_PINNED\x10\x01\x42\n\n\x08override"i\n\x11OnConflictOptions\x12\x19\n\x11\x61ttach_request_id\x18\x01 \x01(\x08\x12#\n\x1b\x61ttach_completion_callbacks\x18\x02 \x01(\x08\x12\x14\n\x0c\x61ttach_links\x18\x03 \x01(\x08"i\n\rRequestIdInfo\x12\x34\n\nevent_type\x18\x01 \x01(\x0e\x32 .temporal.api.enums.v1.EventType\x12\x10\n\x08\x65vent_id\x18\x02 \x01(\x03\x12\x10\n\x08\x62uffered\x18\x03 \x01(\x08"\xb7\x04\n\x12PostResetOperation\x12V\n\x0fsignal_workflow\x18\x01 \x01(\x0b\x32;.temporal.api.workflow.v1.PostResetOperation.SignalWorkflowH\x00\x12\x65\n\x17update_workflow_options\x18\x02 \x01(\x0b\x32\x42.temporal.api.workflow.v1.PostResetOperation.UpdateWorkflowOptionsH\x00\x1a\xb3\x01\n\x0eSignalWorkflow\x12\x13\n\x0bsignal_name\x18\x01 \x01(\t\x12/\n\x05input\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12.\n\x06header\x18\x03 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12+\n\x05links\x18\x04 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x1a\xa0\x01\n\x15UpdateWorkflowOptions\x12V\n\x1aworkflow_execution_options\x18\x01 \x01(\x0b\x32\x32.temporal.api.workflow.v1.WorkflowExecutionOptions\x12/\n\x0bupdate_mask\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.FieldMaskB\t\n\x07variant"o\n\x1aWorkflowExecutionPauseInfo\x12\x10\n\x08identity\x18\x01 \x01(\t\x12/\n\x0bpaused_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06reason\x18\x03 \x01(\tB\x93\x01\n\x1bio.temporal.api.workflow.v1B\x0cMessageProtoP\x01Z\'go.temporal.io/api/workflow/v1;workflow\xaa\x02\x1aTemporalio.Api.Workflow.V1\xea\x02\x1dTemporalio::Api::Workflow::V1b\x06proto3' + b'\n&temporal/api/workflow/v1/message.proto\x12\x18temporal.api.workflow.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a google/protobuf/field_mask.proto\x1a&temporal/api/activity/v1/message.proto\x1a"temporal/api/enums/v1/common.proto\x1a&temporal/api/enums/v1/event_type.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a$temporal/api/common/v1/message.proto\x1a(temporal/api/deployment/v1/message.proto\x1a%temporal/api/failure/v1/message.proto\x1a\'temporal/api/taskqueue/v1/message.proto\x1a\'temporal/api/sdk/v1/user_metadata.proto"\xf0\t\n\x15WorkflowExecutionInfo\x12<\n\texecution\x18\x01 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x32\n\x04type\x18\x02 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12.\n\nstart_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nclose_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x06status\x18\x05 \x01(\x0e\x32..temporal.api.enums.v1.WorkflowExecutionStatus\x12\x16\n\x0ehistory_length\x18\x06 \x01(\x03\x12\x1b\n\x13parent_namespace_id\x18\x07 \x01(\t\x12\x43\n\x10parent_execution\x18\x08 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x32\n\x0e\x65xecution_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12*\n\x04memo\x18\n \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x0b \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12@\n\x11\x61uto_reset_points\x18\x0c \x01(\x0b\x32%.temporal.api.workflow.v1.ResetPoints\x12\x12\n\ntask_queue\x18\r \x01(\t\x12\x1e\n\x16state_transition_count\x18\x0e \x01(\x03\x12\x1a\n\x12history_size_bytes\x18\x0f \x01(\x03\x12X\n most_recent_worker_version_stamp\x18\x10 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12\x35\n\x12\x65xecution_duration\x18\x11 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x41\n\x0eroot_execution\x18\x12 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x1d\n\x11\x61ssigned_build_id\x18\x13 \x01(\tB\x02\x18\x01\x12\x1e\n\x12inherited_build_id\x18\x14 \x01(\tB\x02\x18\x01\x12\x14\n\x0c\x66irst_run_id\x18\x15 \x01(\t\x12R\n\x0fversioning_info\x18\x16 \x01(\x0b\x32\x39.temporal.api.workflow.v1.WorkflowExecutionVersioningInfo\x12\x1e\n\x16worker_deployment_name\x18\x17 \x01(\t\x12\x32\n\x08priority\x18\x18 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12#\n\x1b\x65xternal_payload_size_bytes\x18\x19 \x01(\x03\x12\x1e\n\x16\x65xternal_payload_count\x18\x1a \x01(\x03"\x8c\x05\n\x1dWorkflowExecutionExtendedInfo\x12=\n\x19\x65xecution_expiration_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x37\n\x13run_expiration_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x18\n\x10\x63\x61ncel_requested\x18\x03 \x01(\x08\x12\x33\n\x0flast_reset_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x37\n\x13original_start_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x14\n\x0creset_run_id\x18\x06 \x01(\t\x12\x65\n\x10request_id_infos\x18\x07 \x03(\x0b\x32K.temporal.api.workflow.v1.WorkflowExecutionExtendedInfo.RequestIdInfosEntry\x12H\n\npause_info\x18\x08 \x01(\x0b\x32\x34.temporal.api.workflow.v1.WorkflowExecutionPauseInfo\x12\x44\n\x12time_skipping_info\x18\t \x01(\x0b\x32(.temporal.api.common.v1.TimeSkippingInfo\x1a^\n\x13RequestIdInfosEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x36\n\x05value\x18\x02 \x01(\x0b\x32\'.temporal.api.workflow.v1.RequestIdInfo:\x02\x38\x01"\xfb\x04\n\x1fWorkflowExecutionVersioningInfo\x12;\n\x08\x62\x65havior\x18\x01 \x01(\x0e\x32).temporal.api.enums.v1.VersioningBehavior\x12>\n\ndeployment\x18\x02 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12\x13\n\x07version\x18\x05 \x01(\tB\x02\x18\x01\x12O\n\x12\x64\x65ployment_version\x18\x07 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12I\n\x13versioning_override\x18\x03 \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12Q\n\x15\x64\x65ployment_transition\x18\x04 \x01(\x0b\x32..temporal.api.workflow.v1.DeploymentTransitionB\x02\x18\x01\x12Q\n\x12version_transition\x18\x06 \x01(\x0b\x32\x35.temporal.api.workflow.v1.DeploymentVersionTransition\x12\x17\n\x0frevision_number\x18\x08 \x01(\x03\x12k\n+continue_as_new_initial_versioning_behavior\x18\t \x01(\x0e\x32\x36.temporal.api.enums.v1.ContinueAsNewVersioningBehavior"R\n\x14\x44\x65ploymentTransition\x12:\n\ndeployment\x18\x01 \x01(\x0b\x32&.temporal.api.deployment.v1.Deployment"\x83\x01\n\x1b\x44\x65ploymentVersionTransition\x12\x13\n\x07version\x18\x01 \x01(\tB\x02\x18\x01\x12O\n\x12\x64\x65ployment_version\x18\x02 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion"\xc7\x02\n\x17WorkflowExecutionConfig\x12\x38\n\ntask_queue\x18\x01 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12=\n\x1aworkflow_execution_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12@\n\x1d\x64\x65\x66\x61ult_workflow_task_timeout\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\ruser_metadata\x18\x05 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata"\xbd\r\n\x13PendingActivityInfo\x12\x13\n\x0b\x61\x63tivity_id\x18\x01 \x01(\t\x12;\n\ractivity_type\x18\x02 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12:\n\x05state\x18\x03 \x01(\x0e\x32+.temporal.api.enums.v1.PendingActivityState\x12;\n\x11heartbeat_details\x18\x04 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x37\n\x13last_heartbeat_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x35\n\x11last_started_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07\x61ttempt\x18\x07 \x01(\x05\x12\x18\n\x10maximum_attempts\x18\x08 \x01(\x05\x12\x32\n\x0escheduled_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x33\n\x0f\x65xpiration_time\x18\n \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x36\n\x0clast_failure\x18\x0b \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x1c\n\x14last_worker_identity\x18\x0c \x01(\t\x12;\n\x15use_workflow_build_id\x18\r \x01(\x0b\x32\x16.google.protobuf.EmptyB\x02\x18\x01H\x00\x12\x32\n$last_independently_assigned_build_id\x18\x0e \x01(\tB\x02\x18\x01H\x00\x12Q\n\x19last_worker_version_stamp\x18\x0f \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12\x39\n\x16\x63urrent_retry_interval\x18\x10 \x01(\x0b\x32\x19.google.protobuf.Duration\x12>\n\x1alast_attempt_complete_time\x18\x11 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x1anext_attempt_schedule_time\x18\x12 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06paused\x18\x13 \x01(\x08\x12\x43\n\x0flast_deployment\x18\x14 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12*\n\x1elast_worker_deployment_version\x18\x15 \x01(\tB\x02\x18\x01\x12T\n\x17last_deployment_version\x18\x19 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x32\n\x08priority\x18\x16 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12K\n\npause_info\x18\x17 \x01(\x0b\x32\x37.temporal.api.workflow.v1.PendingActivityInfo.PauseInfo\x12\x43\n\x10\x61\x63tivity_options\x18\x18 \x01(\x0b\x32).temporal.api.activity.v1.ActivityOptions\x1a\xcf\x02\n\tPauseInfo\x12.\n\npause_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12P\n\x06manual\x18\x02 \x01(\x0b\x32>.temporal.api.workflow.v1.PendingActivityInfo.PauseInfo.ManualH\x00\x12L\n\x04rule\x18\x04 \x01(\x0b\x32<.temporal.api.workflow.v1.PendingActivityInfo.PauseInfo.RuleH\x00\x1a*\n\x06Manual\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t\x1a\x39\n\x04Rule\x12\x0f\n\x07rule_id\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\tB\x0b\n\tpaused_byB\x13\n\x11\x61ssigned_build_id"\xb9\x01\n\x19PendingChildExecutionInfo\x12\x13\n\x0bworkflow_id\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12\x1a\n\x12workflow_type_name\x18\x03 \x01(\t\x12\x14\n\x0cinitiated_id\x18\x04 \x01(\x03\x12\x45\n\x13parent_close_policy\x18\x05 \x01(\x0e\x32(.temporal.api.enums.v1.ParentClosePolicy"\x8d\x02\n\x17PendingWorkflowTaskInfo\x12>\n\x05state\x18\x01 \x01(\x0e\x32/.temporal.api.enums.v1.PendingWorkflowTaskState\x12\x32\n\x0escheduled_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12;\n\x17original_scheduled_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x0cstarted_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07\x61ttempt\x18\x05 \x01(\x05"G\n\x0bResetPoints\x12\x38\n\x06points\x18\x01 \x03(\x0b\x32(.temporal.api.workflow.v1.ResetPointInfo"\xef\x01\n\x0eResetPointInfo\x12\x10\n\x08\x62uild_id\x18\x07 \x01(\t\x12\x1b\n\x0f\x62inary_checksum\x18\x01 \x01(\tB\x02\x18\x01\x12\x0e\n\x06run_id\x18\x02 \x01(\t\x12(\n first_workflow_task_completed_id\x18\x03 \x01(\x03\x12/\n\x0b\x63reate_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12/\n\x0b\x65xpire_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x12\n\nresettable\x18\x06 \x01(\x08"\x85\x07\n\x18NewWorkflowExecutionInfo\x12\x13\n\x0bworkflow_id\x18\x01 \x01(\t\x12;\n\rworkflow_type\x18\x02 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x03 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x04 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12=\n\x1aworkflow_execution_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12N\n\x18workflow_id_reuse_policy\x18\x08 \x01(\x0e\x32,.temporal.api.enums.v1.WorkflowIdReusePolicy\x12\x39\n\x0cretry_policy\x18\t \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x15\n\rcron_schedule\x18\n \x01(\t\x12*\n\x04memo\x18\x0b \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x0c \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12.\n\x06header\x18\r \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x38\n\ruser_metadata\x18\x0e \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12I\n\x13versioning_override\x18\x0f \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12\x32\n\x08priority\x18\x10 \x01(\x0b\x32 .temporal.api.common.v1.Priority"\x82\x06\n\x0c\x43\x61llbackInfo\x12\x32\n\x08\x63\x61llback\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Callback\x12?\n\x07trigger\x18\x02 \x01(\x0b\x32..temporal.api.workflow.v1.CallbackInfo.Trigger\x12\x35\n\x11registration_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x33\n\x05state\x18\x04 \x01(\x0e\x32$.temporal.api.enums.v1.CallbackState\x12\x0f\n\x07\x61ttempt\x18\x05 \x01(\x05\x12>\n\x1alast_attempt_complete_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x14last_attempt_failure\x18\x07 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12>\n\x1anext_attempt_schedule_time\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x16\n\x0e\x62locked_reason\x18\t \x01(\t\x1a\x10\n\x0eWorkflowClosed\x1a\x35\n UpdateWorkflowExecutionCompleted\x12\x11\n\tupdate_id\x18\x01 \x01(\t\x1a\xde\x01\n\x07Trigger\x12P\n\x0fworkflow_closed\x18\x01 \x01(\x0b\x32\x35.temporal.api.workflow.v1.CallbackInfo.WorkflowClosedH\x00\x12v\n#update_workflow_execution_completed\x18\x02 \x01(\x0b\x32G.temporal.api.workflow.v1.CallbackInfo.UpdateWorkflowExecutionCompletedH\x00\x42\t\n\x07variant"\x8b\x06\n\x19PendingNexusOperationInfo\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0f\n\x07service\x18\x02 \x01(\t\x12\x11\n\toperation\x18\x03 \x01(\t\x12\x18\n\x0coperation_id\x18\x04 \x01(\tB\x02\x18\x01\x12<\n\x19schedule_to_close_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x32\n\x0escheduled_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12@\n\x05state\x18\x07 \x01(\x0e\x32\x31.temporal.api.enums.v1.PendingNexusOperationState\x12\x0f\n\x07\x61ttempt\x18\x08 \x01(\x05\x12>\n\x1alast_attempt_complete_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x14last_attempt_failure\x18\n \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12>\n\x1anext_attempt_schedule_time\x18\x0b \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12S\n\x11\x63\x61ncellation_info\x18\x0c \x01(\x0b\x32\x38.temporal.api.workflow.v1.NexusOperationCancellationInfo\x12\x1a\n\x12scheduled_event_id\x18\r \x01(\x03\x12\x16\n\x0e\x62locked_reason\x18\x0e \x01(\t\x12\x17\n\x0foperation_token\x18\x0f \x01(\t\x12<\n\x19schedule_to_start_timeout\x18\x10 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\x11 \x01(\x0b\x32\x19.google.protobuf.Duration"\x84\x03\n\x1eNexusOperationCancellationInfo\x12\x32\n\x0erequested_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x45\n\x05state\x18\x02 \x01(\x0e\x32\x36.temporal.api.enums.v1.NexusOperationCancellationState\x12\x0f\n\x07\x61ttempt\x18\x03 \x01(\x05\x12>\n\x1alast_attempt_complete_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12>\n\x14last_attempt_failure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12>\n\x1anext_attempt_schedule_time\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x16\n\x0e\x62locked_reason\x18\x07 \x01(\t"\xe3\x01\n\x18WorkflowExecutionOptions\x12I\n\x13versioning_override\x18\x01 \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12\x32\n\x08priority\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12H\n\x14time_skipping_config\x18\x03 \x01(\x0b\x32*.temporal.api.common.v1.TimeSkippingConfig"\xfa\x05\n\x12VersioningOverride\x12M\n\x06pinned\x18\x03 \x01(\x0b\x32;.temporal.api.workflow.v1.VersioningOverride.PinnedOverrideH\x00\x12\x16\n\x0c\x61uto_upgrade\x18\x04 \x01(\x08H\x00\x12P\n\x08one_time\x18\x05 \x01(\x0b\x32<.temporal.api.workflow.v1.VersioningOverride.OneTimeOverrideH\x00\x12?\n\x08\x62\x65havior\x18\x01 \x01(\x0e\x32).temporal.api.enums.v1.VersioningBehaviorB\x02\x18\x01\x12>\n\ndeployment\x18\x02 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12\x1a\n\x0epinned_version\x18\t \x01(\tB\x02\x18\x01\x1a\xad\x01\n\x0ePinnedOverride\x12U\n\x08\x62\x65havior\x18\x01 \x01(\x0e\x32\x43.temporal.api.workflow.v1.VersioningOverride.PinnedOverrideBehavior\x12\x44\n\x07version\x18\x02 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x1ai\n\x0fOneTimeOverride\x12V\n\x19target_deployment_version\x18\x01 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion"g\n\x16PinnedOverrideBehavior\x12(\n$PINNED_OVERRIDE_BEHAVIOR_UNSPECIFIED\x10\x00\x12#\n\x1fPINNED_OVERRIDE_BEHAVIOR_PINNED\x10\x01\x42\n\n\x08override"i\n\x11OnConflictOptions\x12\x19\n\x11\x61ttach_request_id\x18\x01 \x01(\x08\x12#\n\x1b\x61ttach_completion_callbacks\x18\x02 \x01(\x08\x12\x14\n\x0c\x61ttach_links\x18\x03 \x01(\x08"i\n\rRequestIdInfo\x12\x34\n\nevent_type\x18\x01 \x01(\x0e\x32 .temporal.api.enums.v1.EventType\x12\x10\n\x08\x65vent_id\x18\x02 \x01(\x03\x12\x10\n\x08\x62uffered\x18\x03 \x01(\x08"\xb7\x04\n\x12PostResetOperation\x12V\n\x0fsignal_workflow\x18\x01 \x01(\x0b\x32;.temporal.api.workflow.v1.PostResetOperation.SignalWorkflowH\x00\x12\x65\n\x17update_workflow_options\x18\x02 \x01(\x0b\x32\x42.temporal.api.workflow.v1.PostResetOperation.UpdateWorkflowOptionsH\x00\x1a\xb3\x01\n\x0eSignalWorkflow\x12\x13\n\x0bsignal_name\x18\x01 \x01(\t\x12/\n\x05input\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12.\n\x06header\x18\x03 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12+\n\x05links\x18\x04 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x1a\xa0\x01\n\x15UpdateWorkflowOptions\x12V\n\x1aworkflow_execution_options\x18\x01 \x01(\x0b\x32\x32.temporal.api.workflow.v1.WorkflowExecutionOptions\x12/\n\x0bupdate_mask\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.FieldMaskB\t\n\x07variant"o\n\x1aWorkflowExecutionPauseInfo\x12\x10\n\x08identity\x18\x01 \x01(\t\x12/\n\x0bpaused_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06reason\x18\x03 \x01(\tB\x93\x01\n\x1bio.temporal.api.workflow.v1B\x0cMessageProtoP\x01Z\'go.temporal.io/api/workflow/v1;workflow\xaa\x02\x1aTemporalio.Api.Workflow.V1\xea\x02\x1dTemporalio::Api::Workflow::V1b\x06proto3' ) @@ -95,11 +95,13 @@ "NexusOperationCancellationInfo" ] _WORKFLOWEXECUTIONOPTIONS = DESCRIPTOR.message_types_by_name["WorkflowExecutionOptions"] -_TIMESKIPPINGCONFIG = DESCRIPTOR.message_types_by_name["TimeSkippingConfig"] _VERSIONINGOVERRIDE = DESCRIPTOR.message_types_by_name["VersioningOverride"] _VERSIONINGOVERRIDE_PINNEDOVERRIDE = _VERSIONINGOVERRIDE.nested_types_by_name[ "PinnedOverride" ] +_VERSIONINGOVERRIDE_ONETIMEOVERRIDE = _VERSIONINGOVERRIDE.nested_types_by_name[ + "OneTimeOverride" +] _ONCONFLICTOPTIONS = DESCRIPTOR.message_types_by_name["OnConflictOptions"] _REQUESTIDINFO = DESCRIPTOR.message_types_by_name["RequestIdInfo"] _POSTRESETOPERATION = DESCRIPTOR.message_types_by_name["PostResetOperation"] @@ -361,17 +363,6 @@ ) _sym_db.RegisterMessage(WorkflowExecutionOptions) -TimeSkippingConfig = _reflection.GeneratedProtocolMessageType( - "TimeSkippingConfig", - (_message.Message,), - { - "DESCRIPTOR": _TIMESKIPPINGCONFIG, - "__module__": "temporalio.api.workflow.v1.message_pb2", - # @@protoc_insertion_point(class_scope:temporal.api.workflow.v1.TimeSkippingConfig) - }, -) -_sym_db.RegisterMessage(TimeSkippingConfig) - VersioningOverride = _reflection.GeneratedProtocolMessageType( "VersioningOverride", (_message.Message,), @@ -385,6 +376,15 @@ # @@protoc_insertion_point(class_scope:temporal.api.workflow.v1.VersioningOverride.PinnedOverride) }, ), + "OneTimeOverride": _reflection.GeneratedProtocolMessageType( + "OneTimeOverride", + (_message.Message,), + { + "DESCRIPTOR": _VERSIONINGOVERRIDE_ONETIMEOVERRIDE, + "__module__": "temporalio.api.workflow.v1.message_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.workflow.v1.VersioningOverride.OneTimeOverride) + }, + ), "DESCRIPTOR": _VERSIONINGOVERRIDE, "__module__": "temporalio.api.workflow.v1.message_pb2", # @@protoc_insertion_point(class_scope:temporal.api.workflow.v1.VersioningOverride) @@ -392,6 +392,7 @@ ) _sym_db.RegisterMessage(VersioningOverride) _sym_db.RegisterMessage(VersioningOverride.PinnedOverride) +_sym_db.RegisterMessage(VersioningOverride.OneTimeOverride) OnConflictOptions = _reflection.GeneratedProtocolMessageType( "OnConflictOptions", @@ -535,67 +536,67 @@ _WORKFLOWEXECUTIONINFO._serialized_start = 552 _WORKFLOWEXECUTIONINFO._serialized_end = 1816 _WORKFLOWEXECUTIONEXTENDEDINFO._serialized_start = 1819 - _WORKFLOWEXECUTIONEXTENDEDINFO._serialized_end = 2401 - _WORKFLOWEXECUTIONEXTENDEDINFO_REQUESTIDINFOSENTRY._serialized_start = 2307 - _WORKFLOWEXECUTIONEXTENDEDINFO_REQUESTIDINFOSENTRY._serialized_end = 2401 - _WORKFLOWEXECUTIONVERSIONINGINFO._serialized_start = 2404 - _WORKFLOWEXECUTIONVERSIONINGINFO._serialized_end = 3039 - _DEPLOYMENTTRANSITION._serialized_start = 3041 - _DEPLOYMENTTRANSITION._serialized_end = 3123 - _DEPLOYMENTVERSIONTRANSITION._serialized_start = 3126 - _DEPLOYMENTVERSIONTRANSITION._serialized_end = 3257 - _WORKFLOWEXECUTIONCONFIG._serialized_start = 3260 - _WORKFLOWEXECUTIONCONFIG._serialized_end = 3587 - _PENDINGACTIVITYINFO._serialized_start = 3590 - _PENDINGACTIVITYINFO._serialized_end = 5315 - _PENDINGACTIVITYINFO_PAUSEINFO._serialized_start = 4959 - _PENDINGACTIVITYINFO_PAUSEINFO._serialized_end = 5294 - _PENDINGACTIVITYINFO_PAUSEINFO_MANUAL._serialized_start = 5180 - _PENDINGACTIVITYINFO_PAUSEINFO_MANUAL._serialized_end = 5222 - _PENDINGACTIVITYINFO_PAUSEINFO_RULE._serialized_start = 5224 - _PENDINGACTIVITYINFO_PAUSEINFO_RULE._serialized_end = 5281 - _PENDINGCHILDEXECUTIONINFO._serialized_start = 5318 - _PENDINGCHILDEXECUTIONINFO._serialized_end = 5503 - _PENDINGWORKFLOWTASKINFO._serialized_start = 5506 - _PENDINGWORKFLOWTASKINFO._serialized_end = 5775 - _RESETPOINTS._serialized_start = 5777 - _RESETPOINTS._serialized_end = 5848 - _RESETPOINTINFO._serialized_start = 5851 - _RESETPOINTINFO._serialized_end = 6090 - _NEWWORKFLOWEXECUTIONINFO._serialized_start = 6093 - _NEWWORKFLOWEXECUTIONINFO._serialized_end = 6994 - _CALLBACKINFO._serialized_start = 6997 - _CALLBACKINFO._serialized_end = 7767 - _CALLBACKINFO_WORKFLOWCLOSED._serialized_start = 7471 - _CALLBACKINFO_WORKFLOWCLOSED._serialized_end = 7487 - _CALLBACKINFO_UPDATEWORKFLOWEXECUTIONCOMPLETED._serialized_start = 7489 - _CALLBACKINFO_UPDATEWORKFLOWEXECUTIONCOMPLETED._serialized_end = 7542 - _CALLBACKINFO_TRIGGER._serialized_start = 7545 - _CALLBACKINFO_TRIGGER._serialized_end = 7767 - _PENDINGNEXUSOPERATIONINFO._serialized_start = 7770 - _PENDINGNEXUSOPERATIONINFO._serialized_end = 8549 - _NEXUSOPERATIONCANCELLATIONINFO._serialized_start = 8552 - _NEXUSOPERATIONCANCELLATIONINFO._serialized_end = 8940 - _WORKFLOWEXECUTIONOPTIONS._serialized_start = 8943 - _WORKFLOWEXECUTIONOPTIONS._serialized_end = 9172 - _TIMESKIPPINGCONFIG._serialized_start = 9175 - _TIMESKIPPINGCONFIG._serialized_end = 9389 - _VERSIONINGOVERRIDE._serialized_start = 9392 - _VERSIONINGOVERRIDE._serialized_end = 9965 - _VERSIONINGOVERRIDE_PINNEDOVERRIDE._serialized_start = 9675 - _VERSIONINGOVERRIDE_PINNEDOVERRIDE._serialized_end = 9848 - _VERSIONINGOVERRIDE_PINNEDOVERRIDEBEHAVIOR._serialized_start = 9850 - _VERSIONINGOVERRIDE_PINNEDOVERRIDEBEHAVIOR._serialized_end = 9953 - _ONCONFLICTOPTIONS._serialized_start = 9967 - _ONCONFLICTOPTIONS._serialized_end = 10072 - _REQUESTIDINFO._serialized_start = 10074 - _REQUESTIDINFO._serialized_end = 10179 - _POSTRESETOPERATION._serialized_start = 10182 - _POSTRESETOPERATION._serialized_end = 10749 - _POSTRESETOPERATION_SIGNALWORKFLOW._serialized_start = 10396 - _POSTRESETOPERATION_SIGNALWORKFLOW._serialized_end = 10575 - _POSTRESETOPERATION_UPDATEWORKFLOWOPTIONS._serialized_start = 10578 - _POSTRESETOPERATION_UPDATEWORKFLOWOPTIONS._serialized_end = 10738 - _WORKFLOWEXECUTIONPAUSEINFO._serialized_start = 10751 - _WORKFLOWEXECUTIONPAUSEINFO._serialized_end = 10862 + _WORKFLOWEXECUTIONEXTENDEDINFO._serialized_end = 2471 + _WORKFLOWEXECUTIONEXTENDEDINFO_REQUESTIDINFOSENTRY._serialized_start = 2377 + _WORKFLOWEXECUTIONEXTENDEDINFO_REQUESTIDINFOSENTRY._serialized_end = 2471 + _WORKFLOWEXECUTIONVERSIONINGINFO._serialized_start = 2474 + _WORKFLOWEXECUTIONVERSIONINGINFO._serialized_end = 3109 + _DEPLOYMENTTRANSITION._serialized_start = 3111 + _DEPLOYMENTTRANSITION._serialized_end = 3193 + _DEPLOYMENTVERSIONTRANSITION._serialized_start = 3196 + _DEPLOYMENTVERSIONTRANSITION._serialized_end = 3327 + _WORKFLOWEXECUTIONCONFIG._serialized_start = 3330 + _WORKFLOWEXECUTIONCONFIG._serialized_end = 3657 + _PENDINGACTIVITYINFO._serialized_start = 3660 + _PENDINGACTIVITYINFO._serialized_end = 5385 + _PENDINGACTIVITYINFO_PAUSEINFO._serialized_start = 5029 + _PENDINGACTIVITYINFO_PAUSEINFO._serialized_end = 5364 + _PENDINGACTIVITYINFO_PAUSEINFO_MANUAL._serialized_start = 5250 + _PENDINGACTIVITYINFO_PAUSEINFO_MANUAL._serialized_end = 5292 + _PENDINGACTIVITYINFO_PAUSEINFO_RULE._serialized_start = 5294 + _PENDINGACTIVITYINFO_PAUSEINFO_RULE._serialized_end = 5351 + _PENDINGCHILDEXECUTIONINFO._serialized_start = 5388 + _PENDINGCHILDEXECUTIONINFO._serialized_end = 5573 + _PENDINGWORKFLOWTASKINFO._serialized_start = 5576 + _PENDINGWORKFLOWTASKINFO._serialized_end = 5845 + _RESETPOINTS._serialized_start = 5847 + _RESETPOINTS._serialized_end = 5918 + _RESETPOINTINFO._serialized_start = 5921 + _RESETPOINTINFO._serialized_end = 6160 + _NEWWORKFLOWEXECUTIONINFO._serialized_start = 6163 + _NEWWORKFLOWEXECUTIONINFO._serialized_end = 7064 + _CALLBACKINFO._serialized_start = 7067 + _CALLBACKINFO._serialized_end = 7837 + _CALLBACKINFO_WORKFLOWCLOSED._serialized_start = 7541 + _CALLBACKINFO_WORKFLOWCLOSED._serialized_end = 7557 + _CALLBACKINFO_UPDATEWORKFLOWEXECUTIONCOMPLETED._serialized_start = 7559 + _CALLBACKINFO_UPDATEWORKFLOWEXECUTIONCOMPLETED._serialized_end = 7612 + _CALLBACKINFO_TRIGGER._serialized_start = 7615 + _CALLBACKINFO_TRIGGER._serialized_end = 7837 + _PENDINGNEXUSOPERATIONINFO._serialized_start = 7840 + _PENDINGNEXUSOPERATIONINFO._serialized_end = 8619 + _NEXUSOPERATIONCANCELLATIONINFO._serialized_start = 8622 + _NEXUSOPERATIONCANCELLATIONINFO._serialized_end = 9010 + _WORKFLOWEXECUTIONOPTIONS._serialized_start = 9013 + _WORKFLOWEXECUTIONOPTIONS._serialized_end = 9240 + _VERSIONINGOVERRIDE._serialized_start = 9243 + _VERSIONINGOVERRIDE._serialized_end = 10005 + _VERSIONINGOVERRIDE_PINNEDOVERRIDE._serialized_start = 9608 + _VERSIONINGOVERRIDE_PINNEDOVERRIDE._serialized_end = 9781 + _VERSIONINGOVERRIDE_ONETIMEOVERRIDE._serialized_start = 9783 + _VERSIONINGOVERRIDE_ONETIMEOVERRIDE._serialized_end = 9888 + _VERSIONINGOVERRIDE_PINNEDOVERRIDEBEHAVIOR._serialized_start = 9890 + _VERSIONINGOVERRIDE_PINNEDOVERRIDEBEHAVIOR._serialized_end = 9993 + _ONCONFLICTOPTIONS._serialized_start = 10007 + _ONCONFLICTOPTIONS._serialized_end = 10112 + _REQUESTIDINFO._serialized_start = 10114 + _REQUESTIDINFO._serialized_end = 10219 + _POSTRESETOPERATION._serialized_start = 10222 + _POSTRESETOPERATION._serialized_end = 10789 + _POSTRESETOPERATION_SIGNALWORKFLOW._serialized_start = 10436 + _POSTRESETOPERATION_SIGNALWORKFLOW._serialized_end = 10615 + _POSTRESETOPERATION_UPDATEWORKFLOWOPTIONS._serialized_start = 10618 + _POSTRESETOPERATION_UPDATEWORKFLOWOPTIONS._serialized_end = 10778 + _WORKFLOWEXECUTIONPAUSEINFO._serialized_start = 10791 + _WORKFLOWEXECUTIONPAUSEINFO._serialized_end = 10902 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/workflow/v1/message_pb2.pyi b/temporalio/api/workflow/v1/message_pb2.pyi index e9491a87c..b390d94f4 100644 --- a/temporalio/api/workflow/v1/message_pb2.pyi +++ b/temporalio/api/workflow/v1/message_pb2.pyi @@ -326,6 +326,7 @@ class WorkflowExecutionExtendedInfo(google.protobuf.message.Message): RESET_RUN_ID_FIELD_NUMBER: builtins.int REQUEST_ID_INFOS_FIELD_NUMBER: builtins.int PAUSE_INFO_FIELD_NUMBER: builtins.int + TIME_SKIPPING_INFO_FIELD_NUMBER: builtins.int @property def execution_expiration_time(self) -> google.protobuf.timestamp_pb2.Timestamp: """Workflow execution expiration time is defined as workflow start time plus expiration timeout. @@ -358,6 +359,13 @@ class WorkflowExecutionExtendedInfo(google.protobuf.message.Message): @property def pause_info(self) -> global___WorkflowExecutionPauseInfo: """Information about the workflow execution pause operation.""" + @property + def time_skipping_info( + self, + ) -> temporalio.api.common.v1.message_pb2.TimeSkippingInfo: + """Information about time skipping of the workflow execution. + If the execution has never enabled time skipping, it will be nil. + """ def __init__( self, *, @@ -370,6 +378,8 @@ class WorkflowExecutionExtendedInfo(google.protobuf.message.Message): request_id_infos: collections.abc.Mapping[builtins.str, global___RequestIdInfo] | None = ..., pause_info: global___WorkflowExecutionPauseInfo | None = ..., + time_skipping_info: temporalio.api.common.v1.message_pb2.TimeSkippingInfo + | None = ..., ) -> None: ... def HasField( self, @@ -384,6 +394,8 @@ class WorkflowExecutionExtendedInfo(google.protobuf.message.Message): b"pause_info", "run_expiration_time", b"run_expiration_time", + "time_skipping_info", + b"time_skipping_info", ], ) -> builtins.bool: ... def ClearField( @@ -405,6 +417,8 @@ class WorkflowExecutionExtendedInfo(google.protobuf.message.Message): b"reset_run_id", "run_expiration_time", b"run_expiration_time", + "time_skipping_info", + b"time_skipping_info", ], ) -> None: ... @@ -1886,17 +1900,26 @@ class WorkflowExecutionOptions(google.protobuf.message.Message): def priority(self) -> temporalio.api.common.v1.message_pb2.Priority: """If set, overrides the workflow's priority sent by the SDK.""" @property - def time_skipping_config(self) -> global___TimeSkippingConfig: - """Time-skipping configuration for this workflow execution. - If not set, the time-skipping configuration is not updated by this request; - the existing configuration is preserved. + def time_skipping_config( + self, + ) -> temporalio.api.common.v1.message_pb2.TimeSkippingConfig: + """The time-skipping configuration for this workflow execution. + When `fast_forward` is set, time will be fast-forwarded to a future point relative + to the current workflow timestamp. Each call takes effect, even if + `fast_forward` is set to the same duration, since the target time is recalculated + from the current timestamp on every call. + + This field must be updated as a whole; updating individual sub-fields is not supported. + When setting the update mask in `UpdateWorkflowExecutionOptionsRequest`, + `BatchOperationUpdateWorkflowExecutionOptions`, etc., use a mask that covers the entire field. """ def __init__( self, *, versioning_override: global___VersioningOverride | None = ..., priority: temporalio.api.common.v1.message_pb2.Priority | None = ..., - time_skipping_config: global___TimeSkippingConfig | None = ..., + time_skipping_config: temporalio.api.common.v1.message_pb2.TimeSkippingConfig + | None = ..., ) -> None: ... def HasField( self, @@ -1923,81 +1946,6 @@ class WorkflowExecutionOptions(google.protobuf.message.Message): global___WorkflowExecutionOptions = WorkflowExecutionOptions -class TimeSkippingConfig(google.protobuf.message.Message): - """Configuration for time skipping during a workflow execution. - When enabled, virtual time advances automatically whenever there is no in-flight work. - In-flight work includes activities, child workflows, Nexus operations, signal/cancel external workflow operations, - and possibly other features added in the future. - User timers are not classified as in-flight work and will be skipped over. - When time advances, it skips to the earlier of the next user timer or the configured bound, if either exists. - - Propagation behavior of time skipping: - The enabled flag, bound fields, and accumulated skipped duration are propagated to related executions as follows: - (1) Child workflows and continue-as-new: both the configuration and the accumulated skipped duration are - inherited from the current execution. The configured bound is shared between the inherited skipped - duration and any additional duration skipped by the new run. - (2) Retry and cron: the configuration and accumulated skipped duration are inherited as recorded when the - current workflow started; the accumulated skipped duration of the current run is not propagated. - (3) Reset: the new run retains the time-skipping configuration of the current execution. Because reset replays - all events up to the reset point and re-applies any UpdateWorkflowExecutionOptions changes made after that - point, the resulting run ends up with the same final time-skipping configuration as the previous run. - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - ENABLED_FIELD_NUMBER: builtins.int - MAX_SKIPPED_DURATION_FIELD_NUMBER: builtins.int - MAX_ELAPSED_DURATION_FIELD_NUMBER: builtins.int - enabled: builtins.bool - """Enables or disables time skipping for this workflow execution.""" - @property - def max_skipped_duration(self) -> google.protobuf.duration_pb2.Duration: - """Maximum total virtual time that can be skipped.""" - @property - def max_elapsed_duration(self) -> google.protobuf.duration_pb2.Duration: - """Maximum elapsed time since time skipping was enabled. - This includes both skipped time and real time elapsing. - (-- api-linter: core::0142::time-field-names=disabled --) - """ - def __init__( - self, - *, - enabled: builtins.bool = ..., - max_skipped_duration: google.protobuf.duration_pb2.Duration | None = ..., - max_elapsed_duration: google.protobuf.duration_pb2.Duration | None = ..., - ) -> None: ... - def HasField( - self, - field_name: typing_extensions.Literal[ - "bound", - b"bound", - "max_elapsed_duration", - b"max_elapsed_duration", - "max_skipped_duration", - b"max_skipped_duration", - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "bound", - b"bound", - "enabled", - b"enabled", - "max_elapsed_duration", - b"max_elapsed_duration", - "max_skipped_duration", - b"max_skipped_duration", - ], - ) -> None: ... - def WhichOneof( - self, oneof_group: typing_extensions.Literal["bound", b"bound"] - ) -> ( - typing_extensions.Literal["max_skipped_duration", "max_elapsed_duration"] | None - ): ... - -global___TimeSkippingConfig = TimeSkippingConfig - class VersioningOverride(google.protobuf.message.Message): """Used to override the versioning behavior (and pinned deployment version, if applicable) of a specific workflow execution. If set, this override takes precedence over worker-sent values. @@ -2083,16 +2031,78 @@ class VersioningOverride(google.protobuf.message.Message): ], ) -> None: ... + class OneTimeOverride(google.protobuf.message.Message): + """Routes Workflow Tasks for this execution to `target_deployment_version` + until a Workflow Task completes on that version, then clears the override. + + This does not force the workflow's normal Versioning Behavior to become + Pinned. After the Workflow Task completes on `target_deployment_version`, + the workflow execution's normal Versioning Behavior and Deployment Version + are taken from the worker's completion response. + + Example: if an execution is one-time moved from version X to version Y, and + version Z later becomes current: + - if worker Y reports Pinned, the execution stays on Y; + - if worker Y reports AutoUpgrade, the execution routes to Z on a future + Workflow Task; + - if worker Y reports Pinned and the workflow uses upgrade-on-continue-as-new, + the current run stays on Y and the execution can route to Z after + continue-as-new. + + If no Workflow Task completes on `target_deployment_version`, this override + remains pending. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TARGET_DEPLOYMENT_VERSION_FIELD_NUMBER: builtins.int + @property + def target_deployment_version( + self, + ) -> temporalio.api.deployment.v1.message_pb2.WorkerDeploymentVersion: + """Required. Worker Deployment Version to receive the one-time Workflow Task.""" + def __init__( + self, + *, + target_deployment_version: temporalio.api.deployment.v1.message_pb2.WorkerDeploymentVersion + | None = ..., + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "target_deployment_version", b"target_deployment_version" + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "target_deployment_version", b"target_deployment_version" + ], + ) -> None: ... + PINNED_FIELD_NUMBER: builtins.int AUTO_UPGRADE_FIELD_NUMBER: builtins.int + ONE_TIME_FIELD_NUMBER: builtins.int BEHAVIOR_FIELD_NUMBER: builtins.int DEPLOYMENT_FIELD_NUMBER: builtins.int PINNED_VERSION_FIELD_NUMBER: builtins.int @property def pinned(self) -> global___VersioningOverride.PinnedOverride: - """Override the workflow to have Pinned behavior.""" + """Override the workflow to have Pinned behavior. This is a sticky override: + Workflow Tasks continue to route according to this override until it is + explicitly removed. + """ auto_upgrade: builtins.bool """Override the workflow to have AutoUpgrade behavior.""" + @property + def one_time(self) -> global___VersioningOverride.OneTimeOverride: + """Override Workflow Task routing to a specific Worker Deployment Version until + one Workflow Task completes there. After completion, the workflow execution's + Versioning Behavior and Deployment Version come from the worker's completion + response. + (-- api-linter: core::0142::time-field-type=disabled + aip.dev/not-precedent: one_time describes one-time routing semantics, not a timestamp or duration. --) + """ behavior: temporalio.api.enums.v1.workflow_pb2.VersioningBehavior.ValueType """Required. Deprecated. Use `override`. @@ -2114,6 +2124,7 @@ class VersioningOverride(google.protobuf.message.Message): *, pinned: global___VersioningOverride.PinnedOverride | None = ..., auto_upgrade: builtins.bool = ..., + one_time: global___VersioningOverride.OneTimeOverride | None = ..., behavior: temporalio.api.enums.v1.workflow_pb2.VersioningBehavior.ValueType = ..., deployment: temporalio.api.deployment.v1.message_pb2.Deployment | None = ..., pinned_version: builtins.str = ..., @@ -2125,6 +2136,8 @@ class VersioningOverride(google.protobuf.message.Message): b"auto_upgrade", "deployment", b"deployment", + "one_time", + b"one_time", "override", b"override", "pinned", @@ -2140,6 +2153,8 @@ class VersioningOverride(google.protobuf.message.Message): b"behavior", "deployment", b"deployment", + "one_time", + b"one_time", "override", b"override", "pinned", @@ -2150,7 +2165,7 @@ class VersioningOverride(google.protobuf.message.Message): ) -> None: ... def WhichOneof( self, oneof_group: typing_extensions.Literal["override", b"override"] - ) -> typing_extensions.Literal["pinned", "auto_upgrade"] | None: ... + ) -> typing_extensions.Literal["pinned", "auto_upgrade", "one_time"] | None: ... global___VersioningOverride = VersioningOverride diff --git a/temporalio/api/workflowservice/v1/__init__.py b/temporalio/api/workflowservice/v1/__init__.py index 771e4655c..ab72e6b09 100644 --- a/temporalio/api/workflowservice/v1/__init__.py +++ b/temporalio/api/workflowservice/v1/__init__.py @@ -5,6 +5,8 @@ CountNexusOperationExecutionsResponse, CountSchedulesRequest, CountSchedulesResponse, + CountWorkersRequest, + CountWorkersResponse, CountWorkflowExecutionsRequest, CountWorkflowExecutionsResponse, CreateScheduleRequest, @@ -125,6 +127,8 @@ PollNexusOperationExecutionResponse, PollNexusTaskQueueRequest, PollNexusTaskQueueResponse, + PollWorkflowExecutionTimeSkippingRequest, + PollWorkflowExecutionTimeSkippingResponse, PollWorkflowExecutionUpdateRequest, PollWorkflowExecutionUpdateResponse, PollWorkflowTaskQueueRequest, @@ -250,6 +254,8 @@ "CountNexusOperationExecutionsResponse", "CountSchedulesRequest", "CountSchedulesResponse", + "CountWorkersRequest", + "CountWorkersResponse", "CountWorkflowExecutionsRequest", "CountWorkflowExecutionsResponse", "CreateScheduleRequest", @@ -370,6 +376,8 @@ "PollNexusOperationExecutionResponse", "PollNexusTaskQueueRequest", "PollNexusTaskQueueResponse", + "PollWorkflowExecutionTimeSkippingRequest", + "PollWorkflowExecutionTimeSkippingResponse", "PollWorkflowExecutionUpdateRequest", "PollWorkflowExecutionUpdateResponse", "PollWorkflowTaskQueueRequest", diff --git a/temporalio/api/workflowservice/v1/request_response_pb2.py b/temporalio/api/workflowservice/v1/request_response_pb2.py index 32469c030..e1b2e01c4 100644 --- a/temporalio/api/workflowservice/v1/request_response_pb2.py +++ b/temporalio/api/workflowservice/v1/request_response_pb2.py @@ -66,6 +66,9 @@ from temporalio.api.enums.v1 import ( task_queue_pb2 as temporal_dot_api_dot_enums_dot_v1_dot_task__queue__pb2, ) +from temporalio.api.enums.v1 import ( + time_skipping_pb2 as temporal_dot_api_dot_enums_dot_v1_dot_time__skipping__pb2, +) from temporalio.api.enums.v1 import ( update_pb2 as temporal_dot_api_dot_enums_dot_v1_dot_update__pb2, ) @@ -128,7 +131,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n6temporal/api/workflowservice/v1/request_response.proto\x12\x1ftemporal.api.workflowservice.v1\x1a+temporal/api/enums/v1/batch_operation.proto\x1a"temporal/api/enums/v1/common.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a%temporal/api/enums/v1/namespace.proto\x1a(temporal/api/enums/v1/failed_cause.proto\x1a!temporal/api/enums/v1/query.proto\x1a!temporal/api/enums/v1/reset.proto\x1a&temporal/api/enums/v1/task_queue.proto\x1a&temporal/api/enums/v1/deployment.proto\x1a"temporal/api/enums/v1/update.proto\x1a$temporal/api/enums/v1/activity.proto\x1a!temporal/api/enums/v1/nexus.proto\x1a&temporal/api/activity/v1/message.proto\x1a$temporal/api/common/v1/message.proto\x1a%temporal/api/history/v1/message.proto\x1a&temporal/api/workflow/v1/message.proto\x1a%temporal/api/command/v1/message.proto\x1a$temporal/api/compute/v1/config.proto\x1a(temporal/api/deployment/v1/message.proto\x1a%temporal/api/failure/v1/message.proto\x1a$temporal/api/filter/v1/message.proto\x1a&temporal/api/protocol/v1/message.proto\x1a\'temporal/api/namespace/v1/message.proto\x1a#temporal/api/query/v1/message.proto\x1a)temporal/api/replication/v1/message.proto\x1a#temporal/api/rules/v1/message.proto\x1a\'temporal/api/sdk/v1/worker_config.proto\x1a&temporal/api/schedule/v1/message.proto\x1a\'temporal/api/taskqueue/v1/message.proto\x1a$temporal/api/update/v1/message.proto\x1a%temporal/api/version/v1/message.proto\x1a#temporal/api/batch/v1/message.proto\x1a\x30temporal/api/sdk/v1/task_complete_metadata.proto\x1a\'temporal/api/sdk/v1/user_metadata.proto\x1a#temporal/api/nexus/v1/message.proto\x1a$temporal/api/worker/v1/message.proto\x1a\x1egoogle/protobuf/duration.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\x88\x05\n\x18RegisterNamespaceRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x13\n\x0bowner_email\x18\x03 \x01(\t\x12\x46\n#workflow_execution_retention_period\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12G\n\x08\x63lusters\x18\x05 \x03(\x0b\x32\x35.temporal.api.replication.v1.ClusterReplicationConfig\x12\x1b\n\x13\x61\x63tive_cluster_name\x18\x06 \x01(\t\x12Q\n\x04\x64\x61ta\x18\x07 \x03(\x0b\x32\x43.temporal.api.workflowservice.v1.RegisterNamespaceRequest.DataEntry\x12\x16\n\x0esecurity_token\x18\x08 \x01(\t\x12\x1b\n\x13is_global_namespace\x18\t \x01(\x08\x12\x44\n\x16history_archival_state\x18\n \x01(\x0e\x32$.temporal.api.enums.v1.ArchivalState\x12\x1c\n\x14history_archival_uri\x18\x0b \x01(\t\x12G\n\x19visibility_archival_state\x18\x0c \x01(\x0e\x32$.temporal.api.enums.v1.ArchivalState\x12\x1f\n\x17visibility_archival_uri\x18\r \x01(\t\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"\x1b\n\x19RegisterNamespaceResponse"\x89\x01\n\x15ListNamespacesRequest\x12\x11\n\tpage_size\x18\x01 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c\x12\x44\n\x10namespace_filter\x18\x03 \x01(\x0b\x32*.temporal.api.namespace.v1.NamespaceFilter"\x81\x01\n\x16ListNamespacesResponse\x12N\n\nnamespaces\x18\x01 \x03(\x0b\x32:.temporal.api.workflowservice.v1.DescribeNamespaceResponse\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"S\n\x18\x44\x65scribeNamespaceRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\t\x12\x18\n\x10weak_consistency\x18\x03 \x01(\x08"\xb4\x03\n\x19\x44\x65scribeNamespaceResponse\x12@\n\x0enamespace_info\x18\x01 \x01(\x0b\x32(.temporal.api.namespace.v1.NamespaceInfo\x12:\n\x06\x63onfig\x18\x02 \x01(\x0b\x32*.temporal.api.namespace.v1.NamespaceConfig\x12S\n\x12replication_config\x18\x03 \x01(\x0b\x32\x37.temporal.api.replication.v1.NamespaceReplicationConfig\x12\x18\n\x10\x66\x61ilover_version\x18\x04 \x01(\x03\x12\x1b\n\x13is_global_namespace\x18\x05 \x01(\x08\x12\x45\n\x10\x66\x61ilover_history\x18\x06 \x03(\x0b\x32+.temporal.api.replication.v1.FailoverStatus\x12\x46\n\x12poller_group_infos\x18\x07 \x03(\x0b\x32*.temporal.api.taskqueue.v1.PollerGroupInfo"\xcf\x02\n\x16UpdateNamespaceRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x43\n\x0bupdate_info\x18\x02 \x01(\x0b\x32..temporal.api.namespace.v1.UpdateNamespaceInfo\x12:\n\x06\x63onfig\x18\x03 \x01(\x0b\x32*.temporal.api.namespace.v1.NamespaceConfig\x12S\n\x12replication_config\x18\x04 \x01(\x0b\x32\x37.temporal.api.replication.v1.NamespaceReplicationConfig\x12\x16\n\x0esecurity_token\x18\x05 \x01(\t\x12\x19\n\x11\x64\x65lete_bad_binary\x18\x06 \x01(\t\x12\x19\n\x11promote_namespace\x18\x07 \x01(\x08"\xa3\x02\n\x17UpdateNamespaceResponse\x12@\n\x0enamespace_info\x18\x01 \x01(\x0b\x32(.temporal.api.namespace.v1.NamespaceInfo\x12:\n\x06\x63onfig\x18\x02 \x01(\x0b\x32*.temporal.api.namespace.v1.NamespaceConfig\x12S\n\x12replication_config\x18\x03 \x01(\x0b\x32\x37.temporal.api.replication.v1.NamespaceReplicationConfig\x12\x18\n\x10\x66\x61ilover_version\x18\x04 \x01(\x03\x12\x1b\n\x13is_global_namespace\x18\x05 \x01(\x08"F\n\x19\x44\x65precateNamespaceRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x16\n\x0esecurity_token\x18\x02 \x01(\t"\x1c\n\x1a\x44\x65precateNamespaceResponse"\xd3\x0c\n\x1dStartWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x04 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12=\n\x1aworkflow_execution_timeout\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x10\n\x08identity\x18\t \x01(\t\x12\x12\n\nrequest_id\x18\n \x01(\t\x12N\n\x18workflow_id_reuse_policy\x18\x0b \x01(\x0e\x32,.temporal.api.enums.v1.WorkflowIdReusePolicy\x12T\n\x1bworkflow_id_conflict_policy\x18\x16 \x01(\x0e\x32/.temporal.api.enums.v1.WorkflowIdConflictPolicy\x12\x39\n\x0cretry_policy\x18\x0c \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x15\n\rcron_schedule\x18\r \x01(\t\x12*\n\x04memo\x18\x0e \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x0f \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12.\n\x06header\x18\x10 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x1f\n\x17request_eager_execution\x18\x11 \x01(\x08\x12;\n\x11\x63ontinued_failure\x18\x12 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12@\n\x16last_completion_result\x18\x13 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x37\n\x14workflow_start_delay\x18\x14 \x01(\x0b\x32\x19.google.protobuf.Duration\x12>\n\x14\x63ompletion_callbacks\x18\x15 \x03(\x0b\x32 .temporal.api.common.v1.Callback\x12\x38\n\ruser_metadata\x18\x17 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12+\n\x05links\x18\x18 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x12I\n\x13versioning_override\x18\x19 \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12H\n\x13on_conflict_options\x18\x1a \x01(\x0b\x32+.temporal.api.workflow.v1.OnConflictOptions\x12\x32\n\x08priority\x18\x1b \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12\\\n\x1f\x65\x61ger_worker_deployment_options\x18\x1c \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions\x12J\n\x14time_skipping_config\x18\x1d \x01(\x0b\x32,.temporal.api.workflow.v1.TimeSkippingConfig"\x8a\x02\n\x1eStartWorkflowExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x0f\n\x07started\x18\x03 \x01(\x08\x12>\n\x06status\x18\x05 \x01(\x0e\x32..temporal.api.enums.v1.WorkflowExecutionStatus\x12[\n\x13\x65\x61ger_workflow_task\x18\x02 \x01(\x0b\x32>.temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse\x12*\n\x04link\x18\x04 \x01(\x0b\x32\x1c.temporal.api.common.v1.Link"\xaa\x02\n"GetWorkflowExecutionHistoryRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x19\n\x11maximum_page_size\x18\x03 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x04 \x01(\x0c\x12\x16\n\x0ewait_new_event\x18\x05 \x01(\x08\x12P\n\x19history_event_filter_type\x18\x06 \x01(\x0e\x32-.temporal.api.enums.v1.HistoryEventFilterType\x12\x15\n\rskip_archival\x18\x07 \x01(\x08"\xba\x01\n#GetWorkflowExecutionHistoryResponse\x12\x31\n\x07history\x18\x01 \x01(\x0b\x32 .temporal.api.history.v1.History\x12\x35\n\x0braw_history\x18\x02 \x03(\x0b\x32 .temporal.api.common.v1.DataBlob\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\x10\n\x08\x61rchived\x18\x04 \x01(\x08"\xb0\x01\n)GetWorkflowExecutionHistoryReverseRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x19\n\x11maximum_page_size\x18\x03 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x04 \x01(\x0c"x\n*GetWorkflowExecutionHistoryReverseResponse\x12\x31\n\x07history\x18\x01 \x01(\x0b\x32 .temporal.api.history.v1.History\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c"\xb8\x03\n\x1cPollWorkflowTaskQueueRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x38\n\ntask_queue\x18\x02 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12\x17\n\x0fpoller_group_id\x18\n \x01(\t\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x1b\n\x13worker_instance_key\x18\x08 \x01(\t\x12!\n\x19worker_control_task_queue\x18\t \x01(\t\x12\x1b\n\x0f\x62inary_checksum\x18\x04 \x01(\tB\x02\x18\x01\x12Z\n\x1bworker_version_capabilities\x18\x05 \x01(\x0b\x32\x31.temporal.api.common.v1.WorkerVersionCapabilitiesB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x06 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptionsJ\x04\x08\x07\x10\x08R\x10worker_heartbeat"\xf2\x07\n\x1dPollWorkflowTaskQueueResponse\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12!\n\x19previous_started_event_id\x18\x04 \x01(\x03\x12\x18\n\x10started_event_id\x18\x05 \x01(\x03\x12\x0f\n\x07\x61ttempt\x18\x06 \x01(\x05\x12\x1a\n\x12\x62\x61\x63klog_count_hint\x18\x07 \x01(\x03\x12\x31\n\x07history\x18\x08 \x01(\x0b\x32 .temporal.api.history.v1.History\x12\x17\n\x0fnext_page_token\x18\t \x01(\x0c\x12\x33\n\x05query\x18\n \x01(\x0b\x32$.temporal.api.query.v1.WorkflowQuery\x12K\n\x1dworkflow_execution_task_queue\x18\x0b \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12\x32\n\x0escheduled_time\x18\x0c \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x0cstarted_time\x18\r \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\\\n\x07queries\x18\x0e \x03(\x0b\x32K.temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse.QueriesEntry\x12\x33\n\x08messages\x18\x0f \x03(\x0b\x32!.temporal.api.protocol.v1.Message\x12Q\n\x17poller_scaling_decision\x18\x10 \x01(\x0b\x32\x30.temporal.api.taskqueue.v1.PollerScalingDecision\x12\x17\n\x0fpoller_group_id\x18\x11 \x01(\t\x12\x46\n\x12poller_group_infos\x18\x12 \x03(\x0b\x32*.temporal.api.taskqueue.v1.PollerGroupInfo\x1aT\n\x0cQueriesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x33\n\x05value\x18\x02 \x01(\x0b\x32$.temporal.api.query.v1.WorkflowQuery:\x02\x38\x01"\x8a\n\n#RespondWorkflowTaskCompletedRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x32\n\x08\x63ommands\x18\x02 \x03(\x0b\x32 .temporal.api.command.v1.Command\x12\x10\n\x08identity\x18\x03 \x01(\t\x12O\n\x11sticky_attributes\x18\x04 \x01(\x0b\x32\x34.temporal.api.taskqueue.v1.StickyExecutionAttributes\x12 \n\x18return_new_workflow_task\x18\x05 \x01(\x08\x12&\n\x1e\x66orce_create_new_workflow_task\x18\x06 \x01(\x08\x12\x1b\n\x0f\x62inary_checksum\x18\x07 \x01(\tB\x02\x18\x01\x12m\n\rquery_results\x18\x08 \x03(\x0b\x32V.temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest.QueryResultsEntry\x12\x11\n\tnamespace\x18\t \x01(\t\x12\x13\n\x0bresource_id\x18\x12 \x01(\t\x12L\n\x14worker_version_stamp\x18\n \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12\x33\n\x08messages\x18\x0b \x03(\x0b\x32!.temporal.api.protocol.v1.Message\x12H\n\x0csdk_metadata\x18\x0c \x01(\x0b\x32\x32.temporal.api.sdk.v1.WorkflowTaskCompletedMetadata\x12\x43\n\x11metering_metadata\x18\r \x01(\x0b\x32(.temporal.api.common.v1.MeteringMetadata\x12g\n\x0c\x63\x61pabilities\x18\x0e \x01(\x0b\x32Q.temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest.Capabilities\x12>\n\ndeployment\x18\x0f \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12\x46\n\x13versioning_behavior\x18\x10 \x01(\x0e\x32).temporal.api.enums.v1.VersioningBehavior\x12O\n\x12\x64\x65ployment_options\x18\x11 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions\x12\x1b\n\x13worker_instance_key\x18\x13 \x01(\t\x12!\n\x19worker_control_task_queue\x18\x14 \x01(\t\x1a_\n\x11QueryResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x39\n\x05value\x18\x02 \x01(\x0b\x32*.temporal.api.query.v1.WorkflowQueryResult:\x02\x38\x01\x1a\x45\n\x0c\x43\x61pabilities\x12\x35\n-discard_speculative_workflow_task_with_events\x18\x01 \x01(\x08"\xf5\x01\n$RespondWorkflowTaskCompletedResponse\x12U\n\rworkflow_task\x18\x01 \x01(\x0b\x32>.temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse\x12V\n\x0e\x61\x63tivity_tasks\x18\x02 \x03(\x0b\x32>.temporal.api.workflowservice.v1.PollActivityTaskQueueResponse\x12\x1e\n\x16reset_history_event_id\x18\x03 \x01(\x03"\x8d\x04\n RespondWorkflowTaskFailedRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12=\n\x05\x63\x61use\x18\x02 \x01(\x0e\x32..temporal.api.enums.v1.WorkflowTaskFailedCause\x12\x31\n\x07\x66\x61ilure\x18\x03 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x1b\n\x0f\x62inary_checksum\x18\x05 \x01(\tB\x02\x18\x01\x12\x11\n\tnamespace\x18\x06 \x01(\t\x12\x13\n\x0bresource_id\x18\x0b \x01(\t\x12\x33\n\x08messages\x18\x07 \x03(\x0b\x32!.temporal.api.protocol.v1.Message\x12\x46\n\x0eworker_version\x18\x08 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12>\n\ndeployment\x18\t \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\n \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions"#\n!RespondWorkflowTaskFailedResponse"\xe6\x03\n\x1cPollActivityTaskQueueRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x38\n\ntask_queue\x18\x02 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12\x17\n\x0fpoller_group_id\x18\n \x01(\t\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x1b\n\x13worker_instance_key\x18\x08 \x01(\t\x12!\n\x19worker_control_task_queue\x18\t \x01(\t\x12I\n\x13task_queue_metadata\x18\x04 \x01(\x0b\x32,.temporal.api.taskqueue.v1.TaskQueueMetadata\x12Z\n\x1bworker_version_capabilities\x18\x05 \x01(\x0b\x32\x31.temporal.api.common.v1.WorkerVersionCapabilitiesB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x06 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptionsJ\x04\x08\x07\x10\x08R\x10worker_heartbeat"\xd0\x08\n\x1dPollActivityTaskQueueResponse\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x1a\n\x12workflow_namespace\x18\x02 \x01(\t\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x45\n\x12workflow_execution\x18\x04 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\ractivity_type\x18\x05 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12\x13\n\x0b\x61\x63tivity_id\x18\x06 \x01(\t\x12.\n\x06header\x18\x07 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12/\n\x05input\x18\x08 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12;\n\x11heartbeat_details\x18\t \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x32\n\x0escheduled_time\x18\n \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x42\n\x1e\x63urrent_attempt_scheduled_time\x18\x0b \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x0cstarted_time\x18\x0c \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07\x61ttempt\x18\r \x01(\x05\x12<\n\x19schedule_to_close_timeout\x18\x0e \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\x0f \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\x10 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x11 \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12Q\n\x17poller_scaling_decision\x18\x12 \x01(\x0b\x32\x30.temporal.api.taskqueue.v1.PollerScalingDecision\x12\x32\n\x08priority\x18\x13 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12\x17\n\x0f\x61\x63tivity_run_id\x18\x14 \x01(\t\x12\x46\n\x12poller_group_infos\x18\x15 \x03(\x0b\x32*.temporal.api.taskqueue.v1.PollerGroupInfo"\xa5\x01\n"RecordActivityTaskHeartbeatRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x31\n\x07\x64\x65tails\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x11\n\tnamespace\x18\x04 \x01(\t\x12\x13\n\x0bresource_id\x18\x05 \x01(\t"p\n#RecordActivityTaskHeartbeatResponse\x12\x18\n\x10\x63\x61ncel_requested\x18\x01 \x01(\x08\x12\x17\n\x0f\x61\x63tivity_paused\x18\x02 \x01(\x08\x12\x16\n\x0e\x61\x63tivity_reset\x18\x03 \x01(\x08"\xcf\x01\n&RecordActivityTaskHeartbeatByIdRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x04 \x01(\t\x12\x31\n\x07\x64\x65tails\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x06 \x01(\t\x12\x13\n\x0bresource_id\x18\x07 \x01(\t"t\n\'RecordActivityTaskHeartbeatByIdResponse\x12\x18\n\x10\x63\x61ncel_requested\x18\x01 \x01(\x08\x12\x17\n\x0f\x61\x63tivity_paused\x18\x02 \x01(\x08\x12\x16\n\x0e\x61\x63tivity_reset\x18\x03 \x01(\x08"\xfe\x02\n#RespondActivityTaskCompletedRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x30\n\x06result\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x11\n\tnamespace\x18\x04 \x01(\t\x12\x13\n\x0bresource_id\x18\x08 \x01(\t\x12\x46\n\x0eworker_version\x18\x05 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12>\n\ndeployment\x18\x06 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x07 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions"&\n$RespondActivityTaskCompletedResponse"\xcf\x01\n\'RespondActivityTaskCompletedByIdRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x04 \x01(\t\x12\x30\n\x06result\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x06 \x01(\t\x12\x13\n\x0bresource_id\x18\x07 \x01(\t"*\n(RespondActivityTaskCompletedByIdResponse"\xbe\x03\n RespondActivityTaskFailedRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x31\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x11\n\tnamespace\x18\x04 \x01(\t\x12\x13\n\x0bresource_id\x18\t \x01(\t\x12@\n\x16last_heartbeat_details\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x46\n\x0eworker_version\x18\x06 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12>\n\ndeployment\x18\x07 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x08 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions"W\n!RespondActivityTaskFailedResponse\x12\x32\n\x08\x66\x61ilures\x18\x01 \x03(\x0b\x32 .temporal.api.failure.v1.Failure"\x8f\x02\n$RespondActivityTaskFailedByIdRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x04 \x01(\t\x12\x31\n\x07\x66\x61ilure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x10\n\x08identity\x18\x06 \x01(\t\x12@\n\x16last_heartbeat_details\x18\x07 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x13\n\x0bresource_id\x18\x08 \x01(\t"[\n%RespondActivityTaskFailedByIdResponse\x12\x32\n\x08\x66\x61ilures\x18\x01 \x03(\x0b\x32 .temporal.api.failure.v1.Failure"\xfe\x02\n"RespondActivityTaskCanceledRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x31\n\x07\x64\x65tails\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x11\n\tnamespace\x18\x04 \x01(\t\x12\x13\n\x0bresource_id\x18\x08 \x01(\t\x12\x46\n\x0eworker_version\x18\x05 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12>\n\ndeployment\x18\x06 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x07 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions"%\n#RespondActivityTaskCanceledResponse"\xa0\x02\n&RespondActivityTaskCanceledByIdRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x04 \x01(\t\x12\x31\n\x07\x64\x65tails\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x06 \x01(\t\x12O\n\x12\x64\x65ployment_options\x18\x07 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions\x12\x13\n\x0bresource_id\x18\x08 \x01(\t")\n\'RespondActivityTaskCanceledByIdResponse"\x84\x02\n%RequestCancelWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x12\n\nrequest_id\x18\x04 \x01(\t\x12\x1e\n\x16\x66irst_execution_run_id\x18\x05 \x01(\t\x12\x0e\n\x06reason\x18\x06 \x01(\t\x12+\n\x05links\x18\x07 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link"(\n&RequestCancelWorkflowExecutionResponse"\xde\x02\n\x1eSignalWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x13\n\x0bsignal_name\x18\x03 \x01(\t\x12/\n\x05input\x18\x04 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x12\n\nrequest_id\x18\x06 \x01(\t\x12\x13\n\x07\x63ontrol\x18\x07 \x01(\tB\x02\x18\x01\x12.\n\x06header\x18\x08 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12+\n\x05links\x18\n \x03(\x0b\x32\x1c.temporal.api.common.v1.LinkJ\x04\x08\t\x10\n"M\n\x1fSignalWorkflowExecutionResponse\x12*\n\x04link\x18\x01 \x01(\x0b\x32\x1c.temporal.api.common.v1.Link"\xbd\n\n\'SignalWithStartWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x04 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12=\n\x1aworkflow_execution_timeout\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x10\n\x08identity\x18\t \x01(\t\x12\x12\n\nrequest_id\x18\n \x01(\t\x12N\n\x18workflow_id_reuse_policy\x18\x0b \x01(\x0e\x32,.temporal.api.enums.v1.WorkflowIdReusePolicy\x12T\n\x1bworkflow_id_conflict_policy\x18\x16 \x01(\x0e\x32/.temporal.api.enums.v1.WorkflowIdConflictPolicy\x12\x13\n\x0bsignal_name\x18\x0c \x01(\t\x12\x36\n\x0csignal_input\x18\r \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x13\n\x07\x63ontrol\x18\x0e \x01(\tB\x02\x18\x01\x12\x39\n\x0cretry_policy\x18\x0f \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x15\n\rcron_schedule\x18\x10 \x01(\t\x12*\n\x04memo\x18\x11 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x12 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12.\n\x06header\x18\x13 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x37\n\x14workflow_start_delay\x18\x14 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\ruser_metadata\x18\x17 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12+\n\x05links\x18\x18 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x12I\n\x13versioning_override\x18\x19 \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12\x32\n\x08priority\x18\x1a \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12J\n\x14time_skipping_config\x18\x1b \x01(\x0b\x32,.temporal.api.workflow.v1.TimeSkippingConfigJ\x04\x08\x15\x10\x16"~\n(SignalWithStartWorkflowExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x0f\n\x07started\x18\x02 \x01(\x08\x12\x31\n\x0bsignal_link\x18\x03 \x01(\x0b\x32\x1c.temporal.api.common.v1.Link"\xc1\x03\n\x1dResetWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12%\n\x1dworkflow_task_finish_event_id\x18\x04 \x01(\x03\x12\x12\n\nrequest_id\x18\x05 \x01(\t\x12G\n\x12reset_reapply_type\x18\x06 \x01(\x0e\x32\'.temporal.api.enums.v1.ResetReapplyTypeB\x02\x18\x01\x12S\n\x1breset_reapply_exclude_types\x18\x07 \x03(\x0e\x32..temporal.api.enums.v1.ResetReapplyExcludeType\x12K\n\x15post_reset_operations\x18\x08 \x03(\x0b\x32,.temporal.api.workflow.v1.PostResetOperation\x12\x10\n\x08identity\x18\t \x01(\t"0\n\x1eResetWorkflowExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t"\x9f\x02\n!TerminateWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x31\n\x07\x64\x65tails\x18\x04 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x1e\n\x16\x66irst_execution_run_id\x18\x06 \x01(\t\x12+\n\x05links\x18\x07 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link"$\n"TerminateWorkflowExecutionResponse"z\n\x1e\x44\x65leteWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution"!\n\x1f\x44\x65leteWorkflowExecutionResponse"\xc9\x02\n!ListOpenWorkflowExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x19\n\x11maximum_page_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\x42\n\x11start_time_filter\x18\x04 \x01(\x0b\x32\'.temporal.api.filter.v1.StartTimeFilter\x12K\n\x10\x65xecution_filter\x18\x05 \x01(\x0b\x32/.temporal.api.filter.v1.WorkflowExecutionFilterH\x00\x12\x41\n\x0btype_filter\x18\x06 \x01(\x0b\x32*.temporal.api.filter.v1.WorkflowTypeFilterH\x00\x42\t\n\x07\x66ilters"\x82\x01\n"ListOpenWorkflowExecutionsResponse\x12\x43\n\nexecutions\x18\x01 \x03(\x0b\x32/.temporal.api.workflow.v1.WorkflowExecutionInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"\x8a\x03\n#ListClosedWorkflowExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x19\n\x11maximum_page_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\x42\n\x11start_time_filter\x18\x04 \x01(\x0b\x32\'.temporal.api.filter.v1.StartTimeFilter\x12K\n\x10\x65xecution_filter\x18\x05 \x01(\x0b\x32/.temporal.api.filter.v1.WorkflowExecutionFilterH\x00\x12\x41\n\x0btype_filter\x18\x06 \x01(\x0b\x32*.temporal.api.filter.v1.WorkflowTypeFilterH\x00\x12=\n\rstatus_filter\x18\x07 \x01(\x0b\x32$.temporal.api.filter.v1.StatusFilterH\x00\x42\t\n\x07\x66ilters"\x84\x01\n$ListClosedWorkflowExecutionsResponse\x12\x43\n\nexecutions\x18\x01 \x03(\x0b\x32/.temporal.api.workflow.v1.WorkflowExecutionInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"m\n\x1dListWorkflowExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t"~\n\x1eListWorkflowExecutionsResponse\x12\x43\n\nexecutions\x18\x01 \x03(\x0b\x32/.temporal.api.workflow.v1.WorkflowExecutionInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"u\n%ListArchivedWorkflowExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t"\x86\x01\n&ListArchivedWorkflowExecutionsResponse\x12\x43\n\nexecutions\x18\x01 \x03(\x0b\x32/.temporal.api.workflow.v1.WorkflowExecutionInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"m\n\x1dScanWorkflowExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t"~\n\x1eScanWorkflowExecutionsResponse\x12\x43\n\nexecutions\x18\x01 \x03(\x0b\x32/.temporal.api.workflow.v1.WorkflowExecutionInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"B\n\x1e\x43ountWorkflowExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\r\n\x05query\x18\x02 \x01(\t"\xed\x01\n\x1f\x43ountWorkflowExecutionsResponse\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12\x61\n\x06groups\x18\x02 \x03(\x0b\x32Q.temporal.api.workflowservice.v1.CountWorkflowExecutionsResponse.AggregationGroup\x1aX\n\x10\x41ggregationGroup\x12\x35\n\x0cgroup_values\x18\x01 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\r\n\x05\x63ount\x18\x02 \x01(\x03"\x1c\n\x1aGetSearchAttributesRequest"\xc9\x01\n\x1bGetSearchAttributesResponse\x12T\n\x04keys\x18\x01 \x03(\x0b\x32\x46.temporal.api.workflowservice.v1.GetSearchAttributesResponse.KeysEntry\x1aT\n\tKeysEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x36\n\x05value\x18\x02 \x01(\x0e\x32\'.temporal.api.enums.v1.IndexedValueType:\x02\x38\x01"\xe9\x02\n RespondQueryTaskCompletedRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12>\n\x0e\x63ompleted_type\x18\x02 \x01(\x0e\x32&.temporal.api.enums.v1.QueryResultType\x12\x36\n\x0cquery_result\x18\x03 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x15\n\rerror_message\x18\x04 \x01(\t\x12\x11\n\tnamespace\x18\x06 \x01(\t\x12\x31\n\x07\x66\x61ilure\x18\x07 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12=\n\x05\x63\x61use\x18\x08 \x01(\x0e\x32..temporal.api.enums.v1.WorkflowTaskFailedCause\x12\x17\n\x0fpoller_group_id\x18\t \x01(\tJ\x04\x08\x05\x10\x06"#\n!RespondQueryTaskCompletedResponse"n\n\x1bResetStickyTaskQueueRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution"\x1e\n\x1cResetStickyTaskQueueResponse"\x9b\x02\n\x15ShutdownWorkerRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x19\n\x11sticky_task_queue\x18\x02 \x01(\t\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x0e\n\x06reason\x18\x04 \x01(\t\x12\x41\n\x10worker_heartbeat\x18\x05 \x01(\x0b\x32\'.temporal.api.worker.v1.WorkerHeartbeat\x12\x1b\n\x13worker_instance_key\x18\x06 \x01(\t\x12\x12\n\ntask_queue\x18\x07 \x01(\t\x12>\n\x10task_queue_types\x18\x08 \x03(\x0e\x32$.temporal.api.enums.v1.TaskQueueType"\x18\n\x16ShutdownWorkerResponse"\xe9\x01\n\x14QueryWorkflowRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x33\n\x05query\x18\x03 \x01(\x0b\x32$.temporal.api.query.v1.WorkflowQuery\x12K\n\x16query_reject_condition\x18\x04 \x01(\x0e\x32+.temporal.api.enums.v1.QueryRejectCondition"\x8d\x01\n\x15QueryWorkflowResponse\x12\x36\n\x0cquery_result\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12<\n\x0equery_rejected\x18\x02 \x01(\x0b\x32$.temporal.api.query.v1.QueryRejected"s\n DescribeWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution"\x99\x05\n!DescribeWorkflowExecutionResponse\x12K\n\x10\x65xecution_config\x18\x01 \x01(\x0b\x32\x31.temporal.api.workflow.v1.WorkflowExecutionConfig\x12P\n\x17workflow_execution_info\x18\x02 \x01(\x0b\x32/.temporal.api.workflow.v1.WorkflowExecutionInfo\x12I\n\x12pending_activities\x18\x03 \x03(\x0b\x32-.temporal.api.workflow.v1.PendingActivityInfo\x12M\n\x10pending_children\x18\x04 \x03(\x0b\x32\x33.temporal.api.workflow.v1.PendingChildExecutionInfo\x12P\n\x15pending_workflow_task\x18\x05 \x01(\x0b\x32\x31.temporal.api.workflow.v1.PendingWorkflowTaskInfo\x12\x39\n\tcallbacks\x18\x06 \x03(\x0b\x32&.temporal.api.workflow.v1.CallbackInfo\x12U\n\x18pending_nexus_operations\x18\x07 \x03(\x0b\x32\x33.temporal.api.workflow.v1.PendingNexusOperationInfo\x12W\n\x16workflow_extended_info\x18\x08 \x01(\x0b\x32\x37.temporal.api.workflow.v1.WorkflowExecutionExtendedInfo"\x90\x04\n\x18\x44\x65scribeTaskQueueRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x38\n\ntask_queue\x18\x02 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12=\n\x0ftask_queue_type\x18\x03 \x01(\x0e\x32$.temporal.api.enums.v1.TaskQueueType\x12\x14\n\x0creport_stats\x18\x08 \x01(\x08\x12\x15\n\rreport_config\x18\x0b \x01(\x08\x12%\n\x19include_task_queue_status\x18\x04 \x01(\x08\x42\x02\x18\x01\x12\x42\n\x08\x61pi_mode\x18\x05 \x01(\x0e\x32,.temporal.api.enums.v1.DescribeTaskQueueModeB\x02\x18\x01\x12J\n\x08versions\x18\x06 \x01(\x0b\x32\x34.temporal.api.taskqueue.v1.TaskQueueVersionSelectionB\x02\x18\x01\x12\x42\n\x10task_queue_types\x18\x07 \x03(\x0e\x32$.temporal.api.enums.v1.TaskQueueTypeB\x02\x18\x01\x12\x1a\n\x0ereport_pollers\x18\t \x01(\x08\x42\x02\x18\x01\x12$\n\x18report_task_reachability\x18\n \x01(\x08\x42\x02\x18\x01"\xec\x07\n\x19\x44\x65scribeTaskQueueResponse\x12\x36\n\x07pollers\x18\x01 \x03(\x0b\x32%.temporal.api.taskqueue.v1.PollerInfo\x12\x38\n\x05stats\x18\x05 \x01(\x0b\x32).temporal.api.taskqueue.v1.TaskQueueStats\x12q\n\x15stats_by_priority_key\x18\x08 \x03(\x0b\x32R.temporal.api.workflowservice.v1.DescribeTaskQueueResponse.StatsByPriorityKeyEntry\x12K\n\x0fversioning_info\x18\x04 \x01(\x0b\x32\x32.temporal.api.taskqueue.v1.TaskQueueVersioningInfo\x12:\n\x06\x63onfig\x18\x06 \x01(\x0b\x32*.temporal.api.taskqueue.v1.TaskQueueConfig\x12k\n\x14\x65\x66\x66\x65\x63tive_rate_limit\x18\x07 \x01(\x0b\x32M.temporal.api.workflowservice.v1.DescribeTaskQueueResponse.EffectiveRateLimit\x12I\n\x11task_queue_status\x18\x02 \x01(\x0b\x32*.temporal.api.taskqueue.v1.TaskQueueStatusB\x02\x18\x01\x12g\n\rversions_info\x18\x03 \x03(\x0b\x32L.temporal.api.workflowservice.v1.DescribeTaskQueueResponse.VersionsInfoEntryB\x02\x18\x01\x1a\x64\n\x17StatsByPriorityKeyEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\x38\n\x05value\x18\x02 \x01(\x0b\x32).temporal.api.taskqueue.v1.TaskQueueStats:\x02\x38\x01\x1at\n\x12\x45\x66\x66\x65\x63tiveRateLimit\x12\x1b\n\x13requests_per_second\x18\x01 \x01(\x02\x12\x41\n\x11rate_limit_source\x18\x02 \x01(\x0e\x32&.temporal.api.enums.v1.RateLimitSource\x1a\x64\n\x11VersionsInfoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12>\n\x05value\x18\x02 \x01(\x0b\x32/.temporal.api.taskqueue.v1.TaskQueueVersionInfo:\x02\x38\x01"\x17\n\x15GetClusterInfoRequest"\xd1\x03\n\x16GetClusterInfoResponse\x12h\n\x11supported_clients\x18\x01 \x03(\x0b\x32M.temporal.api.workflowservice.v1.GetClusterInfoResponse.SupportedClientsEntry\x12\x16\n\x0eserver_version\x18\x02 \x01(\t\x12\x12\n\ncluster_id\x18\x03 \x01(\t\x12:\n\x0cversion_info\x18\x04 \x01(\x0b\x32$.temporal.api.version.v1.VersionInfo\x12\x14\n\x0c\x63luster_name\x18\x05 \x01(\t\x12\x1b\n\x13history_shard_count\x18\x06 \x01(\x05\x12\x19\n\x11persistence_store\x18\x07 \x01(\t\x12\x18\n\x10visibility_store\x18\x08 \x01(\t\x12 \n\x18initial_failover_version\x18\t \x01(\x03\x12"\n\x1a\x66\x61ilover_version_increment\x18\n \x01(\x03\x1a\x37\n\x15SupportedClientsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"\x16\n\x14GetSystemInfoRequest"\x97\x04\n\x15GetSystemInfoResponse\x12\x16\n\x0eserver_version\x18\x01 \x01(\t\x12Y\n\x0c\x63\x61pabilities\x18\x02 \x01(\x0b\x32\x43.temporal.api.workflowservice.v1.GetSystemInfoResponse.Capabilities\x1a\x8a\x03\n\x0c\x43\x61pabilities\x12\x1f\n\x17signal_and_query_header\x18\x01 \x01(\x08\x12&\n\x1einternal_error_differentiation\x18\x02 \x01(\x08\x12*\n"activity_failure_include_heartbeat\x18\x03 \x01(\x08\x12\x1a\n\x12supports_schedules\x18\x04 \x01(\x08\x12"\n\x1a\x65ncoded_failure_attributes\x18\x05 \x01(\x08\x12!\n\x19\x62uild_id_based_versioning\x18\x06 \x01(\x08\x12\x13\n\x0bupsert_memo\x18\x07 \x01(\x08\x12\x1c\n\x14\x65\x61ger_workflow_start\x18\x08 \x01(\x08\x12\x14\n\x0csdk_metadata\x18\t \x01(\x08\x12\'\n\x1f\x63ount_group_by_execution_status\x18\n \x01(\x08\x12\r\n\x05nexus\x18\x0b \x01(\x08\x12!\n\x19server_scaled_deployments\x18\x0c \x01(\x08"m\n\x1eListTaskQueuePartitionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x38\n\ntask_queue\x18\x02 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue"\xdf\x01\n\x1fListTaskQueuePartitionsResponse\x12]\n\x1e\x61\x63tivity_task_queue_partitions\x18\x01 \x03(\x0b\x32\x35.temporal.api.taskqueue.v1.TaskQueuePartitionMetadata\x12]\n\x1eworkflow_task_queue_partitions\x18\x02 \x03(\x0b\x32\x35.temporal.api.taskqueue.v1.TaskQueuePartitionMetadata"\xcc\x02\n\x15\x43reateScheduleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bschedule_id\x18\x02 \x01(\t\x12\x34\n\x08schedule\x18\x03 \x01(\x0b\x32".temporal.api.schedule.v1.Schedule\x12>\n\rinitial_patch\x18\x04 \x01(\x0b\x32\'.temporal.api.schedule.v1.SchedulePatch\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x12\n\nrequest_id\x18\x06 \x01(\t\x12*\n\x04memo\x18\x07 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x08 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes"0\n\x16\x43reateScheduleResponse\x12\x16\n\x0e\x63onflict_token\x18\x01 \x01(\x0c"A\n\x17\x44\x65scribeScheduleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bschedule_id\x18\x02 \x01(\t"\x8f\x02\n\x18\x44\x65scribeScheduleResponse\x12\x34\n\x08schedule\x18\x01 \x01(\x0b\x32".temporal.api.schedule.v1.Schedule\x12\x34\n\x04info\x18\x02 \x01(\x0b\x32&.temporal.api.schedule.v1.ScheduleInfo\x12*\n\x04memo\x18\x03 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x04 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x16\n\x0e\x63onflict_token\x18\x05 \x01(\x0c"\xa4\x02\n\x15UpdateScheduleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bschedule_id\x18\x02 \x01(\t\x12\x34\n\x08schedule\x18\x03 \x01(\x0b\x32".temporal.api.schedule.v1.Schedule\x12\x16\n\x0e\x63onflict_token\x18\x04 \x01(\x0c\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x12\n\nrequest_id\x18\x06 \x01(\t\x12\x43\n\x11search_attributes\x18\x07 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12*\n\x04memo\x18\x08 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo"\x18\n\x16UpdateScheduleResponse"\x9c\x01\n\x14PatchScheduleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bschedule_id\x18\x02 \x01(\t\x12\x36\n\x05patch\x18\x03 \x01(\x0b\x32\'.temporal.api.schedule.v1.SchedulePatch\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t"\x17\n\x15PatchScheduleResponse"\xa8\x01\n ListScheduleMatchingTimesRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bschedule_id\x18\x02 \x01(\t\x12.\n\nstart_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08\x65nd_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"S\n!ListScheduleMatchingTimesResponse\x12.\n\nstart_time\x18\x01 \x03(\x0b\x32\x1a.google.protobuf.Timestamp"Q\n\x15\x44\x65leteScheduleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bschedule_id\x18\x02 \x01(\t\x12\x10\n\x08identity\x18\x03 \x01(\t"\x18\n\x16\x44\x65leteScheduleResponse"l\n\x14ListSchedulesRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x19\n\x11maximum_page_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t"p\n\x15ListSchedulesResponse\x12>\n\tschedules\x18\x01 \x03(\x0b\x32+.temporal.api.schedule.v1.ScheduleListEntry\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"9\n\x15\x43ountSchedulesRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\r\n\x05query\x18\x02 \x01(\t"\xdb\x01\n\x16\x43ountSchedulesResponse\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12X\n\x06groups\x18\x02 \x03(\x0b\x32H.temporal.api.workflowservice.v1.CountSchedulesResponse.AggregationGroup\x1aX\n\x10\x41ggregationGroup\x12\x35\n\x0cgroup_values\x18\x01 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\r\n\x05\x63ount\x18\x02 \x01(\x03"\x86\x05\n\'UpdateWorkerBuildIdCompatibilityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x12\n\ntask_queue\x18\x02 \x01(\t\x12-\n#add_new_build_id_in_new_default_set\x18\x03 \x01(\tH\x00\x12\x87\x01\n\x1b\x61\x64\x64_new_compatible_build_id\x18\x04 \x01(\x0b\x32`.temporal.api.workflowservice.v1.UpdateWorkerBuildIdCompatibilityRequest.AddNewCompatibleVersionH\x00\x12!\n\x17promote_set_by_build_id\x18\x05 \x01(\tH\x00\x12%\n\x1bpromote_build_id_within_set\x18\x06 \x01(\tH\x00\x12h\n\nmerge_sets\x18\x07 \x01(\x0b\x32R.temporal.api.workflowservice.v1.UpdateWorkerBuildIdCompatibilityRequest.MergeSetsH\x00\x1ao\n\x17\x41\x64\x64NewCompatibleVersion\x12\x14\n\x0cnew_build_id\x18\x01 \x01(\t\x12$\n\x1c\x65xisting_compatible_build_id\x18\x02 \x01(\t\x12\x18\n\x10make_set_default\x18\x03 \x01(\x08\x1aI\n\tMergeSets\x12\x1c\n\x14primary_set_build_id\x18\x01 \x01(\t\x12\x1e\n\x16secondary_set_build_id\x18\x02 \x01(\tB\x0b\n\toperation"@\n(UpdateWorkerBuildIdCompatibilityResponseJ\x04\x08\x01\x10\x02R\x0eversion_set_id"_\n$GetWorkerBuildIdCompatibilityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x12\n\ntask_queue\x18\x02 \x01(\t\x12\x10\n\x08max_sets\x18\x03 \x01(\x05"t\n%GetWorkerBuildIdCompatibilityResponse\x12K\n\x12major_version_sets\x18\x01 \x03(\x0b\x32/.temporal.api.taskqueue.v1.CompatibleVersionSet"\xb5\r\n"UpdateWorkerVersioningRulesRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x12\n\ntask_queue\x18\x02 \x01(\t\x12\x16\n\x0e\x63onflict_token\x18\x03 \x01(\x0c\x12\x81\x01\n\x16insert_assignment_rule\x18\x04 \x01(\x0b\x32_.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.InsertBuildIdAssignmentRuleH\x00\x12\x83\x01\n\x17replace_assignment_rule\x18\x05 \x01(\x0b\x32`.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.ReplaceBuildIdAssignmentRuleH\x00\x12\x81\x01\n\x16\x64\x65lete_assignment_rule\x18\x06 \x01(\x0b\x32_.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.DeleteBuildIdAssignmentRuleH\x00\x12\x8c\x01\n\x1c\x61\x64\x64_compatible_redirect_rule\x18\x07 \x01(\x0b\x32\x64.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.AddCompatibleBuildIdRedirectRuleH\x00\x12\x94\x01\n replace_compatible_redirect_rule\x18\x08 \x01(\x0b\x32h.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.ReplaceCompatibleBuildIdRedirectRuleH\x00\x12\x92\x01\n\x1f\x64\x65lete_compatible_redirect_rule\x18\t \x01(\x0b\x32g.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.DeleteCompatibleBuildIdRedirectRuleH\x00\x12l\n\x0f\x63ommit_build_id\x18\n \x01(\x0b\x32Q.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.CommitBuildIdH\x00\x1aq\n\x1bInsertBuildIdAssignmentRule\x12\x12\n\nrule_index\x18\x01 \x01(\x05\x12>\n\x04rule\x18\x02 \x01(\x0b\x32\x30.temporal.api.taskqueue.v1.BuildIdAssignmentRule\x1a\x81\x01\n\x1cReplaceBuildIdAssignmentRule\x12\x12\n\nrule_index\x18\x01 \x01(\x05\x12>\n\x04rule\x18\x02 \x01(\x0b\x32\x30.temporal.api.taskqueue.v1.BuildIdAssignmentRule\x12\r\n\x05\x66orce\x18\x03 \x01(\x08\x1a@\n\x1b\x44\x65leteBuildIdAssignmentRule\x12\x12\n\nrule_index\x18\x01 \x01(\x05\x12\r\n\x05\x66orce\x18\x02 \x01(\x08\x1aj\n AddCompatibleBuildIdRedirectRule\x12\x46\n\x04rule\x18\x01 \x01(\x0b\x32\x38.temporal.api.taskqueue.v1.CompatibleBuildIdRedirectRule\x1an\n$ReplaceCompatibleBuildIdRedirectRule\x12\x46\n\x04rule\x18\x01 \x01(\x0b\x32\x38.temporal.api.taskqueue.v1.CompatibleBuildIdRedirectRule\x1a>\n#DeleteCompatibleBuildIdRedirectRule\x12\x17\n\x0fsource_build_id\x18\x01 \x01(\t\x1a\x37\n\rCommitBuildId\x12\x17\n\x0ftarget_build_id\x18\x01 \x01(\t\x12\r\n\x05\x66orce\x18\x02 \x01(\x08\x42\x0b\n\toperation"\xfc\x01\n#UpdateWorkerVersioningRulesResponse\x12U\n\x10\x61ssignment_rules\x18\x01 \x03(\x0b\x32;.temporal.api.taskqueue.v1.TimestampedBuildIdAssignmentRule\x12\x66\n\x19\x63ompatible_redirect_rules\x18\x02 \x03(\x0b\x32\x43.temporal.api.taskqueue.v1.TimestampedCompatibleBuildIdRedirectRule\x12\x16\n\x0e\x63onflict_token\x18\x03 \x01(\x0c"H\n\x1fGetWorkerVersioningRulesRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x12\n\ntask_queue\x18\x02 \x01(\t"\xf9\x01\n GetWorkerVersioningRulesResponse\x12U\n\x10\x61ssignment_rules\x18\x01 \x03(\x0b\x32;.temporal.api.taskqueue.v1.TimestampedBuildIdAssignmentRule\x12\x66\n\x19\x63ompatible_redirect_rules\x18\x02 \x03(\x0b\x32\x43.temporal.api.taskqueue.v1.TimestampedCompatibleBuildIdRedirectRule\x12\x16\n\x0e\x63onflict_token\x18\x03 \x01(\x0c"\x9c\x01\n GetWorkerTaskReachabilityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tbuild_ids\x18\x02 \x03(\t\x12\x13\n\x0btask_queues\x18\x03 \x03(\t\x12=\n\x0creachability\x18\x04 \x01(\x0e\x32\'.temporal.api.enums.v1.TaskReachability"r\n!GetWorkerTaskReachabilityResponse\x12M\n\x15\x62uild_id_reachability\x18\x01 \x03(\x0b\x32..temporal.api.taskqueue.v1.BuildIdReachability"\x85\x02\n\x1eUpdateWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x1e\n\x16\x66irst_execution_run_id\x18\x03 \x01(\t\x12\x37\n\x0bwait_policy\x18\x04 \x01(\x0b\x32".temporal.api.update.v1.WaitPolicy\x12\x30\n\x07request\x18\x05 \x01(\x0b\x32\x1f.temporal.api.update.v1.Request"\x83\x02\n\x1fUpdateWorkflowExecutionResponse\x12\x35\n\nupdate_ref\x18\x01 \x01(\x0b\x32!.temporal.api.update.v1.UpdateRef\x12\x30\n\x07outcome\x18\x02 \x01(\x0b\x32\x1f.temporal.api.update.v1.Outcome\x12K\n\x05stage\x18\x03 \x01(\x0e\x32<.temporal.api.enums.v1.UpdateWorkflowExecutionLifecycleStage\x12*\n\x04link\x18\x04 \x01(\x0b\x32\x1c.temporal.api.common.v1.Link"\xf4\x07\n\x1aStartBatchOperationRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x18\n\x10visibility_query\x18\x02 \x01(\t\x12\x0e\n\x06job_id\x18\x03 \x01(\t\x12\x0e\n\x06reason\x18\x04 \x01(\t\x12=\n\nexecutions\x18\x05 \x03(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12!\n\x19max_operations_per_second\x18\x06 \x01(\x02\x12Q\n\x15termination_operation\x18\n \x01(\x0b\x32\x30.temporal.api.batch.v1.BatchOperationTerminationH\x00\x12G\n\x10signal_operation\x18\x0b \x01(\x0b\x32+.temporal.api.batch.v1.BatchOperationSignalH\x00\x12S\n\x16\x63\x61ncellation_operation\x18\x0c \x01(\x0b\x32\x31.temporal.api.batch.v1.BatchOperationCancellationH\x00\x12K\n\x12\x64\x65letion_operation\x18\r \x01(\x0b\x32-.temporal.api.batch.v1.BatchOperationDeletionH\x00\x12\x45\n\x0freset_operation\x18\x0e \x01(\x0b\x32*.temporal.api.batch.v1.BatchOperationResetH\x00\x12p\n!update_workflow_options_operation\x18\x0f \x01(\x0b\x32\x43.temporal.api.batch.v1.BatchOperationUpdateWorkflowExecutionOptionsH\x00\x12^\n\x1cunpause_activities_operation\x18\x10 \x01(\x0b\x32\x36.temporal.api.batch.v1.BatchOperationUnpauseActivitiesH\x00\x12Z\n\x1areset_activities_operation\x18\x11 \x01(\x0b\x32\x34.temporal.api.batch.v1.BatchOperationResetActivitiesH\x00\x12g\n!update_activity_options_operation\x18\x12 \x01(\x0b\x32:.temporal.api.batch.v1.BatchOperationUpdateActivityOptionsH\x00\x42\x0b\n\toperation"\x1d\n\x1bStartBatchOperationResponse"`\n\x19StopBatchOperationRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x0e\n\x06job_id\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t"\x1c\n\x1aStopBatchOperationResponse"B\n\x1d\x44\x65scribeBatchOperationRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x0e\n\x06job_id\x18\x02 \x01(\t"\x92\x03\n\x1e\x44\x65scribeBatchOperationResponse\x12\x41\n\x0eoperation_type\x18\x01 \x01(\x0e\x32).temporal.api.enums.v1.BatchOperationType\x12\x0e\n\x06job_id\x18\x02 \x01(\t\x12\x39\n\x05state\x18\x03 \x01(\x0e\x32*.temporal.api.enums.v1.BatchOperationState\x12.\n\nstart_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nclose_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x1d\n\x15total_operation_count\x18\x06 \x01(\x03\x12 \n\x18\x63omplete_operation_count\x18\x07 \x01(\x03\x12\x1f\n\x17\x66\x61ilure_operation_count\x18\x08 \x01(\x03\x12\x10\n\x08identity\x18\t \x01(\t\x12\x0e\n\x06reason\x18\n \x01(\t"[\n\x1aListBatchOperationsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c"y\n\x1bListBatchOperationsResponse\x12\x41\n\x0eoperation_info\x18\x01 \x03(\x0b\x32).temporal.api.batch.v1.BatchOperationInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"\xb9\x01\n"PollWorkflowExecutionUpdateRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x35\n\nupdate_ref\x18\x02 \x01(\x0b\x32!.temporal.api.update.v1.UpdateRef\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x37\n\x0bwait_policy\x18\x04 \x01(\x0b\x32".temporal.api.update.v1.WaitPolicy"\xdb\x01\n#PollWorkflowExecutionUpdateResponse\x12\x30\n\x07outcome\x18\x01 \x01(\x0b\x32\x1f.temporal.api.update.v1.Outcome\x12K\n\x05stage\x18\x02 \x01(\x0e\x32<.temporal.api.enums.v1.UpdateWorkflowExecutionLifecycleStage\x12\x35\n\nupdate_ref\x18\x03 \x01(\x0b\x32!.temporal.api.update.v1.UpdateRef"\xa0\x03\n\x19PollNexusTaskQueueRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x38\n\ntask_queue\x18\x03 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12\x17\n\x0fpoller_group_id\x18\t \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x1b\n\x13worker_instance_key\x18\x08 \x01(\t\x12Z\n\x1bworker_version_capabilities\x18\x04 \x01(\x0b\x32\x31.temporal.api.common.v1.WorkerVersionCapabilitiesB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x06 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions\x12\x41\n\x10worker_heartbeat\x18\x07 \x03(\x0b\x32\'.temporal.api.worker.v1.WorkerHeartbeat"\x95\x02\n\x1aPollNexusTaskQueueResponse\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12/\n\x07request\x18\x02 \x01(\x0b\x32\x1e.temporal.api.nexus.v1.Request\x12Q\n\x17poller_scaling_decision\x18\x03 \x01(\x0b\x32\x30.temporal.api.taskqueue.v1.PollerScalingDecision\x12\x17\n\x0fpoller_group_id\x18\x04 \x01(\t\x12\x46\n\x12poller_group_infos\x18\x05 \x03(\x0b\x32*.temporal.api.taskqueue.v1.PollerGroupInfo"\xa7\x01\n RespondNexusTaskCompletedRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\ntask_token\x18\x03 \x01(\x0c\x12\x31\n\x08response\x18\x04 \x01(\x0b\x32\x1f.temporal.api.nexus.v1.Response\x12\x17\n\x0fpoller_group_id\x18\x05 \x01(\t"#\n!RespondNexusTaskCompletedResponse"\xdc\x01\n\x1dRespondNexusTaskFailedRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\ntask_token\x18\x03 \x01(\x0c\x12\x36\n\x05\x65rror\x18\x04 \x01(\x0b\x32#.temporal.api.nexus.v1.HandlerErrorB\x02\x18\x01\x12\x31\n\x07\x66\x61ilure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x17\n\x0fpoller_group_id\x18\x06 \x01(\t" \n\x1eRespondNexusTaskFailedResponse"\xf4\x02\n\x1c\x45xecuteMultiOperationRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12[\n\noperations\x18\x02 \x03(\x0b\x32G.temporal.api.workflowservice.v1.ExecuteMultiOperationRequest.Operation\x12\x13\n\x0bresource_id\x18\x03 \x01(\t\x1a\xce\x01\n\tOperation\x12X\n\x0estart_workflow\x18\x01 \x01(\x0b\x32>.temporal.api.workflowservice.v1.StartWorkflowExecutionRequestH\x00\x12Z\n\x0fupdate_workflow\x18\x02 \x01(\x0b\x32?.temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequestH\x00\x42\x0b\n\toperation"\xcc\x02\n\x1d\x45xecuteMultiOperationResponse\x12Z\n\tresponses\x18\x01 \x03(\x0b\x32G.temporal.api.workflowservice.v1.ExecuteMultiOperationResponse.Response\x1a\xce\x01\n\x08Response\x12Y\n\x0estart_workflow\x18\x01 \x01(\x0b\x32?.temporal.api.workflowservice.v1.StartWorkflowExecutionResponseH\x00\x12[\n\x0fupdate_workflow\x18\x02 \x01(\x0b\x32@.temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponseH\x00\x42\n\n\x08response"\xd0\x02\n\x1cUpdateActivityOptionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x43\n\x10\x61\x63tivity_options\x18\x04 \x01(\x0b\x32).temporal.api.activity.v1.ActivityOptions\x12/\n\x0bupdate_mask\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.FieldMask\x12\x0c\n\x02id\x18\x06 \x01(\tH\x00\x12\x0e\n\x04type\x18\x07 \x01(\tH\x00\x12\x13\n\tmatch_all\x18\t \x01(\x08H\x00\x12\x18\n\x10restore_original\x18\x08 \x01(\x08\x42\n\n\x08\x61\x63tivity"\xab\x02\n%UpdateActivityExecutionOptionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x03 \x01(\t\x12\x0e\n\x06run_id\x18\x04 \x01(\t\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x43\n\x10\x61\x63tivity_options\x18\x06 \x01(\x0b\x32).temporal.api.activity.v1.ActivityOptions\x12/\n\x0bupdate_mask\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.FieldMask\x12\x18\n\x10restore_original\x18\x08 \x01(\x08\x12\x13\n\x0bresource_id\x18\t \x01(\t"d\n\x1dUpdateActivityOptionsResponse\x12\x43\n\x10\x61\x63tivity_options\x18\x01 \x01(\x0b\x32).temporal.api.activity.v1.ActivityOptions"m\n&UpdateActivityExecutionOptionsResponse\x12\x43\n\x10\x61\x63tivity_options\x18\x01 \x01(\x0b\x32).temporal.api.activity.v1.ActivityOptions"\xc7\x01\n\x14PauseActivityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x0c\n\x02id\x18\x04 \x01(\tH\x00\x12\x0e\n\x04type\x18\x05 \x01(\tH\x00\x12\x0e\n\x06reason\x18\x06 \x01(\t\x12\x12\n\nrequest_id\x18\x07 \x01(\tB\n\n\x08\x61\x63tivity"\xb7\x01\n\x1dPauseActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x03 \x01(\t\x12\x0e\n\x06run_id\x18\x04 \x01(\t\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x0e\n\x06reason\x18\x06 \x01(\t\x12\x13\n\x0bresource_id\x18\x07 \x01(\t\x12\x12\n\nrequest_id\x18\x08 \x01(\t"\x17\n\x15PauseActivityResponse" \n\x1ePauseActivityExecutionResponse"\x98\x02\n\x16UnpauseActivityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x0c\n\x02id\x18\x04 \x01(\tH\x00\x12\x0e\n\x04type\x18\x05 \x01(\tH\x00\x12\x15\n\x0bunpause_all\x18\x06 \x01(\x08H\x00\x12\x16\n\x0ereset_attempts\x18\x07 \x01(\x08\x12\x17\n\x0freset_heartbeat\x18\x08 \x01(\x08\x12)\n\x06jitter\x18\t \x01(\x0b\x32\x19.google.protobuf.DurationB\n\n\x08\x61\x63tivity"\x81\x02\n\x1fUnpauseActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x03 \x01(\t\x12\x0e\n\x06run_id\x18\x04 \x01(\t\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x16\n\x0ereset_attempts\x18\x06 \x01(\x08\x12\x17\n\x0freset_heartbeat\x18\x07 \x01(\x08\x12\x0e\n\x06reason\x18\x08 \x01(\t\x12)\n\x06jitter\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x13\n\x0bresource_id\x18\n \x01(\t"\x19\n\x17UnpauseActivityResponse""\n UnpauseActivityExecutionResponse"\xb3\x02\n\x14ResetActivityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x0c\n\x02id\x18\x04 \x01(\tH\x00\x12\x0e\n\x04type\x18\x05 \x01(\tH\x00\x12\x13\n\tmatch_all\x18\n \x01(\x08H\x00\x12\x17\n\x0freset_heartbeat\x18\x06 \x01(\x08\x12\x13\n\x0bkeep_paused\x18\x07 \x01(\x08\x12)\n\x06jitter\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12 \n\x18restore_original_options\x18\t \x01(\x08\x42\n\n\x08\x61\x63tivity"\x8e\x02\n\x1dResetActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x03 \x01(\t\x12\x0e\n\x06run_id\x18\x04 \x01(\t\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x17\n\x0freset_heartbeat\x18\x06 \x01(\x08\x12\x13\n\x0bkeep_paused\x18\x07 \x01(\x08\x12)\n\x06jitter\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12 \n\x18restore_original_options\x18\t \x01(\x08\x12\x13\n\x0bresource_id\x18\n \x01(\t"\x17\n\x15ResetActivityResponse" \n\x1eResetActivityExecutionResponse"\x9c\x02\n%UpdateWorkflowExecutionOptionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12V\n\x1aworkflow_execution_options\x18\x03 \x01(\x0b\x32\x32.temporal.api.workflow.v1.WorkflowExecutionOptions\x12/\n\x0bupdate_mask\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.FieldMask\x12\x10\n\x08identity\x18\x05 \x01(\t"\x80\x01\n&UpdateWorkflowExecutionOptionsResponse\x12V\n\x1aworkflow_execution_options\x18\x01 \x01(\x0b\x32\x32.temporal.api.workflow.v1.WorkflowExecutionOptions"j\n\x19\x44\x65scribeDeploymentRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12:\n\ndeployment\x18\x02 \x01(\x0b\x32&.temporal.api.deployment.v1.Deployment"a\n\x1a\x44\x65scribeDeploymentResponse\x12\x43\n\x0f\x64\x65ployment_info\x18\x01 \x01(\x0b\x32*.temporal.api.deployment.v1.DeploymentInfo"\xc2\x01\n&DescribeWorkerDeploymentVersionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x07version\x18\x02 \x01(\tB\x02\x18\x01\x12O\n\x12\x64\x65ployment_version\x18\x03 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x1f\n\x17report_task_queue_stats\x18\x04 \x01(\x08"\x8c\x05\n\'DescribeWorkerDeploymentVersionResponse\x12_\n\x1eworker_deployment_version_info\x18\x01 \x01(\x0b\x32\x37.temporal.api.deployment.v1.WorkerDeploymentVersionInfo\x12v\n\x13version_task_queues\x18\x02 \x03(\x0b\x32Y.temporal.api.workflowservice.v1.DescribeWorkerDeploymentVersionResponse.VersionTaskQueue\x1a\x87\x03\n\x10VersionTaskQueue\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x32\n\x04type\x18\x02 \x01(\x0e\x32$.temporal.api.enums.v1.TaskQueueType\x12\x38\n\x05stats\x18\x03 \x01(\x0b\x32).temporal.api.taskqueue.v1.TaskQueueStats\x12\x90\x01\n\x15stats_by_priority_key\x18\x04 \x03(\x0b\x32q.temporal.api.workflowservice.v1.DescribeWorkerDeploymentVersionResponse.VersionTaskQueue.StatsByPriorityKeyEntry\x1a\x64\n\x17StatsByPriorityKeyEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\x38\n\x05value\x18\x02 \x01(\x0b\x32).temporal.api.taskqueue.v1.TaskQueueStats:\x02\x38\x01"M\n\x1f\x44\x65scribeWorkerDeploymentRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t"\x8c\x01\n DescribeWorkerDeploymentResponse\x12\x16\n\x0e\x63onflict_token\x18\x01 \x01(\x0c\x12P\n\x16worker_deployment_info\x18\x02 \x01(\x0b\x32\x30.temporal.api.deployment.v1.WorkerDeploymentInfo"l\n\x16ListDeploymentsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\x13\n\x0bseries_name\x18\x04 \x01(\t"w\n\x17ListDeploymentsResponse\x12\x17\n\x0fnext_page_token\x18\x01 \x01(\x0c\x12\x43\n\x0b\x64\x65ployments\x18\x02 \x03(\x0b\x32..temporal.api.deployment.v1.DeploymentListInfo"\xcd\x01\n\x1bSetCurrentDeploymentRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12:\n\ndeployment\x18\x02 \x01(\x0b\x32&.temporal.api.deployment.v1.Deployment\x12\x10\n\x08identity\x18\x03 \x01(\t\x12M\n\x0fupdate_metadata\x18\x04 \x01(\x0b\x32\x34.temporal.api.deployment.v1.UpdateDeploymentMetadata"\xb9\x01\n\x1cSetCurrentDeploymentResponse\x12K\n\x17\x63urrent_deployment_info\x18\x01 \x01(\x0b\x32*.temporal.api.deployment.v1.DeploymentInfo\x12L\n\x18previous_deployment_info\x18\x02 \x01(\x0b\x32*.temporal.api.deployment.v1.DeploymentInfo"\xe5\x01\n(SetWorkerDeploymentCurrentVersionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t\x12\x13\n\x07version\x18\x03 \x01(\tB\x02\x18\x01\x12\x10\n\x08\x62uild_id\x18\x07 \x01(\t\x12\x16\n\x0e\x63onflict_token\x18\x04 \x01(\x0c\x12\x10\n\x08identity\x18\x05 \x01(\t\x12"\n\x1aignore_missing_task_queues\x18\x06 \x01(\x08\x12\x18\n\x10\x61llow_no_pollers\x18\t \x01(\x08"\xbf\x01\n)SetWorkerDeploymentCurrentVersionResponse\x12\x16\n\x0e\x63onflict_token\x18\x01 \x01(\x0c\x12\x1c\n\x10previous_version\x18\x02 \x01(\tB\x02\x18\x01\x12\\\n\x1bprevious_deployment_version\x18\x03 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersionB\x02\x18\x01"\xf9\x01\n(SetWorkerDeploymentRampingVersionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t\x12\x13\n\x07version\x18\x03 \x01(\tB\x02\x18\x01\x12\x10\n\x08\x62uild_id\x18\x08 \x01(\t\x12\x12\n\npercentage\x18\x04 \x01(\x02\x12\x16\n\x0e\x63onflict_token\x18\x05 \x01(\x0c\x12\x10\n\x08identity\x18\x06 \x01(\t\x12"\n\x1aignore_missing_task_queues\x18\x07 \x01(\x08\x12\x18\n\x10\x61llow_no_pollers\x18\n \x01(\x08"\xe0\x01\n)SetWorkerDeploymentRampingVersionResponse\x12\x16\n\x0e\x63onflict_token\x18\x01 \x01(\x0c\x12\x1c\n\x10previous_version\x18\x02 \x01(\tB\x02\x18\x01\x12\\\n\x1bprevious_deployment_version\x18\x04 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersionB\x02\x18\x01\x12\x1f\n\x13previous_percentage\x18\x03 \x01(\x02\x42\x02\x18\x01"q\n\x1d\x43reateWorkerDeploymentRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t"8\n\x1e\x43reateWorkerDeploymentResponse\x12\x16\n\x0e\x63onflict_token\x18\x01 \x01(\x0c"]\n\x1cListWorkerDeploymentsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c"\x9f\x05\n\x1dListWorkerDeploymentsResponse\x12\x17\n\x0fnext_page_token\x18\x01 \x01(\x0c\x12r\n\x12worker_deployments\x18\x02 \x03(\x0b\x32V.temporal.api.workflowservice.v1.ListWorkerDeploymentsResponse.WorkerDeploymentSummary\x1a\xf0\x03\n\x17WorkerDeploymentSummary\x12\x0c\n\x04name\x18\x01 \x01(\t\x12/\n\x0b\x63reate_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x41\n\x0erouting_config\x18\x03 \x01(\x0b\x32).temporal.api.deployment.v1.RoutingConfig\x12o\n\x16latest_version_summary\x18\x04 \x01(\x0b\x32O.temporal.api.deployment.v1.WorkerDeploymentInfo.WorkerDeploymentVersionSummary\x12p\n\x17\x63urrent_version_summary\x18\x05 \x01(\x0b\x32O.temporal.api.deployment.v1.WorkerDeploymentInfo.WorkerDeploymentVersionSummary\x12p\n\x17ramping_version_summary\x18\x06 \x01(\x0b\x32O.temporal.api.deployment.v1.WorkerDeploymentInfo.WorkerDeploymentVersionSummary"\xf0\x01\n$CreateWorkerDeploymentVersionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12O\n\x12\x64\x65ployment_version\x18\x02 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12>\n\x0e\x63ompute_config\x18\x04 \x01(\x0b\x32&.temporal.api.compute.v1.ComputeConfig\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t"\'\n%CreateWorkerDeploymentVersionResponse"\xc8\x01\n$DeleteWorkerDeploymentVersionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x07version\x18\x02 \x01(\tB\x02\x18\x01\x12O\n\x12\x64\x65ployment_version\x18\x05 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x15\n\rskip_drainage\x18\x03 \x01(\x08\x12\x10\n\x08identity\x18\x04 \x01(\t"\'\n%DeleteWorkerDeploymentVersionResponse"]\n\x1d\x44\x65leteWorkerDeploymentRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t\x12\x10\n\x08identity\x18\x03 \x01(\t" \n\x1e\x44\x65leteWorkerDeploymentResponse"\x84\x04\n1UpdateWorkerDeploymentVersionComputeConfigRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12O\n\x12\x64\x65ployment_version\x18\x02 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x99\x01\n\x1d\x63ompute_config_scaling_groups\x18\x06 \x03(\x0b\x32r.temporal.api.workflowservice.v1.UpdateWorkerDeploymentVersionComputeConfigRequest.ComputeConfigScalingGroupsEntry\x12,\n$remove_compute_config_scaling_groups\x18\x07 \x03(\t\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x12\n\nrequest_id\x18\x04 \x01(\t\x1a{\n\x1f\x43omputeConfigScalingGroupsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12G\n\x05value\x18\x02 \x01(\x0b\x32\x38.temporal.api.compute.v1.ComputeConfigScalingGroupUpdate:\x02\x38\x01"4\n2UpdateWorkerDeploymentVersionComputeConfigResponse"\xf4\x03\n3ValidateWorkerDeploymentVersionComputeConfigRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12O\n\x12\x64\x65ployment_version\x18\x02 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x9b\x01\n\x1d\x63ompute_config_scaling_groups\x18\x06 \x03(\x0b\x32t.temporal.api.workflowservice.v1.ValidateWorkerDeploymentVersionComputeConfigRequest.ComputeConfigScalingGroupsEntry\x12,\n$remove_compute_config_scaling_groups\x18\x07 \x03(\t\x12\x10\n\x08identity\x18\x03 \x01(\t\x1a{\n\x1f\x43omputeConfigScalingGroupsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12G\n\x05value\x18\x02 \x01(\x0b\x32\x38.temporal.api.compute.v1.ComputeConfigScalingGroupUpdate:\x02\x38\x01"6\n4ValidateWorkerDeploymentVersionComputeConfigResponse"\xa2\x03\n,UpdateWorkerDeploymentVersionMetadataRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x07version\x18\x02 \x01(\tB\x02\x18\x01\x12O\n\x12\x64\x65ployment_version\x18\x05 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12x\n\x0eupsert_entries\x18\x03 \x03(\x0b\x32`.temporal.api.workflowservice.v1.UpdateWorkerDeploymentVersionMetadataRequest.UpsertEntriesEntry\x12\x16\n\x0eremove_entries\x18\x04 \x03(\t\x12\x10\n\x08identity\x18\x06 \x01(\t\x1aU\n\x12UpsertEntriesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"n\n-UpdateWorkerDeploymentVersionMetadataResponse\x12=\n\x08metadata\x18\x01 \x01(\x0b\x32+.temporal.api.deployment.v1.VersionMetadata"\xbd\x01\n!SetWorkerDeploymentManagerRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t\x12\x1a\n\x10manager_identity\x18\x03 \x01(\tH\x00\x12\x0e\n\x04self\x18\x04 \x01(\x08H\x00\x12\x16\n\x0e\x63onflict_token\x18\x05 \x01(\x0c\x12\x10\n\x08identity\x18\x06 \x01(\tB\x16\n\x14new_manager_identity"c\n"SetWorkerDeploymentManagerResponse\x12\x16\n\x0e\x63onflict_token\x18\x01 \x01(\x0c\x12%\n\x19previous_manager_identity\x18\x02 \x01(\tB\x02\x18\x01"E\n\x1bGetCurrentDeploymentRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bseries_name\x18\x02 \x01(\t"k\n\x1cGetCurrentDeploymentResponse\x12K\n\x17\x63urrent_deployment_info\x18\x01 \x01(\x0b\x32*.temporal.api.deployment.v1.DeploymentInfo"q\n GetDeploymentReachabilityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12:\n\ndeployment\x18\x02 \x01(\x0b\x32&.temporal.api.deployment.v1.Deployment"\xe3\x01\n!GetDeploymentReachabilityResponse\x12\x43\n\x0f\x64\x65ployment_info\x18\x01 \x01(\x0b\x32*.temporal.api.deployment.v1.DeploymentInfo\x12\x43\n\x0creachability\x18\x02 \x01(\x0e\x32-.temporal.api.enums.v1.DeploymentReachability\x12\x34\n\x10last_update_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xb4\x01\n\x19\x43reateWorkflowRuleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x35\n\x04spec\x18\x02 \x01(\x0b\x32\'.temporal.api.rules.v1.WorkflowRuleSpec\x12\x12\n\nforce_scan\x18\x03 \x01(\x08\x12\x12\n\nrequest_id\x18\x04 \x01(\t\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t"_\n\x1a\x43reateWorkflowRuleResponse\x12\x31\n\x04rule\x18\x01 \x01(\x0b\x32#.temporal.api.rules.v1.WorkflowRule\x12\x0e\n\x06job_id\x18\x02 \x01(\t"A\n\x1b\x44\x65scribeWorkflowRuleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x0f\n\x07rule_id\x18\x02 \x01(\t"Q\n\x1c\x44\x65scribeWorkflowRuleResponse\x12\x31\n\x04rule\x18\x01 \x01(\x0b\x32#.temporal.api.rules.v1.WorkflowRule"?\n\x19\x44\x65leteWorkflowRuleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x0f\n\x07rule_id\x18\x02 \x01(\t"\x1c\n\x1a\x44\x65leteWorkflowRuleResponse"F\n\x18ListWorkflowRulesRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"h\n\x19ListWorkflowRulesResponse\x12\x32\n\x05rules\x18\x01 \x03(\x0b\x32#.temporal.api.rules.v1.WorkflowRule\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"\xce\x01\n\x1aTriggerWorkflowRuleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x0c\n\x02id\x18\x04 \x01(\tH\x00\x12\x37\n\x04spec\x18\x05 \x01(\x0b\x32\'.temporal.api.rules.v1.WorkflowRuleSpecH\x00\x12\x10\n\x08identity\x18\x03 \x01(\tB\x06\n\x04rule".\n\x1bTriggerWorkflowRuleResponse\x12\x0f\n\x07\x61pplied\x18\x01 \x01(\x08"\x9b\x01\n\x1cRecordWorkerHeartbeatRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x41\n\x10worker_heartbeat\x18\x03 \x03(\x0b\x32\'.temporal.api.worker.v1.WorkerHeartbeat\x12\x13\n\x0bresource_id\x18\x04 \x01(\t"\x1f\n\x1dRecordWorkerHeartbeatResponse"\x82\x01\n\x12ListWorkersRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t\x12\x1e\n\x16include_system_workers\x18\x05 \x01(\x08"\xa5\x01\n\x13ListWorkersResponse\x12<\n\x0cworkers_info\x18\x01 \x03(\x0b\x32".temporal.api.worker.v1.WorkerInfoB\x02\x18\x01\x12\x37\n\x07workers\x18\x03 \x03(\x0b\x32&.temporal.api.worker.v1.WorkerListInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"\xd5\x05\n\x1cUpdateTaskQueueConfigRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\ntask_queue\x18\x03 \x01(\t\x12=\n\x0ftask_queue_type\x18\x04 \x01(\x0e\x32$.temporal.api.enums.v1.TaskQueueType\x12n\n\x17update_queue_rate_limit\x18\x05 \x01(\x0b\x32M.temporal.api.workflowservice.v1.UpdateTaskQueueConfigRequest.RateLimitUpdate\x12}\n&update_fairness_key_rate_limit_default\x18\x06 \x01(\x0b\x32M.temporal.api.workflowservice.v1.UpdateTaskQueueConfigRequest.RateLimitUpdate\x12\x84\x01\n\x1dset_fairness_weight_overrides\x18\x07 \x03(\x0b\x32].temporal.api.workflowservice.v1.UpdateTaskQueueConfigRequest.SetFairnessWeightOverridesEntry\x12\'\n\x1funset_fairness_weight_overrides\x18\x08 \x03(\t\x1a[\n\x0fRateLimitUpdate\x12\x38\n\nrate_limit\x18\x01 \x01(\x0b\x32$.temporal.api.taskqueue.v1.RateLimit\x12\x0e\n\x06reason\x18\x02 \x01(\t\x1a\x41\n\x1fSetFairnessWeightOverridesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x02:\x02\x38\x01"[\n\x1dUpdateTaskQueueConfigResponse\x12:\n\x06\x63onfig\x18\x01 \x01(\x0b\x32*.temporal.api.taskqueue.v1.TaskQueueConfig"\x9e\x01\n\x18\x46\x65tchWorkerConfigRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x38\n\x08selector\x18\x06 \x01(\x0b\x32&.temporal.api.common.v1.WorkerSelector\x12\x13\n\x0bresource_id\x18\x07 \x01(\t"U\n\x19\x46\x65tchWorkerConfigResponse\x12\x38\n\rworker_config\x18\x01 \x01(\x0b\x32!.temporal.api.sdk.v1.WorkerConfig"\x8a\x02\n\x19UpdateWorkerConfigRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x38\n\rworker_config\x18\x04 \x01(\x0b\x32!.temporal.api.sdk.v1.WorkerConfig\x12/\n\x0bupdate_mask\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.FieldMask\x12\x38\n\x08selector\x18\x06 \x01(\x0b\x32&.temporal.api.common.v1.WorkerSelector\x12\x13\n\x0bresource_id\x18\x07 \x01(\t"d\n\x1aUpdateWorkerConfigResponse\x12:\n\rworker_config\x18\x01 \x01(\x0b\x32!.temporal.api.sdk.v1.WorkerConfigH\x00\x42\n\n\x08response"G\n\x15\x44\x65scribeWorkerRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x1b\n\x13worker_instance_key\x18\x02 \x01(\t"Q\n\x16\x44\x65scribeWorkerResponse\x12\x37\n\x0bworker_info\x18\x01 \x01(\x0b\x32".temporal.api.worker.v1.WorkerInfo"\x8d\x01\n\x1dPauseWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x0e\n\x06reason\x18\x05 \x01(\t\x12\x12\n\nrequest_id\x18\x06 \x01(\t" \n\x1ePauseWorkflowExecutionResponse"\x8f\x01\n\x1fUnpauseWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x0e\n\x06reason\x18\x05 \x01(\t\x12\x12\n\nrequest_id\x18\x06 \x01(\t""\n UnpauseWorkflowExecutionResponse"\x99\t\n\x1dStartActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\nrequest_id\x18\x03 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x04 \x01(\t\x12;\n\ractivity_type\x18\x05 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12\x38\n\ntask_queue\x18\x06 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12<\n\x19schedule_to_close_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x0b \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12/\n\x05input\x18\x0c \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x45\n\x0fid_reuse_policy\x18\r \x01(\x0e\x32,.temporal.api.enums.v1.ActivityIdReusePolicy\x12K\n\x12id_conflict_policy\x18\x0e \x01(\x0e\x32/.temporal.api.enums.v1.ActivityIdConflictPolicy\x12\x43\n\x11search_attributes\x18\x0f \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12.\n\x06header\x18\x10 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x38\n\ruser_metadata\x18\x11 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12\x32\n\x08priority\x18\x12 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12>\n\x14\x63ompletion_callbacks\x18\x13 \x03(\x0b\x32 .temporal.api.common.v1.Callback\x12+\n\x05links\x18\x14 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x12\x46\n\x13on_conflict_options\x18\x15 \x01(\x0b\x32).temporal.api.common.v1.OnConflictOptions\x12.\n\x0bstart_delay\x18\x16 \x01(\x0b\x32\x19.google.protobuf.Duration"m\n\x1eStartActivityExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x0f\n\x07started\x18\x02 \x01(\x08\x12*\n\x04link\x18\x03 \x01(\x0b\x32\x1c.temporal.api.common.v1.Link"\xe4\x01\n DescribeActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x15\n\rinclude_input\x18\x04 \x01(\x08\x12\x17\n\x0finclude_outcome\x18\x05 \x01(\x08\x12\x17\n\x0flong_poll_token\x18\x06 \x01(\x0c\x12!\n\x19include_heartbeat_details\x18\x07 \x01(\x08\x12\x1c\n\x14include_last_failure\x18\x08 \x01(\x08"\xbc\x02\n!DescribeActivityExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12=\n\x04info\x18\x02 \x01(\x0b\x32/.temporal.api.activity.v1.ActivityExecutionInfo\x12/\n\x05input\x18\x03 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x43\n\x07outcome\x18\x04 \x01(\x0b\x32\x32.temporal.api.activity.v1.ActivityExecutionOutcome\x12\x17\n\x0flong_poll_token\x18\x05 \x01(\x0c\x12\x39\n\tcallbacks\x18\x06 \x03(\x0b\x32&.temporal.api.activity.v1.CallbackInfo"V\n\x1cPollActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t"t\n\x1dPollActivityExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x43\n\x07outcome\x18\x02 \x01(\x0b\x32\x32.temporal.api.activity.v1.ActivityExecutionOutcome"m\n\x1dListActivityExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t"\x82\x01\n\x1eListActivityExecutionsResponse\x12G\n\nexecutions\x18\x01 \x03(\x0b\x32\x33.temporal.api.activity.v1.ActivityExecutionListInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"\xd1\x06\n#StartNexusOperationExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\nrequest_id\x18\x03 \x01(\t\x12\x14\n\x0coperation_id\x18\x04 \x01(\t\x12\x10\n\x08\x65ndpoint\x18\x05 \x01(\t\x12\x0f\n\x07service\x18\x06 \x01(\t\x12\x11\n\toperation\x18\x07 \x01(\t\x12<\n\x19schedule_to_close_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12.\n\x05input\x18\x0b \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12K\n\x0fid_reuse_policy\x18\x0c \x01(\x0e\x32\x32.temporal.api.enums.v1.NexusOperationIdReusePolicy\x12Q\n\x12id_conflict_policy\x18\r \x01(\x0e\x32\x35.temporal.api.enums.v1.NexusOperationIdConflictPolicy\x12\x43\n\x11search_attributes\x18\x0e \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12k\n\x0cnexus_header\x18\x0f \x03(\x0b\x32U.temporal.api.workflowservice.v1.StartNexusOperationExecutionRequest.NexusHeaderEntry\x12\x38\n\ruser_metadata\x18\x10 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x1a\x32\n\x10NexusHeaderEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"G\n$StartNexusOperationExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x0f\n\x07started\x18\x02 \x01(\x08"\xaa\x01\n&DescribeNexusOperationExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0coperation_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x15\n\rinclude_input\x18\x04 \x01(\x08\x12\x17\n\x0finclude_outcome\x18\x05 \x01(\x08\x12\x17\n\x0flong_poll_token\x18\x06 \x01(\x0c"\xb7\x02\n\'DescribeNexusOperationExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12@\n\x04info\x18\x02 \x01(\x0b\x32\x32.temporal.api.nexus.v1.NexusOperationExecutionInfo\x12.\n\x05input\x18\x03 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\x31\n\x06result\x18\x04 \x01(\x0b\x32\x1f.temporal.api.common.v1.PayloadH\x00\x12\x33\n\x07\x66\x61ilure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.FailureH\x00\x12\x17\n\x0flong_poll_token\x18\x06 \x01(\x0c\x42\t\n\x07outcome"\xa1\x01\n"PollNexusOperationExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0coperation_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x42\n\nwait_stage\x18\x04 \x01(\x0e\x32..temporal.api.enums.v1.NexusOperationWaitStage"\x85\x02\n#PollNexusOperationExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x42\n\nwait_stage\x18\x02 \x01(\x0e\x32..temporal.api.enums.v1.NexusOperationWaitStage\x12\x17\n\x0foperation_token\x18\x03 \x01(\t\x12\x31\n\x06result\x18\x04 \x01(\x0b\x32\x1f.temporal.api.common.v1.PayloadH\x00\x12\x33\n\x07\x66\x61ilure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.FailureH\x00\x42\t\n\x07outcome"s\n#ListNexusOperationExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t"\x8b\x01\n$ListNexusOperationExecutionsResponse\x12J\n\noperations\x18\x01 \x03(\x0b\x32\x36.temporal.api.nexus.v1.NexusOperationExecutionListInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"B\n\x1e\x43ountActivityExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\r\n\x05query\x18\x02 \x01(\t"\xed\x01\n\x1f\x43ountActivityExecutionsResponse\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12\x61\n\x06groups\x18\x02 \x03(\x0b\x32Q.temporal.api.workflowservice.v1.CountActivityExecutionsResponse.AggregationGroup\x1aX\n\x10\x41ggregationGroup\x12\x35\n\x0cgroup_values\x18\x01 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\r\n\x05\x63ount\x18\x02 \x01(\x03"H\n$CountNexusOperationExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\r\n\x05query\x18\x02 \x01(\t"\xf9\x01\n%CountNexusOperationExecutionsResponse\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12g\n\x06groups\x18\x02 \x03(\x0b\x32W.temporal.api.workflowservice.v1.CountNexusOperationExecutionsResponse.AggregationGroup\x1aX\n\x10\x41ggregationGroup\x12\x35\n\x0cgroup_values\x18\x01 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\r\n\x05\x63ount\x18\x02 \x01(\x03"\x95\x01\n%RequestCancelActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t\x12\x0e\n\x06reason\x18\x06 \x01(\t"(\n&RequestCancelActivityExecutionResponse"\x91\x01\n!TerminateActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t\x12\x0e\n\x06reason\x18\x06 \x01(\t"$\n"TerminateActivityExecutionResponse"X\n\x1e\x44\x65leteActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t"!\n\x1f\x44\x65leteActivityExecutionResponse"\x9c\x01\n+RequestCancelNexusOperationExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0coperation_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t\x12\x0e\n\x06reason\x18\x06 \x01(\t".\n,RequestCancelNexusOperationExecutionResponse"\x98\x01\n\'TerminateNexusOperationExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0coperation_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t\x12\x0e\n\x06reason\x18\x06 \x01(\t"*\n(TerminateNexusOperationExecutionResponse"_\n$DeleteNexusOperationExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0coperation_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t"\'\n%DeleteNexusOperationExecutionResponseB\xbe\x01\n"io.temporal.api.workflowservice.v1B\x14RequestResponseProtoP\x01Z5go.temporal.io/api/workflowservice/v1;workflowservice\xaa\x02!Temporalio.Api.WorkflowService.V1\xea\x02$Temporalio::Api::WorkflowService::V1b\x06proto3' + b'\n6temporal/api/workflowservice/v1/request_response.proto\x12\x1ftemporal.api.workflowservice.v1\x1a+temporal/api/enums/v1/batch_operation.proto\x1a"temporal/api/enums/v1/common.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a%temporal/api/enums/v1/namespace.proto\x1a(temporal/api/enums/v1/failed_cause.proto\x1a!temporal/api/enums/v1/query.proto\x1a!temporal/api/enums/v1/reset.proto\x1a&temporal/api/enums/v1/task_queue.proto\x1a&temporal/api/enums/v1/deployment.proto\x1a"temporal/api/enums/v1/update.proto\x1a)temporal/api/enums/v1/time_skipping.proto\x1a$temporal/api/enums/v1/activity.proto\x1a!temporal/api/enums/v1/nexus.proto\x1a&temporal/api/activity/v1/message.proto\x1a$temporal/api/common/v1/message.proto\x1a%temporal/api/history/v1/message.proto\x1a&temporal/api/workflow/v1/message.proto\x1a%temporal/api/command/v1/message.proto\x1a$temporal/api/compute/v1/config.proto\x1a(temporal/api/deployment/v1/message.proto\x1a%temporal/api/failure/v1/message.proto\x1a$temporal/api/filter/v1/message.proto\x1a&temporal/api/protocol/v1/message.proto\x1a\'temporal/api/namespace/v1/message.proto\x1a#temporal/api/query/v1/message.proto\x1a)temporal/api/replication/v1/message.proto\x1a#temporal/api/rules/v1/message.proto\x1a\'temporal/api/sdk/v1/worker_config.proto\x1a&temporal/api/schedule/v1/message.proto\x1a\'temporal/api/taskqueue/v1/message.proto\x1a$temporal/api/update/v1/message.proto\x1a%temporal/api/version/v1/message.proto\x1a#temporal/api/batch/v1/message.proto\x1a\x30temporal/api/sdk/v1/task_complete_metadata.proto\x1a\'temporal/api/sdk/v1/user_metadata.proto\x1a#temporal/api/nexus/v1/message.proto\x1a$temporal/api/worker/v1/message.proto\x1a\x1egoogle/protobuf/duration.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\x88\x05\n\x18RegisterNamespaceRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x13\n\x0bowner_email\x18\x03 \x01(\t\x12\x46\n#workflow_execution_retention_period\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12G\n\x08\x63lusters\x18\x05 \x03(\x0b\x32\x35.temporal.api.replication.v1.ClusterReplicationConfig\x12\x1b\n\x13\x61\x63tive_cluster_name\x18\x06 \x01(\t\x12Q\n\x04\x64\x61ta\x18\x07 \x03(\x0b\x32\x43.temporal.api.workflowservice.v1.RegisterNamespaceRequest.DataEntry\x12\x16\n\x0esecurity_token\x18\x08 \x01(\t\x12\x1b\n\x13is_global_namespace\x18\t \x01(\x08\x12\x44\n\x16history_archival_state\x18\n \x01(\x0e\x32$.temporal.api.enums.v1.ArchivalState\x12\x1c\n\x14history_archival_uri\x18\x0b \x01(\t\x12G\n\x19visibility_archival_state\x18\x0c \x01(\x0e\x32$.temporal.api.enums.v1.ArchivalState\x12\x1f\n\x17visibility_archival_uri\x18\r \x01(\t\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"\x1b\n\x19RegisterNamespaceResponse"\x89\x01\n\x15ListNamespacesRequest\x12\x11\n\tpage_size\x18\x01 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c\x12\x44\n\x10namespace_filter\x18\x03 \x01(\x0b\x32*.temporal.api.namespace.v1.NamespaceFilter"\x81\x01\n\x16ListNamespacesResponse\x12N\n\nnamespaces\x18\x01 \x03(\x0b\x32:.temporal.api.workflowservice.v1.DescribeNamespaceResponse\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"S\n\x18\x44\x65scribeNamespaceRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\t\x12\x18\n\x10weak_consistency\x18\x03 \x01(\x08"\x81\x04\n\x19\x44\x65scribeNamespaceResponse\x12@\n\x0enamespace_info\x18\x01 \x01(\x0b\x32(.temporal.api.namespace.v1.NamespaceInfo\x12:\n\x06\x63onfig\x18\x02 \x01(\x0b\x32*.temporal.api.namespace.v1.NamespaceConfig\x12S\n\x12replication_config\x18\x03 \x01(\x0b\x32\x37.temporal.api.replication.v1.NamespaceReplicationConfig\x12\x18\n\x10\x66\x61ilover_version\x18\x04 \x01(\x03\x12\x1b\n\x13is_global_namespace\x18\x05 \x01(\x08\x12\x45\n\x10\x66\x61ilover_history\x18\x06 \x03(\x0b\x32+.temporal.api.replication.v1.FailoverStatus\x12J\n\x12poller_group_infos\x18\x07 \x03(\x0b\x32*.temporal.api.taskqueue.v1.PollerGroupInfoB\x02\x18\x01\x12G\n\x12poller_groups_info\x18\x08 \x01(\x0b\x32+.temporal.api.taskqueue.v1.PollerGroupsInfo"\xcf\x02\n\x16UpdateNamespaceRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x43\n\x0bupdate_info\x18\x02 \x01(\x0b\x32..temporal.api.namespace.v1.UpdateNamespaceInfo\x12:\n\x06\x63onfig\x18\x03 \x01(\x0b\x32*.temporal.api.namespace.v1.NamespaceConfig\x12S\n\x12replication_config\x18\x04 \x01(\x0b\x32\x37.temporal.api.replication.v1.NamespaceReplicationConfig\x12\x16\n\x0esecurity_token\x18\x05 \x01(\t\x12\x19\n\x11\x64\x65lete_bad_binary\x18\x06 \x01(\t\x12\x19\n\x11promote_namespace\x18\x07 \x01(\x08"\xa3\x02\n\x17UpdateNamespaceResponse\x12@\n\x0enamespace_info\x18\x01 \x01(\x0b\x32(.temporal.api.namespace.v1.NamespaceInfo\x12:\n\x06\x63onfig\x18\x02 \x01(\x0b\x32*.temporal.api.namespace.v1.NamespaceConfig\x12S\n\x12replication_config\x18\x03 \x01(\x0b\x32\x37.temporal.api.replication.v1.NamespaceReplicationConfig\x12\x18\n\x10\x66\x61ilover_version\x18\x04 \x01(\x03\x12\x1b\n\x13is_global_namespace\x18\x05 \x01(\x08"F\n\x19\x44\x65precateNamespaceRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x16\n\x0esecurity_token\x18\x02 \x01(\t"\x1c\n\x1a\x44\x65precateNamespaceResponse"\xd1\x0c\n\x1dStartWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x04 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12=\n\x1aworkflow_execution_timeout\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x10\n\x08identity\x18\t \x01(\t\x12\x12\n\nrequest_id\x18\n \x01(\t\x12N\n\x18workflow_id_reuse_policy\x18\x0b \x01(\x0e\x32,.temporal.api.enums.v1.WorkflowIdReusePolicy\x12T\n\x1bworkflow_id_conflict_policy\x18\x16 \x01(\x0e\x32/.temporal.api.enums.v1.WorkflowIdConflictPolicy\x12\x39\n\x0cretry_policy\x18\x0c \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x15\n\rcron_schedule\x18\r \x01(\t\x12*\n\x04memo\x18\x0e \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x0f \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12.\n\x06header\x18\x10 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x1f\n\x17request_eager_execution\x18\x11 \x01(\x08\x12;\n\x11\x63ontinued_failure\x18\x12 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12@\n\x16last_completion_result\x18\x13 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x37\n\x14workflow_start_delay\x18\x14 \x01(\x0b\x32\x19.google.protobuf.Duration\x12>\n\x14\x63ompletion_callbacks\x18\x15 \x03(\x0b\x32 .temporal.api.common.v1.Callback\x12\x38\n\ruser_metadata\x18\x17 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12+\n\x05links\x18\x18 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x12I\n\x13versioning_override\x18\x19 \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12H\n\x13on_conflict_options\x18\x1a \x01(\x0b\x32+.temporal.api.workflow.v1.OnConflictOptions\x12\x32\n\x08priority\x18\x1b \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12\\\n\x1f\x65\x61ger_worker_deployment_options\x18\x1c \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions\x12H\n\x14time_skipping_config\x18\x1d \x01(\x0b\x32*.temporal.api.common.v1.TimeSkippingConfig"\xaa\x02\n\x1eStartWorkflowExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x1e\n\x16\x66irst_execution_run_id\x18\x06 \x01(\t\x12\x0f\n\x07started\x18\x03 \x01(\x08\x12>\n\x06status\x18\x05 \x01(\x0e\x32..temporal.api.enums.v1.WorkflowExecutionStatus\x12[\n\x13\x65\x61ger_workflow_task\x18\x02 \x01(\x0b\x32>.temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse\x12*\n\x04link\x18\x04 \x01(\x0b\x32\x1c.temporal.api.common.v1.Link"\xaa\x02\n"GetWorkflowExecutionHistoryRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x19\n\x11maximum_page_size\x18\x03 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x04 \x01(\x0c\x12\x16\n\x0ewait_new_event\x18\x05 \x01(\x08\x12P\n\x19history_event_filter_type\x18\x06 \x01(\x0e\x32-.temporal.api.enums.v1.HistoryEventFilterType\x12\x15\n\rskip_archival\x18\x07 \x01(\x08"\xba\x01\n#GetWorkflowExecutionHistoryResponse\x12\x31\n\x07history\x18\x01 \x01(\x0b\x32 .temporal.api.history.v1.History\x12\x35\n\x0braw_history\x18\x02 \x03(\x0b\x32 .temporal.api.common.v1.DataBlob\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\x10\n\x08\x61rchived\x18\x04 \x01(\x08"\xb0\x01\n)GetWorkflowExecutionHistoryReverseRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x19\n\x11maximum_page_size\x18\x03 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x04 \x01(\x0c"x\n*GetWorkflowExecutionHistoryReverseResponse\x12\x31\n\x07history\x18\x01 \x01(\x0b\x32 .temporal.api.history.v1.History\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c"\xb8\x03\n\x1cPollWorkflowTaskQueueRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x38\n\ntask_queue\x18\x02 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12\x17\n\x0fpoller_group_id\x18\n \x01(\t\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x1b\n\x13worker_instance_key\x18\x08 \x01(\t\x12!\n\x19worker_control_task_queue\x18\t \x01(\t\x12\x1b\n\x0f\x62inary_checksum\x18\x04 \x01(\tB\x02\x18\x01\x12Z\n\x1bworker_version_capabilities\x18\x05 \x01(\x0b\x32\x31.temporal.api.common.v1.WorkerVersionCapabilitiesB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x06 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptionsJ\x04\x08\x07\x10\x08R\x10worker_heartbeat"\xbf\x08\n\x1dPollWorkflowTaskQueueResponse\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12!\n\x19previous_started_event_id\x18\x04 \x01(\x03\x12\x18\n\x10started_event_id\x18\x05 \x01(\x03\x12\x0f\n\x07\x61ttempt\x18\x06 \x01(\x05\x12\x1a\n\x12\x62\x61\x63klog_count_hint\x18\x07 \x01(\x03\x12\x31\n\x07history\x18\x08 \x01(\x0b\x32 .temporal.api.history.v1.History\x12\x17\n\x0fnext_page_token\x18\t \x01(\x0c\x12\x33\n\x05query\x18\n \x01(\x0b\x32$.temporal.api.query.v1.WorkflowQuery\x12K\n\x1dworkflow_execution_task_queue\x18\x0b \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12\x32\n\x0escheduled_time\x18\x0c \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x0cstarted_time\x18\r \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\\\n\x07queries\x18\x0e \x03(\x0b\x32K.temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse.QueriesEntry\x12\x33\n\x08messages\x18\x0f \x03(\x0b\x32!.temporal.api.protocol.v1.Message\x12Q\n\x17poller_scaling_decision\x18\x10 \x01(\x0b\x32\x30.temporal.api.taskqueue.v1.PollerScalingDecision\x12\x17\n\x0fpoller_group_id\x18\x11 \x01(\t\x12J\n\x12poller_group_infos\x18\x12 \x03(\x0b\x32*.temporal.api.taskqueue.v1.PollerGroupInfoB\x02\x18\x01\x12G\n\x12poller_groups_info\x18\x13 \x01(\x0b\x32+.temporal.api.taskqueue.v1.PollerGroupsInfo\x1aT\n\x0cQueriesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x33\n\x05value\x18\x02 \x01(\x0b\x32$.temporal.api.query.v1.WorkflowQuery:\x02\x38\x01"\xba\n\n#RespondWorkflowTaskCompletedRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x32\n\x08\x63ommands\x18\x02 \x03(\x0b\x32 .temporal.api.command.v1.Command\x12\x10\n\x08identity\x18\x03 \x01(\t\x12O\n\x11sticky_attributes\x18\x04 \x01(\x0b\x32\x34.temporal.api.taskqueue.v1.StickyExecutionAttributes\x12 \n\x18return_new_workflow_task\x18\x05 \x01(\x08\x12&\n\x1e\x66orce_create_new_workflow_task\x18\x06 \x01(\x08\x12\x1b\n\x0f\x62inary_checksum\x18\x07 \x01(\tB\x02\x18\x01\x12m\n\rquery_results\x18\x08 \x03(\x0b\x32V.temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest.QueryResultsEntry\x12\x11\n\tnamespace\x18\t \x01(\t\x12\x13\n\x0bresource_id\x18\x12 \x01(\t\x12L\n\x14worker_version_stamp\x18\n \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12\x33\n\x08messages\x18\x0b \x03(\x0b\x32!.temporal.api.protocol.v1.Message\x12H\n\x0csdk_metadata\x18\x0c \x01(\x0b\x32\x32.temporal.api.sdk.v1.WorkflowTaskCompletedMetadata\x12\x43\n\x11metering_metadata\x18\r \x01(\x0b\x32(.temporal.api.common.v1.MeteringMetadata\x12g\n\x0c\x63\x61pabilities\x18\x0e \x01(\x0b\x32Q.temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest.Capabilities\x12>\n\ndeployment\x18\x0f \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12\x46\n\x13versioning_behavior\x18\x10 \x01(\x0e\x32).temporal.api.enums.v1.VersioningBehavior\x12O\n\x12\x64\x65ployment_options\x18\x11 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions\x12\x1b\n\x13worker_instance_key\x18\x13 \x01(\t\x12!\n\x19worker_control_task_queue\x18\x14 \x01(\t\x12\x13\n\x0bpage_number\x18\x15 \x01(\x05\x12\x19\n\x11intermediate_page\x18\x16 \x01(\x08\x1a_\n\x11QueryResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x39\n\x05value\x18\x02 \x01(\x0b\x32*.temporal.api.query.v1.WorkflowQueryResult:\x02\x38\x01\x1a\x45\n\x0c\x43\x61pabilities\x12\x35\n-discard_speculative_workflow_task_with_events\x18\x01 \x01(\x08"\xf5\x01\n$RespondWorkflowTaskCompletedResponse\x12U\n\rworkflow_task\x18\x01 \x01(\x0b\x32>.temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse\x12V\n\x0e\x61\x63tivity_tasks\x18\x02 \x03(\x0b\x32>.temporal.api.workflowservice.v1.PollActivityTaskQueueResponse\x12\x1e\n\x16reset_history_event_id\x18\x03 \x01(\x03"\x8d\x04\n RespondWorkflowTaskFailedRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12=\n\x05\x63\x61use\x18\x02 \x01(\x0e\x32..temporal.api.enums.v1.WorkflowTaskFailedCause\x12\x31\n\x07\x66\x61ilure\x18\x03 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x1b\n\x0f\x62inary_checksum\x18\x05 \x01(\tB\x02\x18\x01\x12\x11\n\tnamespace\x18\x06 \x01(\t\x12\x13\n\x0bresource_id\x18\x0b \x01(\t\x12\x33\n\x08messages\x18\x07 \x03(\x0b\x32!.temporal.api.protocol.v1.Message\x12\x46\n\x0eworker_version\x18\x08 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12>\n\ndeployment\x18\t \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\n \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions"#\n!RespondWorkflowTaskFailedResponse"\xe6\x03\n\x1cPollActivityTaskQueueRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x38\n\ntask_queue\x18\x02 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12\x17\n\x0fpoller_group_id\x18\n \x01(\t\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x1b\n\x13worker_instance_key\x18\x08 \x01(\t\x12!\n\x19worker_control_task_queue\x18\t \x01(\t\x12I\n\x13task_queue_metadata\x18\x04 \x01(\x0b\x32,.temporal.api.taskqueue.v1.TaskQueueMetadata\x12Z\n\x1bworker_version_capabilities\x18\x05 \x01(\x0b\x32\x31.temporal.api.common.v1.WorkerVersionCapabilitiesB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x06 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptionsJ\x04\x08\x07\x10\x08R\x10worker_heartbeat"\x9d\t\n\x1dPollActivityTaskQueueResponse\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x1a\n\x12workflow_namespace\x18\x02 \x01(\t\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x45\n\x12workflow_execution\x18\x04 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12;\n\ractivity_type\x18\x05 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12\x13\n\x0b\x61\x63tivity_id\x18\x06 \x01(\t\x12.\n\x06header\x18\x07 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12/\n\x05input\x18\x08 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12;\n\x11heartbeat_details\x18\t \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x32\n\x0escheduled_time\x18\n \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x42\n\x1e\x63urrent_attempt_scheduled_time\x18\x0b \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x0cstarted_time\x18\x0c \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07\x61ttempt\x18\r \x01(\x05\x12<\n\x19schedule_to_close_timeout\x18\x0e \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\x0f \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\x10 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x11 \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12Q\n\x17poller_scaling_decision\x18\x12 \x01(\x0b\x32\x30.temporal.api.taskqueue.v1.PollerScalingDecision\x12\x32\n\x08priority\x18\x13 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12\x17\n\x0f\x61\x63tivity_run_id\x18\x14 \x01(\t\x12J\n\x12poller_group_infos\x18\x15 \x03(\x0b\x32*.temporal.api.taskqueue.v1.PollerGroupInfoB\x02\x18\x01\x12G\n\x12poller_groups_info\x18\x16 \x01(\x0b\x32+.temporal.api.taskqueue.v1.PollerGroupsInfo"\xa5\x01\n"RecordActivityTaskHeartbeatRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x31\n\x07\x64\x65tails\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x11\n\tnamespace\x18\x04 \x01(\t\x12\x13\n\x0bresource_id\x18\x05 \x01(\t"p\n#RecordActivityTaskHeartbeatResponse\x12\x18\n\x10\x63\x61ncel_requested\x18\x01 \x01(\x08\x12\x17\n\x0f\x61\x63tivity_paused\x18\x02 \x01(\x08\x12\x16\n\x0e\x61\x63tivity_reset\x18\x03 \x01(\x08"\xcf\x01\n&RecordActivityTaskHeartbeatByIdRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x04 \x01(\t\x12\x31\n\x07\x64\x65tails\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x06 \x01(\t\x12\x13\n\x0bresource_id\x18\x07 \x01(\t"t\n\'RecordActivityTaskHeartbeatByIdResponse\x12\x18\n\x10\x63\x61ncel_requested\x18\x01 \x01(\x08\x12\x17\n\x0f\x61\x63tivity_paused\x18\x02 \x01(\x08\x12\x16\n\x0e\x61\x63tivity_reset\x18\x03 \x01(\x08"\xfe\x02\n#RespondActivityTaskCompletedRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x30\n\x06result\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x11\n\tnamespace\x18\x04 \x01(\t\x12\x13\n\x0bresource_id\x18\x08 \x01(\t\x12\x46\n\x0eworker_version\x18\x05 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12>\n\ndeployment\x18\x06 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x07 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions"&\n$RespondActivityTaskCompletedResponse"\xcf\x01\n\'RespondActivityTaskCompletedByIdRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x04 \x01(\t\x12\x30\n\x06result\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x06 \x01(\t\x12\x13\n\x0bresource_id\x18\x07 \x01(\t"*\n(RespondActivityTaskCompletedByIdResponse"\xbe\x03\n RespondActivityTaskFailedRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x31\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x11\n\tnamespace\x18\x04 \x01(\t\x12\x13\n\x0bresource_id\x18\t \x01(\t\x12@\n\x16last_heartbeat_details\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x46\n\x0eworker_version\x18\x06 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12>\n\ndeployment\x18\x07 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x08 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions"W\n!RespondActivityTaskFailedResponse\x12\x32\n\x08\x66\x61ilures\x18\x01 \x03(\x0b\x32 .temporal.api.failure.v1.Failure"\x8f\x02\n$RespondActivityTaskFailedByIdRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x04 \x01(\t\x12\x31\n\x07\x66\x61ilure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x10\n\x08identity\x18\x06 \x01(\t\x12@\n\x16last_heartbeat_details\x18\x07 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x13\n\x0bresource_id\x18\x08 \x01(\t"[\n%RespondActivityTaskFailedByIdResponse\x12\x32\n\x08\x66\x61ilures\x18\x01 \x03(\x0b\x32 .temporal.api.failure.v1.Failure"\xfe\x02\n"RespondActivityTaskCanceledRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12\x31\n\x07\x64\x65tails\x18\x02 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x11\n\tnamespace\x18\x04 \x01(\t\x12\x13\n\x0bresource_id\x18\x08 \x01(\t\x12\x46\n\x0eworker_version\x18\x05 \x01(\x0b\x32*.temporal.api.common.v1.WorkerVersionStampB\x02\x18\x01\x12>\n\ndeployment\x18\x06 \x01(\x0b\x32&.temporal.api.deployment.v1.DeploymentB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x07 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions"%\n#RespondActivityTaskCanceledResponse"\xa0\x02\n&RespondActivityTaskCanceledByIdRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x04 \x01(\t\x12\x31\n\x07\x64\x65tails\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x06 \x01(\t\x12O\n\x12\x64\x65ployment_options\x18\x07 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions\x12\x13\n\x0bresource_id\x18\x08 \x01(\t")\n\'RespondActivityTaskCanceledByIdResponse"\x84\x02\n%RequestCancelWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x12\n\nrequest_id\x18\x04 \x01(\t\x12\x1e\n\x16\x66irst_execution_run_id\x18\x05 \x01(\t\x12\x0e\n\x06reason\x18\x06 \x01(\t\x12+\n\x05links\x18\x07 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link"(\n&RequestCancelWorkflowExecutionResponse"\xde\x02\n\x1eSignalWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x13\n\x0bsignal_name\x18\x03 \x01(\t\x12/\n\x05input\x18\x04 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x12\n\nrequest_id\x18\x06 \x01(\t\x12\x13\n\x07\x63ontrol\x18\x07 \x01(\tB\x02\x18\x01\x12.\n\x06header\x18\x08 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12+\n\x05links\x18\n \x03(\x0b\x32\x1c.temporal.api.common.v1.LinkJ\x04\x08\t\x10\n"M\n\x1fSignalWorkflowExecutionResponse\x12*\n\x04link\x18\x01 \x01(\x0b\x32\x1c.temporal.api.common.v1.Link"\xbb\n\n\'SignalWithStartWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12;\n\rworkflow_type\x18\x03 \x01(\x0b\x32$.temporal.api.common.v1.WorkflowType\x12\x38\n\ntask_queue\x18\x04 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12/\n\x05input\x18\x05 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12=\n\x1aworkflow_execution_timeout\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x10\n\x08identity\x18\t \x01(\t\x12\x12\n\nrequest_id\x18\n \x01(\t\x12N\n\x18workflow_id_reuse_policy\x18\x0b \x01(\x0e\x32,.temporal.api.enums.v1.WorkflowIdReusePolicy\x12T\n\x1bworkflow_id_conflict_policy\x18\x16 \x01(\x0e\x32/.temporal.api.enums.v1.WorkflowIdConflictPolicy\x12\x13\n\x0bsignal_name\x18\x0c \x01(\t\x12\x36\n\x0csignal_input\x18\r \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x13\n\x07\x63ontrol\x18\x0e \x01(\tB\x02\x18\x01\x12\x39\n\x0cretry_policy\x18\x0f \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x15\n\rcron_schedule\x18\x10 \x01(\t\x12*\n\x04memo\x18\x11 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x12 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12.\n\x06header\x18\x13 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x37\n\x14workflow_start_delay\x18\x14 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\ruser_metadata\x18\x17 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12+\n\x05links\x18\x18 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x12I\n\x13versioning_override\x18\x19 \x01(\x0b\x32,.temporal.api.workflow.v1.VersioningOverride\x12\x32\n\x08priority\x18\x1a \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12H\n\x14time_skipping_config\x18\x1b \x01(\x0b\x32*.temporal.api.common.v1.TimeSkippingConfigJ\x04\x08\x15\x10\x16"\x9e\x01\n(SignalWithStartWorkflowExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x1e\n\x16\x66irst_execution_run_id\x18\x04 \x01(\t\x12\x0f\n\x07started\x18\x02 \x01(\x08\x12\x31\n\x0bsignal_link\x18\x03 \x01(\x0b\x32\x1c.temporal.api.common.v1.Link"\xc1\x03\n\x1dResetWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12%\n\x1dworkflow_task_finish_event_id\x18\x04 \x01(\x03\x12\x12\n\nrequest_id\x18\x05 \x01(\t\x12G\n\x12reset_reapply_type\x18\x06 \x01(\x0e\x32\'.temporal.api.enums.v1.ResetReapplyTypeB\x02\x18\x01\x12S\n\x1breset_reapply_exclude_types\x18\x07 \x03(\x0e\x32..temporal.api.enums.v1.ResetReapplyExcludeType\x12K\n\x15post_reset_operations\x18\x08 \x03(\x0b\x32,.temporal.api.workflow.v1.PostResetOperation\x12\x10\n\x08identity\x18\t \x01(\t"0\n\x1eResetWorkflowExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t"\x9f\x02\n!TerminateWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x31\n\x07\x64\x65tails\x18\x04 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x1e\n\x16\x66irst_execution_run_id\x18\x06 \x01(\t\x12+\n\x05links\x18\x07 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link"$\n"TerminateWorkflowExecutionResponse"z\n\x1e\x44\x65leteWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution"!\n\x1f\x44\x65leteWorkflowExecutionResponse"\xc9\x02\n!ListOpenWorkflowExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x19\n\x11maximum_page_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\x42\n\x11start_time_filter\x18\x04 \x01(\x0b\x32\'.temporal.api.filter.v1.StartTimeFilter\x12K\n\x10\x65xecution_filter\x18\x05 \x01(\x0b\x32/.temporal.api.filter.v1.WorkflowExecutionFilterH\x00\x12\x41\n\x0btype_filter\x18\x06 \x01(\x0b\x32*.temporal.api.filter.v1.WorkflowTypeFilterH\x00\x42\t\n\x07\x66ilters"\x82\x01\n"ListOpenWorkflowExecutionsResponse\x12\x43\n\nexecutions\x18\x01 \x03(\x0b\x32/.temporal.api.workflow.v1.WorkflowExecutionInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"\x8a\x03\n#ListClosedWorkflowExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x19\n\x11maximum_page_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\x42\n\x11start_time_filter\x18\x04 \x01(\x0b\x32\'.temporal.api.filter.v1.StartTimeFilter\x12K\n\x10\x65xecution_filter\x18\x05 \x01(\x0b\x32/.temporal.api.filter.v1.WorkflowExecutionFilterH\x00\x12\x41\n\x0btype_filter\x18\x06 \x01(\x0b\x32*.temporal.api.filter.v1.WorkflowTypeFilterH\x00\x12=\n\rstatus_filter\x18\x07 \x01(\x0b\x32$.temporal.api.filter.v1.StatusFilterH\x00\x42\t\n\x07\x66ilters"\x84\x01\n$ListClosedWorkflowExecutionsResponse\x12\x43\n\nexecutions\x18\x01 \x03(\x0b\x32/.temporal.api.workflow.v1.WorkflowExecutionInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"m\n\x1dListWorkflowExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t"~\n\x1eListWorkflowExecutionsResponse\x12\x43\n\nexecutions\x18\x01 \x03(\x0b\x32/.temporal.api.workflow.v1.WorkflowExecutionInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"u\n%ListArchivedWorkflowExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t"\x86\x01\n&ListArchivedWorkflowExecutionsResponse\x12\x43\n\nexecutions\x18\x01 \x03(\x0b\x32/.temporal.api.workflow.v1.WorkflowExecutionInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"m\n\x1dScanWorkflowExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t"~\n\x1eScanWorkflowExecutionsResponse\x12\x43\n\nexecutions\x18\x01 \x03(\x0b\x32/.temporal.api.workflow.v1.WorkflowExecutionInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"B\n\x1e\x43ountWorkflowExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\r\n\x05query\x18\x02 \x01(\t"\xed\x01\n\x1f\x43ountWorkflowExecutionsResponse\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12\x61\n\x06groups\x18\x02 \x03(\x0b\x32Q.temporal.api.workflowservice.v1.CountWorkflowExecutionsResponse.AggregationGroup\x1aX\n\x10\x41ggregationGroup\x12\x35\n\x0cgroup_values\x18\x01 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\r\n\x05\x63ount\x18\x02 \x01(\x03"\x1c\n\x1aGetSearchAttributesRequest"\xc9\x01\n\x1bGetSearchAttributesResponse\x12T\n\x04keys\x18\x01 \x03(\x0b\x32\x46.temporal.api.workflowservice.v1.GetSearchAttributesResponse.KeysEntry\x1aT\n\tKeysEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x36\n\x05value\x18\x02 \x01(\x0e\x32\'.temporal.api.enums.v1.IndexedValueType:\x02\x38\x01"\xe9\x02\n RespondQueryTaskCompletedRequest\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12>\n\x0e\x63ompleted_type\x18\x02 \x01(\x0e\x32&.temporal.api.enums.v1.QueryResultType\x12\x36\n\x0cquery_result\x18\x03 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x15\n\rerror_message\x18\x04 \x01(\t\x12\x11\n\tnamespace\x18\x06 \x01(\t\x12\x31\n\x07\x66\x61ilure\x18\x07 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12=\n\x05\x63\x61use\x18\x08 \x01(\x0e\x32..temporal.api.enums.v1.WorkflowTaskFailedCause\x12\x17\n\x0fpoller_group_id\x18\t \x01(\tJ\x04\x08\x05\x10\x06"#\n!RespondQueryTaskCompletedResponse"n\n\x1bResetStickyTaskQueueRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution"\x1e\n\x1cResetStickyTaskQueueResponse"\x9b\x02\n\x15ShutdownWorkerRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x19\n\x11sticky_task_queue\x18\x02 \x01(\t\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x0e\n\x06reason\x18\x04 \x01(\t\x12\x41\n\x10worker_heartbeat\x18\x05 \x01(\x0b\x32\'.temporal.api.worker.v1.WorkerHeartbeat\x12\x1b\n\x13worker_instance_key\x18\x06 \x01(\t\x12\x12\n\ntask_queue\x18\x07 \x01(\t\x12>\n\x10task_queue_types\x18\x08 \x03(\x0e\x32$.temporal.api.enums.v1.TaskQueueType"\x18\n\x16ShutdownWorkerResponse"\xe9\x01\n\x14QueryWorkflowRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x33\n\x05query\x18\x03 \x01(\x0b\x32$.temporal.api.query.v1.WorkflowQuery\x12K\n\x16query_reject_condition\x18\x04 \x01(\x0e\x32+.temporal.api.enums.v1.QueryRejectCondition"\xb9\x01\n\x15QueryWorkflowResponse\x12\x36\n\x0cquery_result\x18\x01 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12<\n\x0equery_rejected\x18\x02 \x01(\x0b\x32$.temporal.api.query.v1.QueryRejected\x12*\n\x04link\x18\x03 \x01(\x0b\x32\x1c.temporal.api.common.v1.Link"s\n DescribeWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution"\x99\x05\n!DescribeWorkflowExecutionResponse\x12K\n\x10\x65xecution_config\x18\x01 \x01(\x0b\x32\x31.temporal.api.workflow.v1.WorkflowExecutionConfig\x12P\n\x17workflow_execution_info\x18\x02 \x01(\x0b\x32/.temporal.api.workflow.v1.WorkflowExecutionInfo\x12I\n\x12pending_activities\x18\x03 \x03(\x0b\x32-.temporal.api.workflow.v1.PendingActivityInfo\x12M\n\x10pending_children\x18\x04 \x03(\x0b\x32\x33.temporal.api.workflow.v1.PendingChildExecutionInfo\x12P\n\x15pending_workflow_task\x18\x05 \x01(\x0b\x32\x31.temporal.api.workflow.v1.PendingWorkflowTaskInfo\x12\x39\n\tcallbacks\x18\x06 \x03(\x0b\x32&.temporal.api.workflow.v1.CallbackInfo\x12U\n\x18pending_nexus_operations\x18\x07 \x03(\x0b\x32\x33.temporal.api.workflow.v1.PendingNexusOperationInfo\x12W\n\x16workflow_extended_info\x18\x08 \x01(\x0b\x32\x37.temporal.api.workflow.v1.WorkflowExecutionExtendedInfo"\x90\x04\n\x18\x44\x65scribeTaskQueueRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x38\n\ntask_queue\x18\x02 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12=\n\x0ftask_queue_type\x18\x03 \x01(\x0e\x32$.temporal.api.enums.v1.TaskQueueType\x12\x14\n\x0creport_stats\x18\x08 \x01(\x08\x12\x15\n\rreport_config\x18\x0b \x01(\x08\x12%\n\x19include_task_queue_status\x18\x04 \x01(\x08\x42\x02\x18\x01\x12\x42\n\x08\x61pi_mode\x18\x05 \x01(\x0e\x32,.temporal.api.enums.v1.DescribeTaskQueueModeB\x02\x18\x01\x12J\n\x08versions\x18\x06 \x01(\x0b\x32\x34.temporal.api.taskqueue.v1.TaskQueueVersionSelectionB\x02\x18\x01\x12\x42\n\x10task_queue_types\x18\x07 \x03(\x0e\x32$.temporal.api.enums.v1.TaskQueueTypeB\x02\x18\x01\x12\x1a\n\x0ereport_pollers\x18\t \x01(\x08\x42\x02\x18\x01\x12$\n\x18report_task_reachability\x18\n \x01(\x08\x42\x02\x18\x01"\xec\x07\n\x19\x44\x65scribeTaskQueueResponse\x12\x36\n\x07pollers\x18\x01 \x03(\x0b\x32%.temporal.api.taskqueue.v1.PollerInfo\x12\x38\n\x05stats\x18\x05 \x01(\x0b\x32).temporal.api.taskqueue.v1.TaskQueueStats\x12q\n\x15stats_by_priority_key\x18\x08 \x03(\x0b\x32R.temporal.api.workflowservice.v1.DescribeTaskQueueResponse.StatsByPriorityKeyEntry\x12K\n\x0fversioning_info\x18\x04 \x01(\x0b\x32\x32.temporal.api.taskqueue.v1.TaskQueueVersioningInfo\x12:\n\x06\x63onfig\x18\x06 \x01(\x0b\x32*.temporal.api.taskqueue.v1.TaskQueueConfig\x12k\n\x14\x65\x66\x66\x65\x63tive_rate_limit\x18\x07 \x01(\x0b\x32M.temporal.api.workflowservice.v1.DescribeTaskQueueResponse.EffectiveRateLimit\x12I\n\x11task_queue_status\x18\x02 \x01(\x0b\x32*.temporal.api.taskqueue.v1.TaskQueueStatusB\x02\x18\x01\x12g\n\rversions_info\x18\x03 \x03(\x0b\x32L.temporal.api.workflowservice.v1.DescribeTaskQueueResponse.VersionsInfoEntryB\x02\x18\x01\x1a\x64\n\x17StatsByPriorityKeyEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\x38\n\x05value\x18\x02 \x01(\x0b\x32).temporal.api.taskqueue.v1.TaskQueueStats:\x02\x38\x01\x1at\n\x12\x45\x66\x66\x65\x63tiveRateLimit\x12\x1b\n\x13requests_per_second\x18\x01 \x01(\x02\x12\x41\n\x11rate_limit_source\x18\x02 \x01(\x0e\x32&.temporal.api.enums.v1.RateLimitSource\x1a\x64\n\x11VersionsInfoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12>\n\x05value\x18\x02 \x01(\x0b\x32/.temporal.api.taskqueue.v1.TaskQueueVersionInfo:\x02\x38\x01"\x17\n\x15GetClusterInfoRequest"\xd1\x03\n\x16GetClusterInfoResponse\x12h\n\x11supported_clients\x18\x01 \x03(\x0b\x32M.temporal.api.workflowservice.v1.GetClusterInfoResponse.SupportedClientsEntry\x12\x16\n\x0eserver_version\x18\x02 \x01(\t\x12\x12\n\ncluster_id\x18\x03 \x01(\t\x12:\n\x0cversion_info\x18\x04 \x01(\x0b\x32$.temporal.api.version.v1.VersionInfo\x12\x14\n\x0c\x63luster_name\x18\x05 \x01(\t\x12\x1b\n\x13history_shard_count\x18\x06 \x01(\x05\x12\x19\n\x11persistence_store\x18\x07 \x01(\t\x12\x18\n\x10visibility_store\x18\x08 \x01(\t\x12 \n\x18initial_failover_version\x18\t \x01(\x03\x12"\n\x1a\x66\x61ilover_version_increment\x18\n \x01(\x03\x1a\x37\n\x15SupportedClientsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"\x16\n\x14GetSystemInfoRequest"\xc1\x04\n\x15GetSystemInfoResponse\x12\x16\n\x0eserver_version\x18\x01 \x01(\t\x12Y\n\x0c\x63\x61pabilities\x18\x02 \x01(\x0b\x32\x43.temporal.api.workflowservice.v1.GetSystemInfoResponse.Capabilities\x1a\xb4\x03\n\x0c\x43\x61pabilities\x12\x1f\n\x17signal_and_query_header\x18\x01 \x01(\x08\x12&\n\x1einternal_error_differentiation\x18\x02 \x01(\x08\x12*\n"activity_failure_include_heartbeat\x18\x03 \x01(\x08\x12\x1a\n\x12supports_schedules\x18\x04 \x01(\x08\x12"\n\x1a\x65ncoded_failure_attributes\x18\x05 \x01(\x08\x12!\n\x19\x62uild_id_based_versioning\x18\x06 \x01(\x08\x12\x13\n\x0bupsert_memo\x18\x07 \x01(\x08\x12\x1c\n\x14\x65\x61ger_workflow_start\x18\x08 \x01(\x08\x12\x14\n\x0csdk_metadata\x18\t \x01(\x08\x12\'\n\x1f\x63ount_group_by_execution_status\x18\n \x01(\x08\x12\r\n\x05nexus\x18\x0b \x01(\x08\x12!\n\x19server_scaled_deployments\x18\x0c \x01(\x08\x12(\n server_scaled_provider_cloud_run\x18\r \x01(\x08"m\n\x1eListTaskQueuePartitionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x38\n\ntask_queue\x18\x02 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue"\xdf\x01\n\x1fListTaskQueuePartitionsResponse\x12]\n\x1e\x61\x63tivity_task_queue_partitions\x18\x01 \x03(\x0b\x32\x35.temporal.api.taskqueue.v1.TaskQueuePartitionMetadata\x12]\n\x1eworkflow_task_queue_partitions\x18\x02 \x03(\x0b\x32\x35.temporal.api.taskqueue.v1.TaskQueuePartitionMetadata"\xcc\x02\n\x15\x43reateScheduleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bschedule_id\x18\x02 \x01(\t\x12\x34\n\x08schedule\x18\x03 \x01(\x0b\x32".temporal.api.schedule.v1.Schedule\x12>\n\rinitial_patch\x18\x04 \x01(\x0b\x32\'.temporal.api.schedule.v1.SchedulePatch\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x12\n\nrequest_id\x18\x06 \x01(\t\x12*\n\x04memo\x18\x07 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x08 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes"0\n\x16\x43reateScheduleResponse\x12\x16\n\x0e\x63onflict_token\x18\x01 \x01(\x0c"A\n\x17\x44\x65scribeScheduleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bschedule_id\x18\x02 \x01(\t"\x8f\x02\n\x18\x44\x65scribeScheduleResponse\x12\x34\n\x08schedule\x18\x01 \x01(\x0b\x32".temporal.api.schedule.v1.Schedule\x12\x34\n\x04info\x18\x02 \x01(\x0b\x32&.temporal.api.schedule.v1.ScheduleInfo\x12*\n\x04memo\x18\x03 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x04 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12\x16\n\x0e\x63onflict_token\x18\x05 \x01(\x0c"\xa4\x02\n\x15UpdateScheduleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bschedule_id\x18\x02 \x01(\t\x12\x34\n\x08schedule\x18\x03 \x01(\x0b\x32".temporal.api.schedule.v1.Schedule\x12\x16\n\x0e\x63onflict_token\x18\x04 \x01(\x0c\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x12\n\nrequest_id\x18\x06 \x01(\t\x12\x43\n\x11search_attributes\x18\x07 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12*\n\x04memo\x18\x08 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo"\x18\n\x16UpdateScheduleResponse"\x9c\x01\n\x14PatchScheduleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bschedule_id\x18\x02 \x01(\t\x12\x36\n\x05patch\x18\x03 \x01(\x0b\x32\'.temporal.api.schedule.v1.SchedulePatch\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t"\x17\n\x15PatchScheduleResponse"\xa8\x01\n ListScheduleMatchingTimesRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bschedule_id\x18\x02 \x01(\t\x12.\n\nstart_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08\x65nd_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"S\n!ListScheduleMatchingTimesResponse\x12.\n\nstart_time\x18\x01 \x03(\x0b\x32\x1a.google.protobuf.Timestamp"Q\n\x15\x44\x65leteScheduleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bschedule_id\x18\x02 \x01(\t\x12\x10\n\x08identity\x18\x03 \x01(\t"\x18\n\x16\x44\x65leteScheduleResponse"l\n\x14ListSchedulesRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x19\n\x11maximum_page_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t"p\n\x15ListSchedulesResponse\x12>\n\tschedules\x18\x01 \x03(\x0b\x32+.temporal.api.schedule.v1.ScheduleListEntry\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"9\n\x15\x43ountSchedulesRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\r\n\x05query\x18\x02 \x01(\t"\xdb\x01\n\x16\x43ountSchedulesResponse\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12X\n\x06groups\x18\x02 \x03(\x0b\x32H.temporal.api.workflowservice.v1.CountSchedulesResponse.AggregationGroup\x1aX\n\x10\x41ggregationGroup\x12\x35\n\x0cgroup_values\x18\x01 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\r\n\x05\x63ount\x18\x02 \x01(\x03"\x86\x05\n\'UpdateWorkerBuildIdCompatibilityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x12\n\ntask_queue\x18\x02 \x01(\t\x12-\n#add_new_build_id_in_new_default_set\x18\x03 \x01(\tH\x00\x12\x87\x01\n\x1b\x61\x64\x64_new_compatible_build_id\x18\x04 \x01(\x0b\x32`.temporal.api.workflowservice.v1.UpdateWorkerBuildIdCompatibilityRequest.AddNewCompatibleVersionH\x00\x12!\n\x17promote_set_by_build_id\x18\x05 \x01(\tH\x00\x12%\n\x1bpromote_build_id_within_set\x18\x06 \x01(\tH\x00\x12h\n\nmerge_sets\x18\x07 \x01(\x0b\x32R.temporal.api.workflowservice.v1.UpdateWorkerBuildIdCompatibilityRequest.MergeSetsH\x00\x1ao\n\x17\x41\x64\x64NewCompatibleVersion\x12\x14\n\x0cnew_build_id\x18\x01 \x01(\t\x12$\n\x1c\x65xisting_compatible_build_id\x18\x02 \x01(\t\x12\x18\n\x10make_set_default\x18\x03 \x01(\x08\x1aI\n\tMergeSets\x12\x1c\n\x14primary_set_build_id\x18\x01 \x01(\t\x12\x1e\n\x16secondary_set_build_id\x18\x02 \x01(\tB\x0b\n\toperation"@\n(UpdateWorkerBuildIdCompatibilityResponseJ\x04\x08\x01\x10\x02R\x0eversion_set_id"_\n$GetWorkerBuildIdCompatibilityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x12\n\ntask_queue\x18\x02 \x01(\t\x12\x10\n\x08max_sets\x18\x03 \x01(\x05"t\n%GetWorkerBuildIdCompatibilityResponse\x12K\n\x12major_version_sets\x18\x01 \x03(\x0b\x32/.temporal.api.taskqueue.v1.CompatibleVersionSet"\xb5\r\n"UpdateWorkerVersioningRulesRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x12\n\ntask_queue\x18\x02 \x01(\t\x12\x16\n\x0e\x63onflict_token\x18\x03 \x01(\x0c\x12\x81\x01\n\x16insert_assignment_rule\x18\x04 \x01(\x0b\x32_.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.InsertBuildIdAssignmentRuleH\x00\x12\x83\x01\n\x17replace_assignment_rule\x18\x05 \x01(\x0b\x32`.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.ReplaceBuildIdAssignmentRuleH\x00\x12\x81\x01\n\x16\x64\x65lete_assignment_rule\x18\x06 \x01(\x0b\x32_.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.DeleteBuildIdAssignmentRuleH\x00\x12\x8c\x01\n\x1c\x61\x64\x64_compatible_redirect_rule\x18\x07 \x01(\x0b\x32\x64.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.AddCompatibleBuildIdRedirectRuleH\x00\x12\x94\x01\n replace_compatible_redirect_rule\x18\x08 \x01(\x0b\x32h.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.ReplaceCompatibleBuildIdRedirectRuleH\x00\x12\x92\x01\n\x1f\x64\x65lete_compatible_redirect_rule\x18\t \x01(\x0b\x32g.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.DeleteCompatibleBuildIdRedirectRuleH\x00\x12l\n\x0f\x63ommit_build_id\x18\n \x01(\x0b\x32Q.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest.CommitBuildIdH\x00\x1aq\n\x1bInsertBuildIdAssignmentRule\x12\x12\n\nrule_index\x18\x01 \x01(\x05\x12>\n\x04rule\x18\x02 \x01(\x0b\x32\x30.temporal.api.taskqueue.v1.BuildIdAssignmentRule\x1a\x81\x01\n\x1cReplaceBuildIdAssignmentRule\x12\x12\n\nrule_index\x18\x01 \x01(\x05\x12>\n\x04rule\x18\x02 \x01(\x0b\x32\x30.temporal.api.taskqueue.v1.BuildIdAssignmentRule\x12\r\n\x05\x66orce\x18\x03 \x01(\x08\x1a@\n\x1b\x44\x65leteBuildIdAssignmentRule\x12\x12\n\nrule_index\x18\x01 \x01(\x05\x12\r\n\x05\x66orce\x18\x02 \x01(\x08\x1aj\n AddCompatibleBuildIdRedirectRule\x12\x46\n\x04rule\x18\x01 \x01(\x0b\x32\x38.temporal.api.taskqueue.v1.CompatibleBuildIdRedirectRule\x1an\n$ReplaceCompatibleBuildIdRedirectRule\x12\x46\n\x04rule\x18\x01 \x01(\x0b\x32\x38.temporal.api.taskqueue.v1.CompatibleBuildIdRedirectRule\x1a>\n#DeleteCompatibleBuildIdRedirectRule\x12\x17\n\x0fsource_build_id\x18\x01 \x01(\t\x1a\x37\n\rCommitBuildId\x12\x17\n\x0ftarget_build_id\x18\x01 \x01(\t\x12\r\n\x05\x66orce\x18\x02 \x01(\x08\x42\x0b\n\toperation"\xfc\x01\n#UpdateWorkerVersioningRulesResponse\x12U\n\x10\x61ssignment_rules\x18\x01 \x03(\x0b\x32;.temporal.api.taskqueue.v1.TimestampedBuildIdAssignmentRule\x12\x66\n\x19\x63ompatible_redirect_rules\x18\x02 \x03(\x0b\x32\x43.temporal.api.taskqueue.v1.TimestampedCompatibleBuildIdRedirectRule\x12\x16\n\x0e\x63onflict_token\x18\x03 \x01(\x0c"H\n\x1fGetWorkerVersioningRulesRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x12\n\ntask_queue\x18\x02 \x01(\t"\xf9\x01\n GetWorkerVersioningRulesResponse\x12U\n\x10\x61ssignment_rules\x18\x01 \x03(\x0b\x32;.temporal.api.taskqueue.v1.TimestampedBuildIdAssignmentRule\x12\x66\n\x19\x63ompatible_redirect_rules\x18\x02 \x03(\x0b\x32\x43.temporal.api.taskqueue.v1.TimestampedCompatibleBuildIdRedirectRule\x12\x16\n\x0e\x63onflict_token\x18\x03 \x01(\x0c"\x9c\x01\n GetWorkerTaskReachabilityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tbuild_ids\x18\x02 \x03(\t\x12\x13\n\x0btask_queues\x18\x03 \x03(\t\x12=\n\x0creachability\x18\x04 \x01(\x0e\x32\'.temporal.api.enums.v1.TaskReachability"r\n!GetWorkerTaskReachabilityResponse\x12M\n\x15\x62uild_id_reachability\x18\x01 \x03(\x0b\x32..temporal.api.taskqueue.v1.BuildIdReachability"\x85\x02\n\x1eUpdateWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x1e\n\x16\x66irst_execution_run_id\x18\x03 \x01(\t\x12\x37\n\x0bwait_policy\x18\x04 \x01(\x0b\x32".temporal.api.update.v1.WaitPolicy\x12\x30\n\x07request\x18\x05 \x01(\x0b\x32\x1f.temporal.api.update.v1.Request"\x83\x02\n\x1fUpdateWorkflowExecutionResponse\x12\x35\n\nupdate_ref\x18\x01 \x01(\x0b\x32!.temporal.api.update.v1.UpdateRef\x12\x30\n\x07outcome\x18\x02 \x01(\x0b\x32\x1f.temporal.api.update.v1.Outcome\x12K\n\x05stage\x18\x03 \x01(\x0e\x32<.temporal.api.enums.v1.UpdateWorkflowExecutionLifecycleStage\x12*\n\x04link\x18\x04 \x01(\x0b\x32\x1c.temporal.api.common.v1.Link"\xd6\n\n\x1aStartBatchOperationRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x18\n\x10visibility_query\x18\x02 \x01(\t\x12\x0e\n\x06job_id\x18\x03 \x01(\t\x12\x0e\n\x06reason\x18\x04 \x01(\t\x12\x41\n\nexecutions\x18\x05 \x03(\x0b\x32).temporal.api.common.v1.WorkflowExecutionB\x02\x18\x01\x12<\n\x11target_executions\x18\x16 \x03(\x0b\x32!.temporal.api.common.v1.Execution\x12!\n\x19max_operations_per_second\x18\x06 \x01(\x02\x12Q\n\x15termination_operation\x18\n \x01(\x0b\x32\x30.temporal.api.batch.v1.BatchOperationTerminationH\x00\x12G\n\x10signal_operation\x18\x0b \x01(\x0b\x32+.temporal.api.batch.v1.BatchOperationSignalH\x00\x12S\n\x16\x63\x61ncellation_operation\x18\x0c \x01(\x0b\x32\x31.temporal.api.batch.v1.BatchOperationCancellationH\x00\x12K\n\x12\x64\x65letion_operation\x18\r \x01(\x0b\x32-.temporal.api.batch.v1.BatchOperationDeletionH\x00\x12\x45\n\x0freset_operation\x18\x0e \x01(\x0b\x32*.temporal.api.batch.v1.BatchOperationResetH\x00\x12p\n!update_workflow_options_operation\x18\x0f \x01(\x0b\x32\x43.temporal.api.batch.v1.BatchOperationUpdateWorkflowExecutionOptionsH\x00\x12^\n\x1cunpause_activities_operation\x18\x10 \x01(\x0b\x32\x36.temporal.api.batch.v1.BatchOperationUnpauseActivitiesH\x00\x12Z\n\x1areset_activities_operation\x18\x11 \x01(\x0b\x32\x34.temporal.api.batch.v1.BatchOperationResetActivitiesH\x00\x12g\n!update_activity_options_operation\x18\x12 \x01(\x0b\x32:.temporal.api.batch.v1.BatchOperationUpdateActivityOptionsH\x00\x12\\\n\x1b\x63\x61ncel_activities_operation\x18\x13 \x01(\x0b\x32\x35.temporal.api.batch.v1.BatchOperationCancelActivitiesH\x00\x12\x62\n\x1eterminate_activities_operation\x18\x14 \x01(\x0b\x32\x38.temporal.api.batch.v1.BatchOperationTerminateActivitiesH\x00\x12\\\n\x1b\x64\x65lete_activities_operation\x18\x15 \x01(\x0b\x32\x35.temporal.api.batch.v1.BatchOperationDeleteActivitiesH\x00\x42\x0b\n\toperation"\x1d\n\x1bStartBatchOperationResponse"`\n\x19StopBatchOperationRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x0e\n\x06job_id\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t"\x1c\n\x1aStopBatchOperationResponse"B\n\x1d\x44\x65scribeBatchOperationRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x0e\n\x06job_id\x18\x02 \x01(\t"\xd8\x03\n\x1e\x44\x65scribeBatchOperationResponse\x12\x41\n\x0eoperation_type\x18\x01 \x01(\x0e\x32).temporal.api.enums.v1.BatchOperationType\x12\x0e\n\x06job_id\x18\x02 \x01(\t\x12\x39\n\x05state\x18\x03 \x01(\x0e\x32*.temporal.api.enums.v1.BatchOperationState\x12.\n\nstart_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nclose_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x1d\n\x15total_operation_count\x18\x06 \x01(\x03\x12 \n\x18\x63omplete_operation_count\x18\x07 \x01(\x03\x12\x1f\n\x17\x66\x61ilure_operation_count\x18\x08 \x01(\x03\x12\x10\n\x08identity\x18\t \x01(\t\x12\x0e\n\x06reason\x18\n \x01(\t\x12\r\n\x05query\x18\x0b \x01(\t\x12\x35\n\nexecutions\x18\x0c \x03(\x0b\x32!.temporal.api.common.v1.Execution"[\n\x1aListBatchOperationsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c"y\n\x1bListBatchOperationsResponse\x12\x41\n\x0eoperation_info\x18\x01 \x03(\x0b\x32).temporal.api.batch.v1.BatchOperationInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"\xb9\x01\n"PollWorkflowExecutionUpdateRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x35\n\nupdate_ref\x18\x02 \x01(\x0b\x32!.temporal.api.update.v1.UpdateRef\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x37\n\x0bwait_policy\x18\x04 \x01(\x0b\x32".temporal.api.update.v1.WaitPolicy"\xdb\x01\n#PollWorkflowExecutionUpdateResponse\x12\x30\n\x07outcome\x18\x01 \x01(\x0b\x32\x1f.temporal.api.update.v1.Outcome\x12K\n\x05stage\x18\x02 \x01(\x0e\x32<.temporal.api.enums.v1.UpdateWorkflowExecutionLifecycleStage\x12\x35\n\nupdate_ref\x18\x03 \x01(\x0b\x32!.temporal.api.update.v1.UpdateRef"\xa0\x03\n\x19PollNexusTaskQueueRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x38\n\ntask_queue\x18\x03 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12\x17\n\x0fpoller_group_id\x18\t \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x1b\n\x13worker_instance_key\x18\x08 \x01(\t\x12Z\n\x1bworker_version_capabilities\x18\x04 \x01(\x0b\x32\x31.temporal.api.common.v1.WorkerVersionCapabilitiesB\x02\x18\x01\x12O\n\x12\x64\x65ployment_options\x18\x06 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentOptions\x12\x41\n\x10worker_heartbeat\x18\x07 \x03(\x0b\x32\'.temporal.api.worker.v1.WorkerHeartbeat"\xe2\x02\n\x1aPollNexusTaskQueueResponse\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12/\n\x07request\x18\x02 \x01(\x0b\x32\x1e.temporal.api.nexus.v1.Request\x12Q\n\x17poller_scaling_decision\x18\x03 \x01(\x0b\x32\x30.temporal.api.taskqueue.v1.PollerScalingDecision\x12\x17\n\x0fpoller_group_id\x18\x04 \x01(\t\x12J\n\x12poller_group_infos\x18\x05 \x03(\x0b\x32*.temporal.api.taskqueue.v1.PollerGroupInfoB\x02\x18\x01\x12G\n\x12poller_groups_info\x18\x06 \x01(\x0b\x32+.temporal.api.taskqueue.v1.PollerGroupsInfo"\xa7\x01\n RespondNexusTaskCompletedRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\ntask_token\x18\x03 \x01(\x0c\x12\x31\n\x08response\x18\x04 \x01(\x0b\x32\x1f.temporal.api.nexus.v1.Response\x12\x17\n\x0fpoller_group_id\x18\x05 \x01(\t"#\n!RespondNexusTaskCompletedResponse"\xdc\x01\n\x1dRespondNexusTaskFailedRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\ntask_token\x18\x03 \x01(\x0c\x12\x36\n\x05\x65rror\x18\x04 \x01(\x0b\x32#.temporal.api.nexus.v1.HandlerErrorB\x02\x18\x01\x12\x31\n\x07\x66\x61ilure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x17\n\x0fpoller_group_id\x18\x06 \x01(\t" \n\x1eRespondNexusTaskFailedResponse"\xf4\x02\n\x1c\x45xecuteMultiOperationRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12[\n\noperations\x18\x02 \x03(\x0b\x32G.temporal.api.workflowservice.v1.ExecuteMultiOperationRequest.Operation\x12\x13\n\x0bresource_id\x18\x03 \x01(\t\x1a\xce\x01\n\tOperation\x12X\n\x0estart_workflow\x18\x01 \x01(\x0b\x32>.temporal.api.workflowservice.v1.StartWorkflowExecutionRequestH\x00\x12Z\n\x0fupdate_workflow\x18\x02 \x01(\x0b\x32?.temporal.api.workflowservice.v1.UpdateWorkflowExecutionRequestH\x00\x42\x0b\n\toperation"\xcc\x02\n\x1d\x45xecuteMultiOperationResponse\x12Z\n\tresponses\x18\x01 \x03(\x0b\x32G.temporal.api.workflowservice.v1.ExecuteMultiOperationResponse.Response\x1a\xce\x01\n\x08Response\x12Y\n\x0estart_workflow\x18\x01 \x01(\x0b\x32?.temporal.api.workflowservice.v1.StartWorkflowExecutionResponseH\x00\x12[\n\x0fupdate_workflow\x18\x02 \x01(\x0b\x32@.temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponseH\x00\x42\n\n\x08response"\xd0\x02\n\x1cUpdateActivityOptionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x43\n\x10\x61\x63tivity_options\x18\x04 \x01(\x0b\x32).temporal.api.activity.v1.ActivityOptions\x12/\n\x0bupdate_mask\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.FieldMask\x12\x0c\n\x02id\x18\x06 \x01(\tH\x00\x12\x0e\n\x04type\x18\x07 \x01(\tH\x00\x12\x13\n\tmatch_all\x18\t \x01(\x08H\x00\x12\x18\n\x10restore_original\x18\x08 \x01(\x08\x42\n\n\x08\x61\x63tivity"\xbf\x02\n%UpdateActivityExecutionOptionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x03 \x01(\t\x12\x0e\n\x06run_id\x18\x04 \x01(\t\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x43\n\x10\x61\x63tivity_options\x18\x06 \x01(\x0b\x32).temporal.api.activity.v1.ActivityOptions\x12/\n\x0bupdate_mask\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.FieldMask\x12\x18\n\x10restore_original\x18\x08 \x01(\x08\x12\x13\n\x0bresource_id\x18\t \x01(\t\x12\x12\n\nrequest_id\x18\n \x01(\t"d\n\x1dUpdateActivityOptionsResponse\x12\x43\n\x10\x61\x63tivity_options\x18\x01 \x01(\x0b\x32).temporal.api.activity.v1.ActivityOptions"m\n&UpdateActivityExecutionOptionsResponse\x12\x43\n\x10\x61\x63tivity_options\x18\x01 \x01(\x0b\x32).temporal.api.activity.v1.ActivityOptions"\xc7\x01\n\x14PauseActivityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x0c\n\x02id\x18\x04 \x01(\tH\x00\x12\x0e\n\x04type\x18\x05 \x01(\tH\x00\x12\x0e\n\x06reason\x18\x06 \x01(\t\x12\x12\n\nrequest_id\x18\x07 \x01(\tB\n\n\x08\x61\x63tivity"\xb7\x01\n\x1dPauseActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x03 \x01(\t\x12\x0e\n\x06run_id\x18\x04 \x01(\t\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x0e\n\x06reason\x18\x06 \x01(\t\x12\x13\n\x0bresource_id\x18\x07 \x01(\t\x12\x12\n\nrequest_id\x18\x08 \x01(\t"\x17\n\x15PauseActivityResponse" \n\x1ePauseActivityExecutionResponse"\x98\x02\n\x16UnpauseActivityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x0c\n\x02id\x18\x04 \x01(\tH\x00\x12\x0e\n\x04type\x18\x05 \x01(\tH\x00\x12\x15\n\x0bunpause_all\x18\x06 \x01(\x08H\x00\x12\x16\n\x0ereset_attempts\x18\x07 \x01(\x08\x12\x17\n\x0freset_heartbeat\x18\x08 \x01(\x08\x12)\n\x06jitter\x18\t \x01(\x0b\x32\x19.google.protobuf.DurationB\n\n\x08\x61\x63tivity"\x91\x02\n\x1fUnpauseActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x03 \x01(\t\x12\x0e\n\x06run_id\x18\x04 \x01(\t\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x0e\n\x06reason\x18\x08 \x01(\t\x12)\n\x06jitter\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x13\n\x0bresource_id\x18\n \x01(\t\x12\x12\n\nrequest_id\x18\x0b \x01(\tJ\x04\x08\x06\x10\x07J\x04\x08\x07\x10\x08R\x0ereset_attemptsR\x0freset_heartbeat"\x19\n\x17UnpauseActivityResponse""\n UnpauseActivityExecutionResponse"\xb3\x02\n\x14ResetActivityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x0c\n\x02id\x18\x04 \x01(\tH\x00\x12\x0e\n\x04type\x18\x05 \x01(\tH\x00\x12\x13\n\tmatch_all\x18\n \x01(\x08H\x00\x12\x17\n\x0freset_heartbeat\x18\x06 \x01(\x08\x12\x13\n\x0bkeep_paused\x18\x07 \x01(\x08\x12)\n\x06jitter\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12 \n\x18restore_original_options\x18\t \x01(\x08\x42\n\n\x08\x61\x63tivity"\xa2\x02\n\x1dResetActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x03 \x01(\t\x12\x0e\n\x06run_id\x18\x04 \x01(\t\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x13\n\x0bkeep_paused\x18\x06 \x01(\x08\x12)\n\x06jitter\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12 \n\x18restore_original_options\x18\x08 \x01(\x08\x12\x13\n\x0bresource_id\x18\t \x01(\t\x12\x12\n\nrequest_id\x18\n \x01(\t\x12\x17\n\x0freset_heartbeat\x18\x0b \x01(\x08"\x17\n\x15ResetActivityResponse" \n\x1eResetActivityExecutionResponse"\x9c\x02\n%UpdateWorkflowExecutionOptionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12V\n\x1aworkflow_execution_options\x18\x03 \x01(\x0b\x32\x32.temporal.api.workflow.v1.WorkflowExecutionOptions\x12/\n\x0bupdate_mask\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.FieldMask\x12\x10\n\x08identity\x18\x05 \x01(\t"\xb1\x01\n&UpdateWorkflowExecutionOptionsResponse\x12V\n\x1aworkflow_execution_options\x18\x01 \x01(\x0b\x32\x32.temporal.api.workflow.v1.WorkflowExecutionOptions\x12/\n\x0bupdate_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"j\n\x19\x44\x65scribeDeploymentRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12:\n\ndeployment\x18\x02 \x01(\x0b\x32&.temporal.api.deployment.v1.Deployment"a\n\x1a\x44\x65scribeDeploymentResponse\x12\x43\n\x0f\x64\x65ployment_info\x18\x01 \x01(\x0b\x32*.temporal.api.deployment.v1.DeploymentInfo"\xc2\x01\n&DescribeWorkerDeploymentVersionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x07version\x18\x02 \x01(\tB\x02\x18\x01\x12O\n\x12\x64\x65ployment_version\x18\x03 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x1f\n\x17report_task_queue_stats\x18\x04 \x01(\x08"\x8c\x05\n\'DescribeWorkerDeploymentVersionResponse\x12_\n\x1eworker_deployment_version_info\x18\x01 \x01(\x0b\x32\x37.temporal.api.deployment.v1.WorkerDeploymentVersionInfo\x12v\n\x13version_task_queues\x18\x02 \x03(\x0b\x32Y.temporal.api.workflowservice.v1.DescribeWorkerDeploymentVersionResponse.VersionTaskQueue\x1a\x87\x03\n\x10VersionTaskQueue\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x32\n\x04type\x18\x02 \x01(\x0e\x32$.temporal.api.enums.v1.TaskQueueType\x12\x38\n\x05stats\x18\x03 \x01(\x0b\x32).temporal.api.taskqueue.v1.TaskQueueStats\x12\x90\x01\n\x15stats_by_priority_key\x18\x04 \x03(\x0b\x32q.temporal.api.workflowservice.v1.DescribeWorkerDeploymentVersionResponse.VersionTaskQueue.StatsByPriorityKeyEntry\x1a\x64\n\x17StatsByPriorityKeyEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\x38\n\x05value\x18\x02 \x01(\x0b\x32).temporal.api.taskqueue.v1.TaskQueueStats:\x02\x38\x01"M\n\x1f\x44\x65scribeWorkerDeploymentRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t"\x8c\x01\n DescribeWorkerDeploymentResponse\x12\x16\n\x0e\x63onflict_token\x18\x01 \x01(\x0c\x12P\n\x16worker_deployment_info\x18\x02 \x01(\x0b\x32\x30.temporal.api.deployment.v1.WorkerDeploymentInfo"l\n\x16ListDeploymentsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\x13\n\x0bseries_name\x18\x04 \x01(\t"w\n\x17ListDeploymentsResponse\x12\x17\n\x0fnext_page_token\x18\x01 \x01(\x0c\x12\x43\n\x0b\x64\x65ployments\x18\x02 \x03(\x0b\x32..temporal.api.deployment.v1.DeploymentListInfo"\xcd\x01\n\x1bSetCurrentDeploymentRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12:\n\ndeployment\x18\x02 \x01(\x0b\x32&.temporal.api.deployment.v1.Deployment\x12\x10\n\x08identity\x18\x03 \x01(\t\x12M\n\x0fupdate_metadata\x18\x04 \x01(\x0b\x32\x34.temporal.api.deployment.v1.UpdateDeploymentMetadata"\xb9\x01\n\x1cSetCurrentDeploymentResponse\x12K\n\x17\x63urrent_deployment_info\x18\x01 \x01(\x0b\x32*.temporal.api.deployment.v1.DeploymentInfo\x12L\n\x18previous_deployment_info\x18\x02 \x01(\x0b\x32*.temporal.api.deployment.v1.DeploymentInfo"\xe5\x01\n(SetWorkerDeploymentCurrentVersionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t\x12\x13\n\x07version\x18\x03 \x01(\tB\x02\x18\x01\x12\x10\n\x08\x62uild_id\x18\x07 \x01(\t\x12\x16\n\x0e\x63onflict_token\x18\x04 \x01(\x0c\x12\x10\n\x08identity\x18\x05 \x01(\t\x12"\n\x1aignore_missing_task_queues\x18\x06 \x01(\x08\x12\x18\n\x10\x61llow_no_pollers\x18\t \x01(\x08"\xbf\x01\n)SetWorkerDeploymentCurrentVersionResponse\x12\x16\n\x0e\x63onflict_token\x18\x01 \x01(\x0c\x12\x1c\n\x10previous_version\x18\x02 \x01(\tB\x02\x18\x01\x12\\\n\x1bprevious_deployment_version\x18\x03 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersionB\x02\x18\x01"\xf9\x01\n(SetWorkerDeploymentRampingVersionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t\x12\x13\n\x07version\x18\x03 \x01(\tB\x02\x18\x01\x12\x10\n\x08\x62uild_id\x18\x08 \x01(\t\x12\x12\n\npercentage\x18\x04 \x01(\x02\x12\x16\n\x0e\x63onflict_token\x18\x05 \x01(\x0c\x12\x10\n\x08identity\x18\x06 \x01(\t\x12"\n\x1aignore_missing_task_queues\x18\x07 \x01(\x08\x12\x18\n\x10\x61llow_no_pollers\x18\n \x01(\x08"\xe0\x01\n)SetWorkerDeploymentRampingVersionResponse\x12\x16\n\x0e\x63onflict_token\x18\x01 \x01(\x0c\x12\x1c\n\x10previous_version\x18\x02 \x01(\tB\x02\x18\x01\x12\\\n\x1bprevious_deployment_version\x18\x04 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersionB\x02\x18\x01\x12\x1f\n\x13previous_percentage\x18\x03 \x01(\x02\x42\x02\x18\x01"q\n\x1d\x43reateWorkerDeploymentRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t"8\n\x1e\x43reateWorkerDeploymentResponse\x12\x16\n\x0e\x63onflict_token\x18\x01 \x01(\x0c"]\n\x1cListWorkerDeploymentsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c"\x9f\x05\n\x1dListWorkerDeploymentsResponse\x12\x17\n\x0fnext_page_token\x18\x01 \x01(\x0c\x12r\n\x12worker_deployments\x18\x02 \x03(\x0b\x32V.temporal.api.workflowservice.v1.ListWorkerDeploymentsResponse.WorkerDeploymentSummary\x1a\xf0\x03\n\x17WorkerDeploymentSummary\x12\x0c\n\x04name\x18\x01 \x01(\t\x12/\n\x0b\x63reate_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x41\n\x0erouting_config\x18\x03 \x01(\x0b\x32).temporal.api.deployment.v1.RoutingConfig\x12o\n\x16latest_version_summary\x18\x04 \x01(\x0b\x32O.temporal.api.deployment.v1.WorkerDeploymentInfo.WorkerDeploymentVersionSummary\x12p\n\x17\x63urrent_version_summary\x18\x05 \x01(\x0b\x32O.temporal.api.deployment.v1.WorkerDeploymentInfo.WorkerDeploymentVersionSummary\x12p\n\x17ramping_version_summary\x18\x06 \x01(\x0b\x32O.temporal.api.deployment.v1.WorkerDeploymentInfo.WorkerDeploymentVersionSummary"\xf0\x01\n$CreateWorkerDeploymentVersionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12O\n\x12\x64\x65ployment_version\x18\x02 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12>\n\x0e\x63ompute_config\x18\x04 \x01(\x0b\x32&.temporal.api.compute.v1.ComputeConfig\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t"\'\n%CreateWorkerDeploymentVersionResponse"\xc8\x01\n$DeleteWorkerDeploymentVersionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x07version\x18\x02 \x01(\tB\x02\x18\x01\x12O\n\x12\x64\x65ployment_version\x18\x05 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x15\n\rskip_drainage\x18\x03 \x01(\x08\x12\x10\n\x08identity\x18\x04 \x01(\t"\'\n%DeleteWorkerDeploymentVersionResponse"]\n\x1d\x44\x65leteWorkerDeploymentRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t\x12\x10\n\x08identity\x18\x03 \x01(\t" \n\x1e\x44\x65leteWorkerDeploymentResponse"\x84\x04\n1UpdateWorkerDeploymentVersionComputeConfigRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12O\n\x12\x64\x65ployment_version\x18\x02 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x99\x01\n\x1d\x63ompute_config_scaling_groups\x18\x06 \x03(\x0b\x32r.temporal.api.workflowservice.v1.UpdateWorkerDeploymentVersionComputeConfigRequest.ComputeConfigScalingGroupsEntry\x12,\n$remove_compute_config_scaling_groups\x18\x07 \x03(\t\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x12\n\nrequest_id\x18\x04 \x01(\t\x1a{\n\x1f\x43omputeConfigScalingGroupsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12G\n\x05value\x18\x02 \x01(\x0b\x32\x38.temporal.api.compute.v1.ComputeConfigScalingGroupUpdate:\x02\x38\x01"4\n2UpdateWorkerDeploymentVersionComputeConfigResponse"\xf4\x03\n3ValidateWorkerDeploymentVersionComputeConfigRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12O\n\x12\x64\x65ployment_version\x18\x02 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12\x9b\x01\n\x1d\x63ompute_config_scaling_groups\x18\x06 \x03(\x0b\x32t.temporal.api.workflowservice.v1.ValidateWorkerDeploymentVersionComputeConfigRequest.ComputeConfigScalingGroupsEntry\x12,\n$remove_compute_config_scaling_groups\x18\x07 \x03(\t\x12\x10\n\x08identity\x18\x03 \x01(\t\x1a{\n\x1f\x43omputeConfigScalingGroupsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12G\n\x05value\x18\x02 \x01(\x0b\x32\x38.temporal.api.compute.v1.ComputeConfigScalingGroupUpdate:\x02\x38\x01"6\n4ValidateWorkerDeploymentVersionComputeConfigResponse"\xa2\x03\n,UpdateWorkerDeploymentVersionMetadataRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x07version\x18\x02 \x01(\tB\x02\x18\x01\x12O\n\x12\x64\x65ployment_version\x18\x05 \x01(\x0b\x32\x33.temporal.api.deployment.v1.WorkerDeploymentVersion\x12x\n\x0eupsert_entries\x18\x03 \x03(\x0b\x32`.temporal.api.workflowservice.v1.UpdateWorkerDeploymentVersionMetadataRequest.UpsertEntriesEntry\x12\x16\n\x0eremove_entries\x18\x04 \x03(\t\x12\x10\n\x08identity\x18\x06 \x01(\t\x1aU\n\x12UpsertEntriesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"n\n-UpdateWorkerDeploymentVersionMetadataResponse\x12=\n\x08metadata\x18\x01 \x01(\x0b\x32+.temporal.api.deployment.v1.VersionMetadata"\xbd\x01\n!SetWorkerDeploymentManagerRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65ployment_name\x18\x02 \x01(\t\x12\x1a\n\x10manager_identity\x18\x03 \x01(\tH\x00\x12\x0e\n\x04self\x18\x04 \x01(\x08H\x00\x12\x16\n\x0e\x63onflict_token\x18\x05 \x01(\x0c\x12\x10\n\x08identity\x18\x06 \x01(\tB\x16\n\x14new_manager_identity"c\n"SetWorkerDeploymentManagerResponse\x12\x16\n\x0e\x63onflict_token\x18\x01 \x01(\x0c\x12%\n\x19previous_manager_identity\x18\x02 \x01(\tB\x02\x18\x01"E\n\x1bGetCurrentDeploymentRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bseries_name\x18\x02 \x01(\t"k\n\x1cGetCurrentDeploymentResponse\x12K\n\x17\x63urrent_deployment_info\x18\x01 \x01(\x0b\x32*.temporal.api.deployment.v1.DeploymentInfo"q\n GetDeploymentReachabilityRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12:\n\ndeployment\x18\x02 \x01(\x0b\x32&.temporal.api.deployment.v1.Deployment"\xe3\x01\n!GetDeploymentReachabilityResponse\x12\x43\n\x0f\x64\x65ployment_info\x18\x01 \x01(\x0b\x32*.temporal.api.deployment.v1.DeploymentInfo\x12\x43\n\x0creachability\x18\x02 \x01(\x0e\x32-.temporal.api.enums.v1.DeploymentReachability\x12\x34\n\x10last_update_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xb4\x01\n\x19\x43reateWorkflowRuleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x35\n\x04spec\x18\x02 \x01(\x0b\x32\'.temporal.api.rules.v1.WorkflowRuleSpec\x12\x12\n\nforce_scan\x18\x03 \x01(\x08\x12\x12\n\nrequest_id\x18\x04 \x01(\t\x12\x10\n\x08identity\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t"_\n\x1a\x43reateWorkflowRuleResponse\x12\x31\n\x04rule\x18\x01 \x01(\x0b\x32#.temporal.api.rules.v1.WorkflowRule\x12\x0e\n\x06job_id\x18\x02 \x01(\t"A\n\x1b\x44\x65scribeWorkflowRuleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x0f\n\x07rule_id\x18\x02 \x01(\t"Q\n\x1c\x44\x65scribeWorkflowRuleResponse\x12\x31\n\x04rule\x18\x01 \x01(\x0b\x32#.temporal.api.rules.v1.WorkflowRule"?\n\x19\x44\x65leteWorkflowRuleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x0f\n\x07rule_id\x18\x02 \x01(\t"\x1c\n\x1a\x44\x65leteWorkflowRuleResponse"F\n\x18ListWorkflowRulesRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"h\n\x19ListWorkflowRulesResponse\x12\x32\n\x05rules\x18\x01 \x03(\x0b\x32#.temporal.api.rules.v1.WorkflowRule\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"\xce\x01\n\x1aTriggerWorkflowRuleRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12<\n\texecution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x0c\n\x02id\x18\x04 \x01(\tH\x00\x12\x37\n\x04spec\x18\x05 \x01(\x0b\x32\'.temporal.api.rules.v1.WorkflowRuleSpecH\x00\x12\x10\n\x08identity\x18\x03 \x01(\tB\x06\n\x04rule".\n\x1bTriggerWorkflowRuleResponse\x12\x0f\n\x07\x61pplied\x18\x01 \x01(\x08"\x9b\x01\n\x1cRecordWorkerHeartbeatRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x41\n\x10worker_heartbeat\x18\x03 \x03(\x0b\x32\'.temporal.api.worker.v1.WorkerHeartbeat\x12\x13\n\x0bresource_id\x18\x04 \x01(\t"\x1f\n\x1dRecordWorkerHeartbeatResponse"\x82\x01\n\x12ListWorkersRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t\x12\x1e\n\x16include_system_workers\x18\x05 \x01(\x08"\xa5\x01\n\x13ListWorkersResponse\x12<\n\x0cworkers_info\x18\x01 \x03(\x0b\x32".temporal.api.worker.v1.WorkerInfoB\x02\x18\x01\x12\x37\n\x07workers\x18\x03 \x03(\x0b\x32&.temporal.api.worker.v1.WorkerListInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"\xd5\x05\n\x1cUpdateTaskQueueConfigRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\ntask_queue\x18\x03 \x01(\t\x12=\n\x0ftask_queue_type\x18\x04 \x01(\x0e\x32$.temporal.api.enums.v1.TaskQueueType\x12n\n\x17update_queue_rate_limit\x18\x05 \x01(\x0b\x32M.temporal.api.workflowservice.v1.UpdateTaskQueueConfigRequest.RateLimitUpdate\x12}\n&update_fairness_key_rate_limit_default\x18\x06 \x01(\x0b\x32M.temporal.api.workflowservice.v1.UpdateTaskQueueConfigRequest.RateLimitUpdate\x12\x84\x01\n\x1dset_fairness_weight_overrides\x18\x07 \x03(\x0b\x32].temporal.api.workflowservice.v1.UpdateTaskQueueConfigRequest.SetFairnessWeightOverridesEntry\x12\'\n\x1funset_fairness_weight_overrides\x18\x08 \x03(\t\x1a[\n\x0fRateLimitUpdate\x12\x38\n\nrate_limit\x18\x01 \x01(\x0b\x32$.temporal.api.taskqueue.v1.RateLimit\x12\x0e\n\x06reason\x18\x02 \x01(\t\x1a\x41\n\x1fSetFairnessWeightOverridesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x02:\x02\x38\x01"[\n\x1dUpdateTaskQueueConfigResponse\x12:\n\x06\x63onfig\x18\x01 \x01(\x0b\x32*.temporal.api.taskqueue.v1.TaskQueueConfig"\x9e\x01\n\x18\x46\x65tchWorkerConfigRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x38\n\x08selector\x18\x06 \x01(\x0b\x32&.temporal.api.common.v1.WorkerSelector\x12\x13\n\x0bresource_id\x18\x07 \x01(\t"U\n\x19\x46\x65tchWorkerConfigResponse\x12\x38\n\rworker_config\x18\x01 \x01(\x0b\x32!.temporal.api.sdk.v1.WorkerConfig"\x8a\x02\n\x19UpdateWorkerConfigRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x38\n\rworker_config\x18\x04 \x01(\x0b\x32!.temporal.api.sdk.v1.WorkerConfig\x12/\n\x0bupdate_mask\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.FieldMask\x12\x38\n\x08selector\x18\x06 \x01(\x0b\x32&.temporal.api.common.v1.WorkerSelector\x12\x13\n\x0bresource_id\x18\x07 \x01(\t"d\n\x1aUpdateWorkerConfigResponse\x12:\n\rworker_config\x18\x01 \x01(\x0b\x32!.temporal.api.sdk.v1.WorkerConfigH\x00\x42\n\n\x08response"G\n\x15\x44\x65scribeWorkerRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x1b\n\x13worker_instance_key\x18\x02 \x01(\t"Q\n\x16\x44\x65scribeWorkerResponse\x12\x37\n\x0bworker_info\x18\x01 \x01(\x0b\x32".temporal.api.worker.v1.WorkerInfo"W\n\x13\x43ountWorkersRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\r\n\x05query\x18\x02 \x01(\t\x12\x1e\n\x16include_system_workers\x18\x03 \x01(\x08"%\n\x14\x43ountWorkersResponse\x12\r\n\x05\x63ount\x18\x01 \x01(\x03"\x8d\x01\n\x1dPauseWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x0e\n\x06reason\x18\x05 \x01(\t\x12\x12\n\nrequest_id\x18\x06 \x01(\t" \n\x1ePauseWorkflowExecutionResponse"\x8f\x01\n\x1fUnpauseWorkflowExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x0e\n\x06reason\x18\x05 \x01(\t\x12\x12\n\nrequest_id\x18\x06 \x01(\t""\n UnpauseWorkflowExecutionResponse"\x99\t\n\x1dStartActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\nrequest_id\x18\x03 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x04 \x01(\t\x12;\n\ractivity_type\x18\x05 \x01(\x0b\x32$.temporal.api.common.v1.ActivityType\x12\x38\n\ntask_queue\x18\x06 \x01(\x0b\x32$.temporal.api.taskqueue.v1.TaskQueue\x12<\n\x19schedule_to_close_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x0b \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12/\n\x05input\x18\x0c \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x45\n\x0fid_reuse_policy\x18\r \x01(\x0e\x32,.temporal.api.enums.v1.ActivityIdReusePolicy\x12K\n\x12id_conflict_policy\x18\x0e \x01(\x0e\x32/.temporal.api.enums.v1.ActivityIdConflictPolicy\x12\x43\n\x11search_attributes\x18\x0f \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12.\n\x06header\x18\x10 \x01(\x0b\x32\x1e.temporal.api.common.v1.Header\x12\x38\n\ruser_metadata\x18\x11 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x12\x32\n\x08priority\x18\x12 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12>\n\x14\x63ompletion_callbacks\x18\x13 \x03(\x0b\x32 .temporal.api.common.v1.Callback\x12+\n\x05links\x18\x14 \x03(\x0b\x32\x1c.temporal.api.common.v1.Link\x12\x46\n\x13on_conflict_options\x18\x15 \x01(\x0b\x32).temporal.api.common.v1.OnConflictOptions\x12.\n\x0bstart_delay\x18\x16 \x01(\x0b\x32\x19.google.protobuf.Duration"m\n\x1eStartActivityExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x0f\n\x07started\x18\x02 \x01(\x08\x12*\n\x04link\x18\x03 \x01(\x0b\x32\x1c.temporal.api.common.v1.Link"\xe4\x01\n DescribeActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x15\n\rinclude_input\x18\x04 \x01(\x08\x12\x17\n\x0finclude_outcome\x18\x05 \x01(\x08\x12\x17\n\x0flong_poll_token\x18\x06 \x01(\x0c\x12!\n\x19include_heartbeat_details\x18\x07 \x01(\x08\x12\x1c\n\x14include_last_failure\x18\x08 \x01(\x08"\xbc\x02\n!DescribeActivityExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12=\n\x04info\x18\x02 \x01(\x0b\x32/.temporal.api.activity.v1.ActivityExecutionInfo\x12/\n\x05input\x18\x03 \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x43\n\x07outcome\x18\x04 \x01(\x0b\x32\x32.temporal.api.activity.v1.ActivityExecutionOutcome\x12\x17\n\x0flong_poll_token\x18\x05 \x01(\x0c\x12\x39\n\tcallbacks\x18\x06 \x03(\x0b\x32&.temporal.api.activity.v1.CallbackInfo"V\n\x1cPollActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t"t\n\x1dPollActivityExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x43\n\x07outcome\x18\x02 \x01(\x0b\x32\x32.temporal.api.activity.v1.ActivityExecutionOutcome"m\n\x1dListActivityExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t"\x82\x01\n\x1eListActivityExecutionsResponse\x12G\n\nexecutions\x18\x01 \x03(\x0b\x32\x33.temporal.api.activity.v1.ActivityExecutionListInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"\xd1\x06\n#StartNexusOperationExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\nrequest_id\x18\x03 \x01(\t\x12\x14\n\x0coperation_id\x18\x04 \x01(\t\x12\x10\n\x08\x65ndpoint\x18\x05 \x01(\t\x12\x0f\n\x07service\x18\x06 \x01(\t\x12\x11\n\toperation\x18\x07 \x01(\t\x12<\n\x19schedule_to_close_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12.\n\x05input\x18\x0b \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12K\n\x0fid_reuse_policy\x18\x0c \x01(\x0e\x32\x32.temporal.api.enums.v1.NexusOperationIdReusePolicy\x12Q\n\x12id_conflict_policy\x18\r \x01(\x0e\x32\x35.temporal.api.enums.v1.NexusOperationIdConflictPolicy\x12\x43\n\x11search_attributes\x18\x0e \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12k\n\x0cnexus_header\x18\x0f \x03(\x0b\x32U.temporal.api.workflowservice.v1.StartNexusOperationExecutionRequest.NexusHeaderEntry\x12\x38\n\ruser_metadata\x18\x10 \x01(\x0b\x32!.temporal.api.sdk.v1.UserMetadata\x1a\x32\n\x10NexusHeaderEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"G\n$StartNexusOperationExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x0f\n\x07started\x18\x02 \x01(\x08"\xaa\x01\n&DescribeNexusOperationExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0coperation_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x15\n\rinclude_input\x18\x04 \x01(\x08\x12\x17\n\x0finclude_outcome\x18\x05 \x01(\x08\x12\x17\n\x0flong_poll_token\x18\x06 \x01(\x0c"\xb7\x02\n\'DescribeNexusOperationExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12@\n\x04info\x18\x02 \x01(\x0b\x32\x32.temporal.api.nexus.v1.NexusOperationExecutionInfo\x12.\n\x05input\x18\x03 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\x31\n\x06result\x18\x04 \x01(\x0b\x32\x1f.temporal.api.common.v1.PayloadH\x00\x12\x33\n\x07\x66\x61ilure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.FailureH\x00\x12\x17\n\x0flong_poll_token\x18\x06 \x01(\x0c\x42\t\n\x07outcome"\xa1\x01\n"PollNexusOperationExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0coperation_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x42\n\nwait_stage\x18\x04 \x01(\x0e\x32..temporal.api.enums.v1.NexusOperationWaitStage"\x85\x02\n#PollNexusOperationExecutionResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12\x42\n\nwait_stage\x18\x02 \x01(\x0e\x32..temporal.api.enums.v1.NexusOperationWaitStage\x12\x17\n\x0foperation_token\x18\x03 \x01(\t\x12\x31\n\x06result\x18\x04 \x01(\x0b\x32\x1f.temporal.api.common.v1.PayloadH\x00\x12\x33\n\x07\x66\x61ilure\x18\x05 \x01(\x0b\x32 .temporal.api.failure.v1.FailureH\x00\x42\t\n\x07outcome"s\n#ListNexusOperationExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x17\n\x0fnext_page_token\x18\x03 \x01(\x0c\x12\r\n\x05query\x18\x04 \x01(\t"\x8b\x01\n$ListNexusOperationExecutionsResponse\x12J\n\noperations\x18\x01 \x03(\x0b\x32\x36.temporal.api.nexus.v1.NexusOperationExecutionListInfo\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\x0c"B\n\x1e\x43ountActivityExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\r\n\x05query\x18\x02 \x01(\t"\xed\x01\n\x1f\x43ountActivityExecutionsResponse\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12\x61\n\x06groups\x18\x02 \x03(\x0b\x32Q.temporal.api.workflowservice.v1.CountActivityExecutionsResponse.AggregationGroup\x1aX\n\x10\x41ggregationGroup\x12\x35\n\x0cgroup_values\x18\x01 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\r\n\x05\x63ount\x18\x02 \x01(\x03"H\n$CountNexusOperationExecutionsRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\r\n\x05query\x18\x02 \x01(\t"\xf9\x01\n%CountNexusOperationExecutionsResponse\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12g\n\x06groups\x18\x02 \x03(\x0b\x32W.temporal.api.workflowservice.v1.CountNexusOperationExecutionsResponse.AggregationGroup\x1aX\n\x10\x41ggregationGroup\x12\x35\n\x0cgroup_values\x18\x01 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\r\n\x05\x63ount\x18\x02 \x01(\x03"\x95\x01\n%RequestCancelActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t\x12\x0e\n\x06reason\x18\x06 \x01(\t"(\n&RequestCancelActivityExecutionResponse"\x91\x01\n!TerminateActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t\x12\x0e\n\x06reason\x18\x06 \x01(\t"$\n"TerminateActivityExecutionResponse"X\n\x1e\x44\x65leteActivityExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t"!\n\x1f\x44\x65leteActivityExecutionResponse"\x9c\x01\n+RequestCancelNexusOperationExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0coperation_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t\x12\x0e\n\x06reason\x18\x06 \x01(\t".\n,RequestCancelNexusOperationExecutionResponse"\x98\x01\n\'TerminateNexusOperationExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0coperation_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\x12\x10\n\x08identity\x18\x04 \x01(\t\x12\x12\n\nrequest_id\x18\x05 \x01(\t\x12\x0e\n\x06reason\x18\x06 \x01(\t"*\n(TerminateNexusOperationExecutionResponse"_\n$DeleteNexusOperationExecutionRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x14\n\x0coperation_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t"\'\n%DeleteNexusOperationExecutionResponse"\x9d\x01\n(PollWorkflowExecutionTimeSkippingRequest\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x45\n\x12workflow_execution\x18\x02 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x17\n\x0f\x66\x61st_forward_id\x18\x03 \x01(\t"\xe8\x01\n)PollWorkflowExecutionTimeSkippingResponse\x12T\n\x1b\x66\x61st_forward_polling_result\x18\x01 \x01(\x0e\x32/.temporal.api.enums.v1.FastForwardPollingResult\x12\x15\n\rfailed_reason\x18\x02 \x01(\t\x12N\n\x11\x66\x61st_forward_info\x18\x03 \x01(\x0b\x32\x33.temporal.api.common.v1.TimeSkippingFastForwardInfoB\xbe\x01\n"io.temporal.api.workflowservice.v1B\x14RequestResponseProtoP\x01Z5go.temporal.io/api/workflowservice/v1;workflowservice\xaa\x02!Temporalio.Api.WorkflowService.V1\xea\x02$Temporalio::Api::WorkflowService::V1b\x06proto3' ) @@ -783,6 +786,8 @@ ] _DESCRIBEWORKERREQUEST = DESCRIPTOR.message_types_by_name["DescribeWorkerRequest"] _DESCRIBEWORKERRESPONSE = DESCRIPTOR.message_types_by_name["DescribeWorkerResponse"] +_COUNTWORKERSREQUEST = DESCRIPTOR.message_types_by_name["CountWorkersRequest"] +_COUNTWORKERSRESPONSE = DESCRIPTOR.message_types_by_name["CountWorkersResponse"] _PAUSEWORKFLOWEXECUTIONREQUEST = DESCRIPTOR.message_types_by_name[ "PauseWorkflowExecutionRequest" ] @@ -900,6 +905,12 @@ _DELETENEXUSOPERATIONEXECUTIONRESPONSE = DESCRIPTOR.message_types_by_name[ "DeleteNexusOperationExecutionResponse" ] +_POLLWORKFLOWEXECUTIONTIMESKIPPINGREQUEST = DESCRIPTOR.message_types_by_name[ + "PollWorkflowExecutionTimeSkippingRequest" +] +_POLLWORKFLOWEXECUTIONTIMESKIPPINGRESPONSE = DESCRIPTOR.message_types_by_name[ + "PollWorkflowExecutionTimeSkippingResponse" +] RegisterNamespaceRequest = _reflection.GeneratedProtocolMessageType( "RegisterNamespaceRequest", (_message.Message,), @@ -3494,6 +3505,28 @@ ) _sym_db.RegisterMessage(DescribeWorkerResponse) +CountWorkersRequest = _reflection.GeneratedProtocolMessageType( + "CountWorkersRequest", + (_message.Message,), + { + "DESCRIPTOR": _COUNTWORKERSREQUEST, + "__module__": "temporalio.api.workflowservice.v1.request_response_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.workflowservice.v1.CountWorkersRequest) + }, +) +_sym_db.RegisterMessage(CountWorkersRequest) + +CountWorkersResponse = _reflection.GeneratedProtocolMessageType( + "CountWorkersResponse", + (_message.Message,), + { + "DESCRIPTOR": _COUNTWORKERSRESPONSE, + "__module__": "temporalio.api.workflowservice.v1.request_response_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.workflowservice.v1.CountWorkersResponse) + }, +) +_sym_db.RegisterMessage(CountWorkersResponse) + PauseWorkflowExecutionRequest = _reflection.GeneratedProtocolMessageType( "PauseWorkflowExecutionRequest", (_message.Message,), @@ -3920,11 +3953,37 @@ ) _sym_db.RegisterMessage(DeleteNexusOperationExecutionResponse) +PollWorkflowExecutionTimeSkippingRequest = _reflection.GeneratedProtocolMessageType( + "PollWorkflowExecutionTimeSkippingRequest", + (_message.Message,), + { + "DESCRIPTOR": _POLLWORKFLOWEXECUTIONTIMESKIPPINGREQUEST, + "__module__": "temporalio.api.workflowservice.v1.request_response_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingRequest) + }, +) +_sym_db.RegisterMessage(PollWorkflowExecutionTimeSkippingRequest) + +PollWorkflowExecutionTimeSkippingResponse = _reflection.GeneratedProtocolMessageType( + "PollWorkflowExecutionTimeSkippingResponse", + (_message.Message,), + { + "DESCRIPTOR": _POLLWORKFLOWEXECUTIONTIMESKIPPINGRESPONSE, + "__module__": "temporalio.api.workflowservice.v1.request_response_pb2", + # @@protoc_insertion_point(class_scope:temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingResponse) + }, +) +_sym_db.RegisterMessage(PollWorkflowExecutionTimeSkippingResponse) + if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n"io.temporal.api.workflowservice.v1B\024RequestResponseProtoP\001Z5go.temporal.io/api/workflowservice/v1;workflowservice\252\002!Temporalio.Api.WorkflowService.V1\352\002$Temporalio::Api::WorkflowService::V1' _REGISTERNAMESPACEREQUEST_DATAENTRY._options = None _REGISTERNAMESPACEREQUEST_DATAENTRY._serialized_options = b"8\001" + _DESCRIBENAMESPACERESPONSE.fields_by_name["poller_group_infos"]._options = None + _DESCRIBENAMESPACERESPONSE.fields_by_name[ + "poller_group_infos" + ]._serialized_options = b"\030\001" _POLLWORKFLOWTASKQUEUEREQUEST.fields_by_name["binary_checksum"]._options = None _POLLWORKFLOWTASKQUEUEREQUEST.fields_by_name[ "binary_checksum" @@ -3937,6 +3996,10 @@ ]._serialized_options = b"\030\001" _POLLWORKFLOWTASKQUEUERESPONSE_QUERIESENTRY._options = None _POLLWORKFLOWTASKQUEUERESPONSE_QUERIESENTRY._serialized_options = b"8\001" + _POLLWORKFLOWTASKQUEUERESPONSE.fields_by_name["poller_group_infos"]._options = None + _POLLWORKFLOWTASKQUEUERESPONSE.fields_by_name[ + "poller_group_infos" + ]._serialized_options = b"\030\001" _RESPONDWORKFLOWTASKCOMPLETEDREQUEST_QUERYRESULTSENTRY._options = None _RESPONDWORKFLOWTASKCOMPLETEDREQUEST_QUERYRESULTSENTRY._serialized_options = ( b"8\001" @@ -3975,6 +4038,10 @@ _POLLACTIVITYTASKQUEUEREQUEST.fields_by_name[ "worker_version_capabilities" ]._serialized_options = b"\030\001" + _POLLACTIVITYTASKQUEUERESPONSE.fields_by_name["poller_group_infos"]._options = None + _POLLACTIVITYTASKQUEUERESPONSE.fields_by_name[ + "poller_group_infos" + ]._serialized_options = b"\030\001" _RESPONDACTIVITYTASKCOMPLETEDREQUEST.fields_by_name[ "worker_version" ]._options = None @@ -4055,12 +4122,20 @@ ]._serialized_options = b"\030\001" _GETCLUSTERINFORESPONSE_SUPPORTEDCLIENTSENTRY._options = None _GETCLUSTERINFORESPONSE_SUPPORTEDCLIENTSENTRY._serialized_options = b"8\001" + _STARTBATCHOPERATIONREQUEST.fields_by_name["executions"]._options = None + _STARTBATCHOPERATIONREQUEST.fields_by_name[ + "executions" + ]._serialized_options = b"\030\001" _POLLNEXUSTASKQUEUEREQUEST.fields_by_name[ "worker_version_capabilities" ]._options = None _POLLNEXUSTASKQUEUEREQUEST.fields_by_name[ "worker_version_capabilities" ]._serialized_options = b"\030\001" + _POLLNEXUSTASKQUEUERESPONSE.fields_by_name["poller_group_infos"]._options = None + _POLLNEXUSTASKQUEUERESPONSE.fields_by_name[ + "poller_group_infos" + ]._serialized_options = b"\030\001" _RESPONDNEXUSTASKFAILEDREQUEST.fields_by_name["error"]._options = None _RESPONDNEXUSTASKFAILEDREQUEST.fields_by_name[ "error" @@ -4139,570 +4214,578 @@ _UPDATETASKQUEUECONFIGREQUEST_SETFAIRNESSWEIGHTOVERRIDESENTRY._serialized_options = b"8\001" _STARTNEXUSOPERATIONEXECUTIONREQUEST_NEXUSHEADERENTRY._options = None _STARTNEXUSOPERATIONEXECUTIONREQUEST_NEXUSHEADERENTRY._serialized_options = b"8\001" - _REGISTERNAMESPACEREQUEST._serialized_start = 1603 - _REGISTERNAMESPACEREQUEST._serialized_end = 2251 - _REGISTERNAMESPACEREQUEST_DATAENTRY._serialized_start = 2208 - _REGISTERNAMESPACEREQUEST_DATAENTRY._serialized_end = 2251 - _REGISTERNAMESPACERESPONSE._serialized_start = 2253 - _REGISTERNAMESPACERESPONSE._serialized_end = 2280 - _LISTNAMESPACESREQUEST._serialized_start = 2283 - _LISTNAMESPACESREQUEST._serialized_end = 2420 - _LISTNAMESPACESRESPONSE._serialized_start = 2423 - _LISTNAMESPACESRESPONSE._serialized_end = 2552 - _DESCRIBENAMESPACEREQUEST._serialized_start = 2554 - _DESCRIBENAMESPACEREQUEST._serialized_end = 2637 - _DESCRIBENAMESPACERESPONSE._serialized_start = 2640 - _DESCRIBENAMESPACERESPONSE._serialized_end = 3076 - _UPDATENAMESPACEREQUEST._serialized_start = 3079 - _UPDATENAMESPACEREQUEST._serialized_end = 3414 - _UPDATENAMESPACERESPONSE._serialized_start = 3417 - _UPDATENAMESPACERESPONSE._serialized_end = 3708 - _DEPRECATENAMESPACEREQUEST._serialized_start = 3710 - _DEPRECATENAMESPACEREQUEST._serialized_end = 3780 - _DEPRECATENAMESPACERESPONSE._serialized_start = 3782 - _DEPRECATENAMESPACERESPONSE._serialized_end = 3810 - _STARTWORKFLOWEXECUTIONREQUEST._serialized_start = 3813 - _STARTWORKFLOWEXECUTIONREQUEST._serialized_end = 5432 - _STARTWORKFLOWEXECUTIONRESPONSE._serialized_start = 5435 - _STARTWORKFLOWEXECUTIONRESPONSE._serialized_end = 5701 - _GETWORKFLOWEXECUTIONHISTORYREQUEST._serialized_start = 5704 - _GETWORKFLOWEXECUTIONHISTORYREQUEST._serialized_end = 6002 - _GETWORKFLOWEXECUTIONHISTORYRESPONSE._serialized_start = 6005 - _GETWORKFLOWEXECUTIONHISTORYRESPONSE._serialized_end = 6191 - _GETWORKFLOWEXECUTIONHISTORYREVERSEREQUEST._serialized_start = 6194 - _GETWORKFLOWEXECUTIONHISTORYREVERSEREQUEST._serialized_end = 6370 - _GETWORKFLOWEXECUTIONHISTORYREVERSERESPONSE._serialized_start = 6372 - _GETWORKFLOWEXECUTIONHISTORYREVERSERESPONSE._serialized_end = 6492 - _POLLWORKFLOWTASKQUEUEREQUEST._serialized_start = 6495 - _POLLWORKFLOWTASKQUEUEREQUEST._serialized_end = 6935 - _POLLWORKFLOWTASKQUEUERESPONSE._serialized_start = 6938 - _POLLWORKFLOWTASKQUEUERESPONSE._serialized_end = 7948 - _POLLWORKFLOWTASKQUEUERESPONSE_QUERIESENTRY._serialized_start = 7864 - _POLLWORKFLOWTASKQUEUERESPONSE_QUERIESENTRY._serialized_end = 7948 - _RESPONDWORKFLOWTASKCOMPLETEDREQUEST._serialized_start = 7951 - _RESPONDWORKFLOWTASKCOMPLETEDREQUEST._serialized_end = 9241 - _RESPONDWORKFLOWTASKCOMPLETEDREQUEST_QUERYRESULTSENTRY._serialized_start = 9075 - _RESPONDWORKFLOWTASKCOMPLETEDREQUEST_QUERYRESULTSENTRY._serialized_end = 9170 - _RESPONDWORKFLOWTASKCOMPLETEDREQUEST_CAPABILITIES._serialized_start = 9172 - _RESPONDWORKFLOWTASKCOMPLETEDREQUEST_CAPABILITIES._serialized_end = 9241 - _RESPONDWORKFLOWTASKCOMPLETEDRESPONSE._serialized_start = 9244 - _RESPONDWORKFLOWTASKCOMPLETEDRESPONSE._serialized_end = 9489 - _RESPONDWORKFLOWTASKFAILEDREQUEST._serialized_start = 9492 - _RESPONDWORKFLOWTASKFAILEDREQUEST._serialized_end = 10017 - _RESPONDWORKFLOWTASKFAILEDRESPONSE._serialized_start = 10019 - _RESPONDWORKFLOWTASKFAILEDRESPONSE._serialized_end = 10054 - _POLLACTIVITYTASKQUEUEREQUEST._serialized_start = 10057 - _POLLACTIVITYTASKQUEUEREQUEST._serialized_end = 10543 - _POLLACTIVITYTASKQUEUERESPONSE._serialized_start = 10546 - _POLLACTIVITYTASKQUEUERESPONSE._serialized_end = 11650 - _RECORDACTIVITYTASKHEARTBEATREQUEST._serialized_start = 11653 - _RECORDACTIVITYTASKHEARTBEATREQUEST._serialized_end = 11818 - _RECORDACTIVITYTASKHEARTBEATRESPONSE._serialized_start = 11820 - _RECORDACTIVITYTASKHEARTBEATRESPONSE._serialized_end = 11932 - _RECORDACTIVITYTASKHEARTBEATBYIDREQUEST._serialized_start = 11935 - _RECORDACTIVITYTASKHEARTBEATBYIDREQUEST._serialized_end = 12142 - _RECORDACTIVITYTASKHEARTBEATBYIDRESPONSE._serialized_start = 12144 - _RECORDACTIVITYTASKHEARTBEATBYIDRESPONSE._serialized_end = 12260 - _RESPONDACTIVITYTASKCOMPLETEDREQUEST._serialized_start = 12263 - _RESPONDACTIVITYTASKCOMPLETEDREQUEST._serialized_end = 12645 - _RESPONDACTIVITYTASKCOMPLETEDRESPONSE._serialized_start = 12647 - _RESPONDACTIVITYTASKCOMPLETEDRESPONSE._serialized_end = 12685 - _RESPONDACTIVITYTASKCOMPLETEDBYIDREQUEST._serialized_start = 12688 - _RESPONDACTIVITYTASKCOMPLETEDBYIDREQUEST._serialized_end = 12895 - _RESPONDACTIVITYTASKCOMPLETEDBYIDRESPONSE._serialized_start = 12897 - _RESPONDACTIVITYTASKCOMPLETEDBYIDRESPONSE._serialized_end = 12939 - _RESPONDACTIVITYTASKFAILEDREQUEST._serialized_start = 12942 - _RESPONDACTIVITYTASKFAILEDREQUEST._serialized_end = 13388 - _RESPONDACTIVITYTASKFAILEDRESPONSE._serialized_start = 13390 - _RESPONDACTIVITYTASKFAILEDRESPONSE._serialized_end = 13477 - _RESPONDACTIVITYTASKFAILEDBYIDREQUEST._serialized_start = 13480 - _RESPONDACTIVITYTASKFAILEDBYIDREQUEST._serialized_end = 13751 - _RESPONDACTIVITYTASKFAILEDBYIDRESPONSE._serialized_start = 13753 - _RESPONDACTIVITYTASKFAILEDBYIDRESPONSE._serialized_end = 13844 - _RESPONDACTIVITYTASKCANCELEDREQUEST._serialized_start = 13847 - _RESPONDACTIVITYTASKCANCELEDREQUEST._serialized_end = 14229 - _RESPONDACTIVITYTASKCANCELEDRESPONSE._serialized_start = 14231 - _RESPONDACTIVITYTASKCANCELEDRESPONSE._serialized_end = 14268 - _RESPONDACTIVITYTASKCANCELEDBYIDREQUEST._serialized_start = 14271 - _RESPONDACTIVITYTASKCANCELEDBYIDREQUEST._serialized_end = 14559 - _RESPONDACTIVITYTASKCANCELEDBYIDRESPONSE._serialized_start = 14561 - _RESPONDACTIVITYTASKCANCELEDBYIDRESPONSE._serialized_end = 14602 - _REQUESTCANCELWORKFLOWEXECUTIONREQUEST._serialized_start = 14605 - _REQUESTCANCELWORKFLOWEXECUTIONREQUEST._serialized_end = 14865 - _REQUESTCANCELWORKFLOWEXECUTIONRESPONSE._serialized_start = 14867 - _REQUESTCANCELWORKFLOWEXECUTIONRESPONSE._serialized_end = 14907 - _SIGNALWORKFLOWEXECUTIONREQUEST._serialized_start = 14910 - _SIGNALWORKFLOWEXECUTIONREQUEST._serialized_end = 15260 - _SIGNALWORKFLOWEXECUTIONRESPONSE._serialized_start = 15262 - _SIGNALWORKFLOWEXECUTIONRESPONSE._serialized_end = 15339 - _SIGNALWITHSTARTWORKFLOWEXECUTIONREQUEST._serialized_start = 15342 - _SIGNALWITHSTARTWORKFLOWEXECUTIONREQUEST._serialized_end = 16683 - _SIGNALWITHSTARTWORKFLOWEXECUTIONRESPONSE._serialized_start = 16685 - _SIGNALWITHSTARTWORKFLOWEXECUTIONRESPONSE._serialized_end = 16811 - _RESETWORKFLOWEXECUTIONREQUEST._serialized_start = 16814 - _RESETWORKFLOWEXECUTIONREQUEST._serialized_end = 17263 - _RESETWORKFLOWEXECUTIONRESPONSE._serialized_start = 17265 - _RESETWORKFLOWEXECUTIONRESPONSE._serialized_end = 17313 - _TERMINATEWORKFLOWEXECUTIONREQUEST._serialized_start = 17316 - _TERMINATEWORKFLOWEXECUTIONREQUEST._serialized_end = 17603 - _TERMINATEWORKFLOWEXECUTIONRESPONSE._serialized_start = 17605 - _TERMINATEWORKFLOWEXECUTIONRESPONSE._serialized_end = 17641 - _DELETEWORKFLOWEXECUTIONREQUEST._serialized_start = 17643 - _DELETEWORKFLOWEXECUTIONREQUEST._serialized_end = 17765 - _DELETEWORKFLOWEXECUTIONRESPONSE._serialized_start = 17767 - _DELETEWORKFLOWEXECUTIONRESPONSE._serialized_end = 17800 - _LISTOPENWORKFLOWEXECUTIONSREQUEST._serialized_start = 17803 - _LISTOPENWORKFLOWEXECUTIONSREQUEST._serialized_end = 18132 - _LISTOPENWORKFLOWEXECUTIONSRESPONSE._serialized_start = 18135 - _LISTOPENWORKFLOWEXECUTIONSRESPONSE._serialized_end = 18265 - _LISTCLOSEDWORKFLOWEXECUTIONSREQUEST._serialized_start = 18268 - _LISTCLOSEDWORKFLOWEXECUTIONSREQUEST._serialized_end = 18662 - _LISTCLOSEDWORKFLOWEXECUTIONSRESPONSE._serialized_start = 18665 - _LISTCLOSEDWORKFLOWEXECUTIONSRESPONSE._serialized_end = 18797 - _LISTWORKFLOWEXECUTIONSREQUEST._serialized_start = 18799 - _LISTWORKFLOWEXECUTIONSREQUEST._serialized_end = 18908 - _LISTWORKFLOWEXECUTIONSRESPONSE._serialized_start = 18910 - _LISTWORKFLOWEXECUTIONSRESPONSE._serialized_end = 19036 - _LISTARCHIVEDWORKFLOWEXECUTIONSREQUEST._serialized_start = 19038 - _LISTARCHIVEDWORKFLOWEXECUTIONSREQUEST._serialized_end = 19155 - _LISTARCHIVEDWORKFLOWEXECUTIONSRESPONSE._serialized_start = 19158 - _LISTARCHIVEDWORKFLOWEXECUTIONSRESPONSE._serialized_end = 19292 - _SCANWORKFLOWEXECUTIONSREQUEST._serialized_start = 19294 - _SCANWORKFLOWEXECUTIONSREQUEST._serialized_end = 19403 - _SCANWORKFLOWEXECUTIONSRESPONSE._serialized_start = 19405 - _SCANWORKFLOWEXECUTIONSRESPONSE._serialized_end = 19531 - _COUNTWORKFLOWEXECUTIONSREQUEST._serialized_start = 19533 - _COUNTWORKFLOWEXECUTIONSREQUEST._serialized_end = 19599 - _COUNTWORKFLOWEXECUTIONSRESPONSE._serialized_start = 19602 - _COUNTWORKFLOWEXECUTIONSRESPONSE._serialized_end = 19839 - _COUNTWORKFLOWEXECUTIONSRESPONSE_AGGREGATIONGROUP._serialized_start = 19751 - _COUNTWORKFLOWEXECUTIONSRESPONSE_AGGREGATIONGROUP._serialized_end = 19839 - _GETSEARCHATTRIBUTESREQUEST._serialized_start = 19841 - _GETSEARCHATTRIBUTESREQUEST._serialized_end = 19869 - _GETSEARCHATTRIBUTESRESPONSE._serialized_start = 19872 - _GETSEARCHATTRIBUTESRESPONSE._serialized_end = 20073 - _GETSEARCHATTRIBUTESRESPONSE_KEYSENTRY._serialized_start = 19989 - _GETSEARCHATTRIBUTESRESPONSE_KEYSENTRY._serialized_end = 20073 - _RESPONDQUERYTASKCOMPLETEDREQUEST._serialized_start = 20076 - _RESPONDQUERYTASKCOMPLETEDREQUEST._serialized_end = 20437 - _RESPONDQUERYTASKCOMPLETEDRESPONSE._serialized_start = 20439 - _RESPONDQUERYTASKCOMPLETEDRESPONSE._serialized_end = 20474 - _RESETSTICKYTASKQUEUEREQUEST._serialized_start = 20476 - _RESETSTICKYTASKQUEUEREQUEST._serialized_end = 20586 - _RESETSTICKYTASKQUEUERESPONSE._serialized_start = 20588 - _RESETSTICKYTASKQUEUERESPONSE._serialized_end = 20618 - _SHUTDOWNWORKERREQUEST._serialized_start = 20621 - _SHUTDOWNWORKERREQUEST._serialized_end = 20904 - _SHUTDOWNWORKERRESPONSE._serialized_start = 20906 - _SHUTDOWNWORKERRESPONSE._serialized_end = 20930 - _QUERYWORKFLOWREQUEST._serialized_start = 20933 - _QUERYWORKFLOWREQUEST._serialized_end = 21166 - _QUERYWORKFLOWRESPONSE._serialized_start = 21169 - _QUERYWORKFLOWRESPONSE._serialized_end = 21310 - _DESCRIBEWORKFLOWEXECUTIONREQUEST._serialized_start = 21312 - _DESCRIBEWORKFLOWEXECUTIONREQUEST._serialized_end = 21427 - _DESCRIBEWORKFLOWEXECUTIONRESPONSE._serialized_start = 21430 - _DESCRIBEWORKFLOWEXECUTIONRESPONSE._serialized_end = 22095 - _DESCRIBETASKQUEUEREQUEST._serialized_start = 22098 - _DESCRIBETASKQUEUEREQUEST._serialized_end = 22626 - _DESCRIBETASKQUEUERESPONSE._serialized_start = 22629 - _DESCRIBETASKQUEUERESPONSE._serialized_end = 23633 - _DESCRIBETASKQUEUERESPONSE_STATSBYPRIORITYKEYENTRY._serialized_start = 23313 - _DESCRIBETASKQUEUERESPONSE_STATSBYPRIORITYKEYENTRY._serialized_end = 23413 - _DESCRIBETASKQUEUERESPONSE_EFFECTIVERATELIMIT._serialized_start = 23415 - _DESCRIBETASKQUEUERESPONSE_EFFECTIVERATELIMIT._serialized_end = 23531 - _DESCRIBETASKQUEUERESPONSE_VERSIONSINFOENTRY._serialized_start = 23533 - _DESCRIBETASKQUEUERESPONSE_VERSIONSINFOENTRY._serialized_end = 23633 - _GETCLUSTERINFOREQUEST._serialized_start = 23635 - _GETCLUSTERINFOREQUEST._serialized_end = 23658 - _GETCLUSTERINFORESPONSE._serialized_start = 23661 - _GETCLUSTERINFORESPONSE._serialized_end = 24126 - _GETCLUSTERINFORESPONSE_SUPPORTEDCLIENTSENTRY._serialized_start = 24071 - _GETCLUSTERINFORESPONSE_SUPPORTEDCLIENTSENTRY._serialized_end = 24126 - _GETSYSTEMINFOREQUEST._serialized_start = 24128 - _GETSYSTEMINFOREQUEST._serialized_end = 24150 - _GETSYSTEMINFORESPONSE._serialized_start = 24153 - _GETSYSTEMINFORESPONSE._serialized_end = 24688 - _GETSYSTEMINFORESPONSE_CAPABILITIES._serialized_start = 24294 - _GETSYSTEMINFORESPONSE_CAPABILITIES._serialized_end = 24688 - _LISTTASKQUEUEPARTITIONSREQUEST._serialized_start = 24690 - _LISTTASKQUEUEPARTITIONSREQUEST._serialized_end = 24799 - _LISTTASKQUEUEPARTITIONSRESPONSE._serialized_start = 24802 - _LISTTASKQUEUEPARTITIONSRESPONSE._serialized_end = 25025 - _CREATESCHEDULEREQUEST._serialized_start = 25028 - _CREATESCHEDULEREQUEST._serialized_end = 25360 - _CREATESCHEDULERESPONSE._serialized_start = 25362 - _CREATESCHEDULERESPONSE._serialized_end = 25410 - _DESCRIBESCHEDULEREQUEST._serialized_start = 25412 - _DESCRIBESCHEDULEREQUEST._serialized_end = 25477 - _DESCRIBESCHEDULERESPONSE._serialized_start = 25480 - _DESCRIBESCHEDULERESPONSE._serialized_end = 25751 - _UPDATESCHEDULEREQUEST._serialized_start = 25754 - _UPDATESCHEDULEREQUEST._serialized_end = 26046 - _UPDATESCHEDULERESPONSE._serialized_start = 26048 - _UPDATESCHEDULERESPONSE._serialized_end = 26072 - _PATCHSCHEDULEREQUEST._serialized_start = 26075 - _PATCHSCHEDULEREQUEST._serialized_end = 26231 - _PATCHSCHEDULERESPONSE._serialized_start = 26233 - _PATCHSCHEDULERESPONSE._serialized_end = 26256 - _LISTSCHEDULEMATCHINGTIMESREQUEST._serialized_start = 26259 - _LISTSCHEDULEMATCHINGTIMESREQUEST._serialized_end = 26427 - _LISTSCHEDULEMATCHINGTIMESRESPONSE._serialized_start = 26429 - _LISTSCHEDULEMATCHINGTIMESRESPONSE._serialized_end = 26512 - _DELETESCHEDULEREQUEST._serialized_start = 26514 - _DELETESCHEDULEREQUEST._serialized_end = 26595 - _DELETESCHEDULERESPONSE._serialized_start = 26597 - _DELETESCHEDULERESPONSE._serialized_end = 26621 - _LISTSCHEDULESREQUEST._serialized_start = 26623 - _LISTSCHEDULESREQUEST._serialized_end = 26731 - _LISTSCHEDULESRESPONSE._serialized_start = 26733 - _LISTSCHEDULESRESPONSE._serialized_end = 26845 - _COUNTSCHEDULESREQUEST._serialized_start = 26847 - _COUNTSCHEDULESREQUEST._serialized_end = 26904 - _COUNTSCHEDULESRESPONSE._serialized_start = 26907 - _COUNTSCHEDULESRESPONSE._serialized_end = 27126 - _COUNTSCHEDULESRESPONSE_AGGREGATIONGROUP._serialized_start = 19751 - _COUNTSCHEDULESRESPONSE_AGGREGATIONGROUP._serialized_end = 19839 - _UPDATEWORKERBUILDIDCOMPATIBILITYREQUEST._serialized_start = 27129 - _UPDATEWORKERBUILDIDCOMPATIBILITYREQUEST._serialized_end = 27775 - _UPDATEWORKERBUILDIDCOMPATIBILITYREQUEST_ADDNEWCOMPATIBLEVERSION._serialized_start = 27576 + _REGISTERNAMESPACEREQUEST._serialized_start = 1646 + _REGISTERNAMESPACEREQUEST._serialized_end = 2294 + _REGISTERNAMESPACEREQUEST_DATAENTRY._serialized_start = 2251 + _REGISTERNAMESPACEREQUEST_DATAENTRY._serialized_end = 2294 + _REGISTERNAMESPACERESPONSE._serialized_start = 2296 + _REGISTERNAMESPACERESPONSE._serialized_end = 2323 + _LISTNAMESPACESREQUEST._serialized_start = 2326 + _LISTNAMESPACESREQUEST._serialized_end = 2463 + _LISTNAMESPACESRESPONSE._serialized_start = 2466 + _LISTNAMESPACESRESPONSE._serialized_end = 2595 + _DESCRIBENAMESPACEREQUEST._serialized_start = 2597 + _DESCRIBENAMESPACEREQUEST._serialized_end = 2680 + _DESCRIBENAMESPACERESPONSE._serialized_start = 2683 + _DESCRIBENAMESPACERESPONSE._serialized_end = 3196 + _UPDATENAMESPACEREQUEST._serialized_start = 3199 + _UPDATENAMESPACEREQUEST._serialized_end = 3534 + _UPDATENAMESPACERESPONSE._serialized_start = 3537 + _UPDATENAMESPACERESPONSE._serialized_end = 3828 + _DEPRECATENAMESPACEREQUEST._serialized_start = 3830 + _DEPRECATENAMESPACEREQUEST._serialized_end = 3900 + _DEPRECATENAMESPACERESPONSE._serialized_start = 3902 + _DEPRECATENAMESPACERESPONSE._serialized_end = 3930 + _STARTWORKFLOWEXECUTIONREQUEST._serialized_start = 3933 + _STARTWORKFLOWEXECUTIONREQUEST._serialized_end = 5550 + _STARTWORKFLOWEXECUTIONRESPONSE._serialized_start = 5553 + _STARTWORKFLOWEXECUTIONRESPONSE._serialized_end = 5851 + _GETWORKFLOWEXECUTIONHISTORYREQUEST._serialized_start = 5854 + _GETWORKFLOWEXECUTIONHISTORYREQUEST._serialized_end = 6152 + _GETWORKFLOWEXECUTIONHISTORYRESPONSE._serialized_start = 6155 + _GETWORKFLOWEXECUTIONHISTORYRESPONSE._serialized_end = 6341 + _GETWORKFLOWEXECUTIONHISTORYREVERSEREQUEST._serialized_start = 6344 + _GETWORKFLOWEXECUTIONHISTORYREVERSEREQUEST._serialized_end = 6520 + _GETWORKFLOWEXECUTIONHISTORYREVERSERESPONSE._serialized_start = 6522 + _GETWORKFLOWEXECUTIONHISTORYREVERSERESPONSE._serialized_end = 6642 + _POLLWORKFLOWTASKQUEUEREQUEST._serialized_start = 6645 + _POLLWORKFLOWTASKQUEUEREQUEST._serialized_end = 7085 + _POLLWORKFLOWTASKQUEUERESPONSE._serialized_start = 7088 + _POLLWORKFLOWTASKQUEUERESPONSE._serialized_end = 8175 + _POLLWORKFLOWTASKQUEUERESPONSE_QUERIESENTRY._serialized_start = 8091 + _POLLWORKFLOWTASKQUEUERESPONSE_QUERIESENTRY._serialized_end = 8175 + _RESPONDWORKFLOWTASKCOMPLETEDREQUEST._serialized_start = 8178 + _RESPONDWORKFLOWTASKCOMPLETEDREQUEST._serialized_end = 9516 + _RESPONDWORKFLOWTASKCOMPLETEDREQUEST_QUERYRESULTSENTRY._serialized_start = 9350 + _RESPONDWORKFLOWTASKCOMPLETEDREQUEST_QUERYRESULTSENTRY._serialized_end = 9445 + _RESPONDWORKFLOWTASKCOMPLETEDREQUEST_CAPABILITIES._serialized_start = 9447 + _RESPONDWORKFLOWTASKCOMPLETEDREQUEST_CAPABILITIES._serialized_end = 9516 + _RESPONDWORKFLOWTASKCOMPLETEDRESPONSE._serialized_start = 9519 + _RESPONDWORKFLOWTASKCOMPLETEDRESPONSE._serialized_end = 9764 + _RESPONDWORKFLOWTASKFAILEDREQUEST._serialized_start = 9767 + _RESPONDWORKFLOWTASKFAILEDREQUEST._serialized_end = 10292 + _RESPONDWORKFLOWTASKFAILEDRESPONSE._serialized_start = 10294 + _RESPONDWORKFLOWTASKFAILEDRESPONSE._serialized_end = 10329 + _POLLACTIVITYTASKQUEUEREQUEST._serialized_start = 10332 + _POLLACTIVITYTASKQUEUEREQUEST._serialized_end = 10818 + _POLLACTIVITYTASKQUEUERESPONSE._serialized_start = 10821 + _POLLACTIVITYTASKQUEUERESPONSE._serialized_end = 12002 + _RECORDACTIVITYTASKHEARTBEATREQUEST._serialized_start = 12005 + _RECORDACTIVITYTASKHEARTBEATREQUEST._serialized_end = 12170 + _RECORDACTIVITYTASKHEARTBEATRESPONSE._serialized_start = 12172 + _RECORDACTIVITYTASKHEARTBEATRESPONSE._serialized_end = 12284 + _RECORDACTIVITYTASKHEARTBEATBYIDREQUEST._serialized_start = 12287 + _RECORDACTIVITYTASKHEARTBEATBYIDREQUEST._serialized_end = 12494 + _RECORDACTIVITYTASKHEARTBEATBYIDRESPONSE._serialized_start = 12496 + _RECORDACTIVITYTASKHEARTBEATBYIDRESPONSE._serialized_end = 12612 + _RESPONDACTIVITYTASKCOMPLETEDREQUEST._serialized_start = 12615 + _RESPONDACTIVITYTASKCOMPLETEDREQUEST._serialized_end = 12997 + _RESPONDACTIVITYTASKCOMPLETEDRESPONSE._serialized_start = 12999 + _RESPONDACTIVITYTASKCOMPLETEDRESPONSE._serialized_end = 13037 + _RESPONDACTIVITYTASKCOMPLETEDBYIDREQUEST._serialized_start = 13040 + _RESPONDACTIVITYTASKCOMPLETEDBYIDREQUEST._serialized_end = 13247 + _RESPONDACTIVITYTASKCOMPLETEDBYIDRESPONSE._serialized_start = 13249 + _RESPONDACTIVITYTASKCOMPLETEDBYIDRESPONSE._serialized_end = 13291 + _RESPONDACTIVITYTASKFAILEDREQUEST._serialized_start = 13294 + _RESPONDACTIVITYTASKFAILEDREQUEST._serialized_end = 13740 + _RESPONDACTIVITYTASKFAILEDRESPONSE._serialized_start = 13742 + _RESPONDACTIVITYTASKFAILEDRESPONSE._serialized_end = 13829 + _RESPONDACTIVITYTASKFAILEDBYIDREQUEST._serialized_start = 13832 + _RESPONDACTIVITYTASKFAILEDBYIDREQUEST._serialized_end = 14103 + _RESPONDACTIVITYTASKFAILEDBYIDRESPONSE._serialized_start = 14105 + _RESPONDACTIVITYTASKFAILEDBYIDRESPONSE._serialized_end = 14196 + _RESPONDACTIVITYTASKCANCELEDREQUEST._serialized_start = 14199 + _RESPONDACTIVITYTASKCANCELEDREQUEST._serialized_end = 14581 + _RESPONDACTIVITYTASKCANCELEDRESPONSE._serialized_start = 14583 + _RESPONDACTIVITYTASKCANCELEDRESPONSE._serialized_end = 14620 + _RESPONDACTIVITYTASKCANCELEDBYIDREQUEST._serialized_start = 14623 + _RESPONDACTIVITYTASKCANCELEDBYIDREQUEST._serialized_end = 14911 + _RESPONDACTIVITYTASKCANCELEDBYIDRESPONSE._serialized_start = 14913 + _RESPONDACTIVITYTASKCANCELEDBYIDRESPONSE._serialized_end = 14954 + _REQUESTCANCELWORKFLOWEXECUTIONREQUEST._serialized_start = 14957 + _REQUESTCANCELWORKFLOWEXECUTIONREQUEST._serialized_end = 15217 + _REQUESTCANCELWORKFLOWEXECUTIONRESPONSE._serialized_start = 15219 + _REQUESTCANCELWORKFLOWEXECUTIONRESPONSE._serialized_end = 15259 + _SIGNALWORKFLOWEXECUTIONREQUEST._serialized_start = 15262 + _SIGNALWORKFLOWEXECUTIONREQUEST._serialized_end = 15612 + _SIGNALWORKFLOWEXECUTIONRESPONSE._serialized_start = 15614 + _SIGNALWORKFLOWEXECUTIONRESPONSE._serialized_end = 15691 + _SIGNALWITHSTARTWORKFLOWEXECUTIONREQUEST._serialized_start = 15694 + _SIGNALWITHSTARTWORKFLOWEXECUTIONREQUEST._serialized_end = 17033 + _SIGNALWITHSTARTWORKFLOWEXECUTIONRESPONSE._serialized_start = 17036 + _SIGNALWITHSTARTWORKFLOWEXECUTIONRESPONSE._serialized_end = 17194 + _RESETWORKFLOWEXECUTIONREQUEST._serialized_start = 17197 + _RESETWORKFLOWEXECUTIONREQUEST._serialized_end = 17646 + _RESETWORKFLOWEXECUTIONRESPONSE._serialized_start = 17648 + _RESETWORKFLOWEXECUTIONRESPONSE._serialized_end = 17696 + _TERMINATEWORKFLOWEXECUTIONREQUEST._serialized_start = 17699 + _TERMINATEWORKFLOWEXECUTIONREQUEST._serialized_end = 17986 + _TERMINATEWORKFLOWEXECUTIONRESPONSE._serialized_start = 17988 + _TERMINATEWORKFLOWEXECUTIONRESPONSE._serialized_end = 18024 + _DELETEWORKFLOWEXECUTIONREQUEST._serialized_start = 18026 + _DELETEWORKFLOWEXECUTIONREQUEST._serialized_end = 18148 + _DELETEWORKFLOWEXECUTIONRESPONSE._serialized_start = 18150 + _DELETEWORKFLOWEXECUTIONRESPONSE._serialized_end = 18183 + _LISTOPENWORKFLOWEXECUTIONSREQUEST._serialized_start = 18186 + _LISTOPENWORKFLOWEXECUTIONSREQUEST._serialized_end = 18515 + _LISTOPENWORKFLOWEXECUTIONSRESPONSE._serialized_start = 18518 + _LISTOPENWORKFLOWEXECUTIONSRESPONSE._serialized_end = 18648 + _LISTCLOSEDWORKFLOWEXECUTIONSREQUEST._serialized_start = 18651 + _LISTCLOSEDWORKFLOWEXECUTIONSREQUEST._serialized_end = 19045 + _LISTCLOSEDWORKFLOWEXECUTIONSRESPONSE._serialized_start = 19048 + _LISTCLOSEDWORKFLOWEXECUTIONSRESPONSE._serialized_end = 19180 + _LISTWORKFLOWEXECUTIONSREQUEST._serialized_start = 19182 + _LISTWORKFLOWEXECUTIONSREQUEST._serialized_end = 19291 + _LISTWORKFLOWEXECUTIONSRESPONSE._serialized_start = 19293 + _LISTWORKFLOWEXECUTIONSRESPONSE._serialized_end = 19419 + _LISTARCHIVEDWORKFLOWEXECUTIONSREQUEST._serialized_start = 19421 + _LISTARCHIVEDWORKFLOWEXECUTIONSREQUEST._serialized_end = 19538 + _LISTARCHIVEDWORKFLOWEXECUTIONSRESPONSE._serialized_start = 19541 + _LISTARCHIVEDWORKFLOWEXECUTIONSRESPONSE._serialized_end = 19675 + _SCANWORKFLOWEXECUTIONSREQUEST._serialized_start = 19677 + _SCANWORKFLOWEXECUTIONSREQUEST._serialized_end = 19786 + _SCANWORKFLOWEXECUTIONSRESPONSE._serialized_start = 19788 + _SCANWORKFLOWEXECUTIONSRESPONSE._serialized_end = 19914 + _COUNTWORKFLOWEXECUTIONSREQUEST._serialized_start = 19916 + _COUNTWORKFLOWEXECUTIONSREQUEST._serialized_end = 19982 + _COUNTWORKFLOWEXECUTIONSRESPONSE._serialized_start = 19985 + _COUNTWORKFLOWEXECUTIONSRESPONSE._serialized_end = 20222 + _COUNTWORKFLOWEXECUTIONSRESPONSE_AGGREGATIONGROUP._serialized_start = 20134 + _COUNTWORKFLOWEXECUTIONSRESPONSE_AGGREGATIONGROUP._serialized_end = 20222 + _GETSEARCHATTRIBUTESREQUEST._serialized_start = 20224 + _GETSEARCHATTRIBUTESREQUEST._serialized_end = 20252 + _GETSEARCHATTRIBUTESRESPONSE._serialized_start = 20255 + _GETSEARCHATTRIBUTESRESPONSE._serialized_end = 20456 + _GETSEARCHATTRIBUTESRESPONSE_KEYSENTRY._serialized_start = 20372 + _GETSEARCHATTRIBUTESRESPONSE_KEYSENTRY._serialized_end = 20456 + _RESPONDQUERYTASKCOMPLETEDREQUEST._serialized_start = 20459 + _RESPONDQUERYTASKCOMPLETEDREQUEST._serialized_end = 20820 + _RESPONDQUERYTASKCOMPLETEDRESPONSE._serialized_start = 20822 + _RESPONDQUERYTASKCOMPLETEDRESPONSE._serialized_end = 20857 + _RESETSTICKYTASKQUEUEREQUEST._serialized_start = 20859 + _RESETSTICKYTASKQUEUEREQUEST._serialized_end = 20969 + _RESETSTICKYTASKQUEUERESPONSE._serialized_start = 20971 + _RESETSTICKYTASKQUEUERESPONSE._serialized_end = 21001 + _SHUTDOWNWORKERREQUEST._serialized_start = 21004 + _SHUTDOWNWORKERREQUEST._serialized_end = 21287 + _SHUTDOWNWORKERRESPONSE._serialized_start = 21289 + _SHUTDOWNWORKERRESPONSE._serialized_end = 21313 + _QUERYWORKFLOWREQUEST._serialized_start = 21316 + _QUERYWORKFLOWREQUEST._serialized_end = 21549 + _QUERYWORKFLOWRESPONSE._serialized_start = 21552 + _QUERYWORKFLOWRESPONSE._serialized_end = 21737 + _DESCRIBEWORKFLOWEXECUTIONREQUEST._serialized_start = 21739 + _DESCRIBEWORKFLOWEXECUTIONREQUEST._serialized_end = 21854 + _DESCRIBEWORKFLOWEXECUTIONRESPONSE._serialized_start = 21857 + _DESCRIBEWORKFLOWEXECUTIONRESPONSE._serialized_end = 22522 + _DESCRIBETASKQUEUEREQUEST._serialized_start = 22525 + _DESCRIBETASKQUEUEREQUEST._serialized_end = 23053 + _DESCRIBETASKQUEUERESPONSE._serialized_start = 23056 + _DESCRIBETASKQUEUERESPONSE._serialized_end = 24060 + _DESCRIBETASKQUEUERESPONSE_STATSBYPRIORITYKEYENTRY._serialized_start = 23740 + _DESCRIBETASKQUEUERESPONSE_STATSBYPRIORITYKEYENTRY._serialized_end = 23840 + _DESCRIBETASKQUEUERESPONSE_EFFECTIVERATELIMIT._serialized_start = 23842 + _DESCRIBETASKQUEUERESPONSE_EFFECTIVERATELIMIT._serialized_end = 23958 + _DESCRIBETASKQUEUERESPONSE_VERSIONSINFOENTRY._serialized_start = 23960 + _DESCRIBETASKQUEUERESPONSE_VERSIONSINFOENTRY._serialized_end = 24060 + _GETCLUSTERINFOREQUEST._serialized_start = 24062 + _GETCLUSTERINFOREQUEST._serialized_end = 24085 + _GETCLUSTERINFORESPONSE._serialized_start = 24088 + _GETCLUSTERINFORESPONSE._serialized_end = 24553 + _GETCLUSTERINFORESPONSE_SUPPORTEDCLIENTSENTRY._serialized_start = 24498 + _GETCLUSTERINFORESPONSE_SUPPORTEDCLIENTSENTRY._serialized_end = 24553 + _GETSYSTEMINFOREQUEST._serialized_start = 24555 + _GETSYSTEMINFOREQUEST._serialized_end = 24577 + _GETSYSTEMINFORESPONSE._serialized_start = 24580 + _GETSYSTEMINFORESPONSE._serialized_end = 25157 + _GETSYSTEMINFORESPONSE_CAPABILITIES._serialized_start = 24721 + _GETSYSTEMINFORESPONSE_CAPABILITIES._serialized_end = 25157 + _LISTTASKQUEUEPARTITIONSREQUEST._serialized_start = 25159 + _LISTTASKQUEUEPARTITIONSREQUEST._serialized_end = 25268 + _LISTTASKQUEUEPARTITIONSRESPONSE._serialized_start = 25271 + _LISTTASKQUEUEPARTITIONSRESPONSE._serialized_end = 25494 + _CREATESCHEDULEREQUEST._serialized_start = 25497 + _CREATESCHEDULEREQUEST._serialized_end = 25829 + _CREATESCHEDULERESPONSE._serialized_start = 25831 + _CREATESCHEDULERESPONSE._serialized_end = 25879 + _DESCRIBESCHEDULEREQUEST._serialized_start = 25881 + _DESCRIBESCHEDULEREQUEST._serialized_end = 25946 + _DESCRIBESCHEDULERESPONSE._serialized_start = 25949 + _DESCRIBESCHEDULERESPONSE._serialized_end = 26220 + _UPDATESCHEDULEREQUEST._serialized_start = 26223 + _UPDATESCHEDULEREQUEST._serialized_end = 26515 + _UPDATESCHEDULERESPONSE._serialized_start = 26517 + _UPDATESCHEDULERESPONSE._serialized_end = 26541 + _PATCHSCHEDULEREQUEST._serialized_start = 26544 + _PATCHSCHEDULEREQUEST._serialized_end = 26700 + _PATCHSCHEDULERESPONSE._serialized_start = 26702 + _PATCHSCHEDULERESPONSE._serialized_end = 26725 + _LISTSCHEDULEMATCHINGTIMESREQUEST._serialized_start = 26728 + _LISTSCHEDULEMATCHINGTIMESREQUEST._serialized_end = 26896 + _LISTSCHEDULEMATCHINGTIMESRESPONSE._serialized_start = 26898 + _LISTSCHEDULEMATCHINGTIMESRESPONSE._serialized_end = 26981 + _DELETESCHEDULEREQUEST._serialized_start = 26983 + _DELETESCHEDULEREQUEST._serialized_end = 27064 + _DELETESCHEDULERESPONSE._serialized_start = 27066 + _DELETESCHEDULERESPONSE._serialized_end = 27090 + _LISTSCHEDULESREQUEST._serialized_start = 27092 + _LISTSCHEDULESREQUEST._serialized_end = 27200 + _LISTSCHEDULESRESPONSE._serialized_start = 27202 + _LISTSCHEDULESRESPONSE._serialized_end = 27314 + _COUNTSCHEDULESREQUEST._serialized_start = 27316 + _COUNTSCHEDULESREQUEST._serialized_end = 27373 + _COUNTSCHEDULESRESPONSE._serialized_start = 27376 + _COUNTSCHEDULESRESPONSE._serialized_end = 27595 + _COUNTSCHEDULESRESPONSE_AGGREGATIONGROUP._serialized_start = 20134 + _COUNTSCHEDULESRESPONSE_AGGREGATIONGROUP._serialized_end = 20222 + _UPDATEWORKERBUILDIDCOMPATIBILITYREQUEST._serialized_start = 27598 + _UPDATEWORKERBUILDIDCOMPATIBILITYREQUEST._serialized_end = 28244 + _UPDATEWORKERBUILDIDCOMPATIBILITYREQUEST_ADDNEWCOMPATIBLEVERSION._serialized_start = 28045 _UPDATEWORKERBUILDIDCOMPATIBILITYREQUEST_ADDNEWCOMPATIBLEVERSION._serialized_end = ( - 27687 + 28156 ) - _UPDATEWORKERBUILDIDCOMPATIBILITYREQUEST_MERGESETS._serialized_start = 27689 - _UPDATEWORKERBUILDIDCOMPATIBILITYREQUEST_MERGESETS._serialized_end = 27762 - _UPDATEWORKERBUILDIDCOMPATIBILITYRESPONSE._serialized_start = 27777 - _UPDATEWORKERBUILDIDCOMPATIBILITYRESPONSE._serialized_end = 27841 - _GETWORKERBUILDIDCOMPATIBILITYREQUEST._serialized_start = 27843 - _GETWORKERBUILDIDCOMPATIBILITYREQUEST._serialized_end = 27938 - _GETWORKERBUILDIDCOMPATIBILITYRESPONSE._serialized_start = 27940 - _GETWORKERBUILDIDCOMPATIBILITYRESPONSE._serialized_end = 28056 - _UPDATEWORKERVERSIONINGRULESREQUEST._serialized_start = 28059 - _UPDATEWORKERVERSIONINGRULESREQUEST._serialized_end = 29776 - _UPDATEWORKERVERSIONINGRULESREQUEST_INSERTBUILDIDASSIGNMENTRULE._serialized_start = 29111 + _UPDATEWORKERBUILDIDCOMPATIBILITYREQUEST_MERGESETS._serialized_start = 28158 + _UPDATEWORKERBUILDIDCOMPATIBILITYREQUEST_MERGESETS._serialized_end = 28231 + _UPDATEWORKERBUILDIDCOMPATIBILITYRESPONSE._serialized_start = 28246 + _UPDATEWORKERBUILDIDCOMPATIBILITYRESPONSE._serialized_end = 28310 + _GETWORKERBUILDIDCOMPATIBILITYREQUEST._serialized_start = 28312 + _GETWORKERBUILDIDCOMPATIBILITYREQUEST._serialized_end = 28407 + _GETWORKERBUILDIDCOMPATIBILITYRESPONSE._serialized_start = 28409 + _GETWORKERBUILDIDCOMPATIBILITYRESPONSE._serialized_end = 28525 + _UPDATEWORKERVERSIONINGRULESREQUEST._serialized_start = 28528 + _UPDATEWORKERVERSIONINGRULESREQUEST._serialized_end = 30245 + _UPDATEWORKERVERSIONINGRULESREQUEST_INSERTBUILDIDASSIGNMENTRULE._serialized_start = 29580 _UPDATEWORKERVERSIONINGRULESREQUEST_INSERTBUILDIDASSIGNMENTRULE._serialized_end = ( - 29224 + 29693 ) - _UPDATEWORKERVERSIONINGRULESREQUEST_REPLACEBUILDIDASSIGNMENTRULE._serialized_start = 29227 + _UPDATEWORKERVERSIONINGRULESREQUEST_REPLACEBUILDIDASSIGNMENTRULE._serialized_start = 29696 _UPDATEWORKERVERSIONINGRULESREQUEST_REPLACEBUILDIDASSIGNMENTRULE._serialized_end = ( - 29356 + 29825 ) - _UPDATEWORKERVERSIONINGRULESREQUEST_DELETEBUILDIDASSIGNMENTRULE._serialized_start = 29358 + _UPDATEWORKERVERSIONINGRULESREQUEST_DELETEBUILDIDASSIGNMENTRULE._serialized_start = 29827 _UPDATEWORKERVERSIONINGRULESREQUEST_DELETEBUILDIDASSIGNMENTRULE._serialized_end = ( - 29422 + 29891 ) - _UPDATEWORKERVERSIONINGRULESREQUEST_ADDCOMPATIBLEBUILDIDREDIRECTRULE._serialized_start = 29424 - _UPDATEWORKERVERSIONINGRULESREQUEST_ADDCOMPATIBLEBUILDIDREDIRECTRULE._serialized_end = 29530 - _UPDATEWORKERVERSIONINGRULESREQUEST_REPLACECOMPATIBLEBUILDIDREDIRECTRULE._serialized_start = 29532 - _UPDATEWORKERVERSIONINGRULESREQUEST_REPLACECOMPATIBLEBUILDIDREDIRECTRULE._serialized_end = 29642 - _UPDATEWORKERVERSIONINGRULESREQUEST_DELETECOMPATIBLEBUILDIDREDIRECTRULE._serialized_start = 29644 - _UPDATEWORKERVERSIONINGRULESREQUEST_DELETECOMPATIBLEBUILDIDREDIRECTRULE._serialized_end = 29706 - _UPDATEWORKERVERSIONINGRULESREQUEST_COMMITBUILDID._serialized_start = 29708 - _UPDATEWORKERVERSIONINGRULESREQUEST_COMMITBUILDID._serialized_end = 29763 - _UPDATEWORKERVERSIONINGRULESRESPONSE._serialized_start = 29779 - _UPDATEWORKERVERSIONINGRULESRESPONSE._serialized_end = 30031 - _GETWORKERVERSIONINGRULESREQUEST._serialized_start = 30033 - _GETWORKERVERSIONINGRULESREQUEST._serialized_end = 30105 - _GETWORKERVERSIONINGRULESRESPONSE._serialized_start = 30108 - _GETWORKERVERSIONINGRULESRESPONSE._serialized_end = 30357 - _GETWORKERTASKREACHABILITYREQUEST._serialized_start = 30360 - _GETWORKERTASKREACHABILITYREQUEST._serialized_end = 30516 - _GETWORKERTASKREACHABILITYRESPONSE._serialized_start = 30518 - _GETWORKERTASKREACHABILITYRESPONSE._serialized_end = 30632 - _UPDATEWORKFLOWEXECUTIONREQUEST._serialized_start = 30635 - _UPDATEWORKFLOWEXECUTIONREQUEST._serialized_end = 30896 - _UPDATEWORKFLOWEXECUTIONRESPONSE._serialized_start = 30899 - _UPDATEWORKFLOWEXECUTIONRESPONSE._serialized_end = 31158 - _STARTBATCHOPERATIONREQUEST._serialized_start = 31161 - _STARTBATCHOPERATIONREQUEST._serialized_end = 32173 - _STARTBATCHOPERATIONRESPONSE._serialized_start = 32175 - _STARTBATCHOPERATIONRESPONSE._serialized_end = 32204 - _STOPBATCHOPERATIONREQUEST._serialized_start = 32206 - _STOPBATCHOPERATIONREQUEST._serialized_end = 32302 - _STOPBATCHOPERATIONRESPONSE._serialized_start = 32304 - _STOPBATCHOPERATIONRESPONSE._serialized_end = 32332 - _DESCRIBEBATCHOPERATIONREQUEST._serialized_start = 32334 - _DESCRIBEBATCHOPERATIONREQUEST._serialized_end = 32400 - _DESCRIBEBATCHOPERATIONRESPONSE._serialized_start = 32403 - _DESCRIBEBATCHOPERATIONRESPONSE._serialized_end = 32805 - _LISTBATCHOPERATIONSREQUEST._serialized_start = 32807 - _LISTBATCHOPERATIONSREQUEST._serialized_end = 32898 - _LISTBATCHOPERATIONSRESPONSE._serialized_start = 32900 - _LISTBATCHOPERATIONSRESPONSE._serialized_end = 33021 - _POLLWORKFLOWEXECUTIONUPDATEREQUEST._serialized_start = 33024 - _POLLWORKFLOWEXECUTIONUPDATEREQUEST._serialized_end = 33209 - _POLLWORKFLOWEXECUTIONUPDATERESPONSE._serialized_start = 33212 - _POLLWORKFLOWEXECUTIONUPDATERESPONSE._serialized_end = 33431 - _POLLNEXUSTASKQUEUEREQUEST._serialized_start = 33434 - _POLLNEXUSTASKQUEUEREQUEST._serialized_end = 33850 - _POLLNEXUSTASKQUEUERESPONSE._serialized_start = 33853 - _POLLNEXUSTASKQUEUERESPONSE._serialized_end = 34130 - _RESPONDNEXUSTASKCOMPLETEDREQUEST._serialized_start = 34133 - _RESPONDNEXUSTASKCOMPLETEDREQUEST._serialized_end = 34300 - _RESPONDNEXUSTASKCOMPLETEDRESPONSE._serialized_start = 34302 - _RESPONDNEXUSTASKCOMPLETEDRESPONSE._serialized_end = 34337 - _RESPONDNEXUSTASKFAILEDREQUEST._serialized_start = 34340 - _RESPONDNEXUSTASKFAILEDREQUEST._serialized_end = 34560 - _RESPONDNEXUSTASKFAILEDRESPONSE._serialized_start = 34562 - _RESPONDNEXUSTASKFAILEDRESPONSE._serialized_end = 34594 - _EXECUTEMULTIOPERATIONREQUEST._serialized_start = 34597 - _EXECUTEMULTIOPERATIONREQUEST._serialized_end = 34969 - _EXECUTEMULTIOPERATIONREQUEST_OPERATION._serialized_start = 34763 - _EXECUTEMULTIOPERATIONREQUEST_OPERATION._serialized_end = 34969 - _EXECUTEMULTIOPERATIONRESPONSE._serialized_start = 34972 - _EXECUTEMULTIOPERATIONRESPONSE._serialized_end = 35304 - _EXECUTEMULTIOPERATIONRESPONSE_RESPONSE._serialized_start = 35098 - _EXECUTEMULTIOPERATIONRESPONSE_RESPONSE._serialized_end = 35304 - _UPDATEACTIVITYOPTIONSREQUEST._serialized_start = 35307 - _UPDATEACTIVITYOPTIONSREQUEST._serialized_end = 35643 - _UPDATEACTIVITYEXECUTIONOPTIONSREQUEST._serialized_start = 35646 - _UPDATEACTIVITYEXECUTIONOPTIONSREQUEST._serialized_end = 35945 - _UPDATEACTIVITYOPTIONSRESPONSE._serialized_start = 35947 - _UPDATEACTIVITYOPTIONSRESPONSE._serialized_end = 36047 - _UPDATEACTIVITYEXECUTIONOPTIONSRESPONSE._serialized_start = 36049 - _UPDATEACTIVITYEXECUTIONOPTIONSRESPONSE._serialized_end = 36158 - _PAUSEACTIVITYREQUEST._serialized_start = 36161 - _PAUSEACTIVITYREQUEST._serialized_end = 36360 - _PAUSEACTIVITYEXECUTIONREQUEST._serialized_start = 36363 - _PAUSEACTIVITYEXECUTIONREQUEST._serialized_end = 36546 - _PAUSEACTIVITYRESPONSE._serialized_start = 36548 - _PAUSEACTIVITYRESPONSE._serialized_end = 36571 - _PAUSEACTIVITYEXECUTIONRESPONSE._serialized_start = 36573 - _PAUSEACTIVITYEXECUTIONRESPONSE._serialized_end = 36605 - _UNPAUSEACTIVITYREQUEST._serialized_start = 36608 - _UNPAUSEACTIVITYREQUEST._serialized_end = 36888 - _UNPAUSEACTIVITYEXECUTIONREQUEST._serialized_start = 36891 - _UNPAUSEACTIVITYEXECUTIONREQUEST._serialized_end = 37148 - _UNPAUSEACTIVITYRESPONSE._serialized_start = 37150 - _UNPAUSEACTIVITYRESPONSE._serialized_end = 37175 - _UNPAUSEACTIVITYEXECUTIONRESPONSE._serialized_start = 37177 - _UNPAUSEACTIVITYEXECUTIONRESPONSE._serialized_end = 37211 - _RESETACTIVITYREQUEST._serialized_start = 37214 - _RESETACTIVITYREQUEST._serialized_end = 37521 - _RESETACTIVITYEXECUTIONREQUEST._serialized_start = 37524 - _RESETACTIVITYEXECUTIONREQUEST._serialized_end = 37794 - _RESETACTIVITYRESPONSE._serialized_start = 37796 - _RESETACTIVITYRESPONSE._serialized_end = 37819 - _RESETACTIVITYEXECUTIONRESPONSE._serialized_start = 37821 - _RESETACTIVITYEXECUTIONRESPONSE._serialized_end = 37853 - _UPDATEWORKFLOWEXECUTIONOPTIONSREQUEST._serialized_start = 37856 - _UPDATEWORKFLOWEXECUTIONOPTIONSREQUEST._serialized_end = 38140 - _UPDATEWORKFLOWEXECUTIONOPTIONSRESPONSE._serialized_start = 38143 - _UPDATEWORKFLOWEXECUTIONOPTIONSRESPONSE._serialized_end = 38271 - _DESCRIBEDEPLOYMENTREQUEST._serialized_start = 38273 - _DESCRIBEDEPLOYMENTREQUEST._serialized_end = 38379 - _DESCRIBEDEPLOYMENTRESPONSE._serialized_start = 38381 - _DESCRIBEDEPLOYMENTRESPONSE._serialized_end = 38478 - _DESCRIBEWORKERDEPLOYMENTVERSIONREQUEST._serialized_start = 38481 - _DESCRIBEWORKERDEPLOYMENTVERSIONREQUEST._serialized_end = 38675 - _DESCRIBEWORKERDEPLOYMENTVERSIONRESPONSE._serialized_start = 38678 - _DESCRIBEWORKERDEPLOYMENTVERSIONRESPONSE._serialized_end = 39330 - _DESCRIBEWORKERDEPLOYMENTVERSIONRESPONSE_VERSIONTASKQUEUE._serialized_start = 38939 - _DESCRIBEWORKERDEPLOYMENTVERSIONRESPONSE_VERSIONTASKQUEUE._serialized_end = 39330 - _DESCRIBEWORKERDEPLOYMENTVERSIONRESPONSE_VERSIONTASKQUEUE_STATSBYPRIORITYKEYENTRY._serialized_start = 23313 - _DESCRIBEWORKERDEPLOYMENTVERSIONRESPONSE_VERSIONTASKQUEUE_STATSBYPRIORITYKEYENTRY._serialized_end = 23413 - _DESCRIBEWORKERDEPLOYMENTREQUEST._serialized_start = 39332 - _DESCRIBEWORKERDEPLOYMENTREQUEST._serialized_end = 39409 - _DESCRIBEWORKERDEPLOYMENTRESPONSE._serialized_start = 39412 - _DESCRIBEWORKERDEPLOYMENTRESPONSE._serialized_end = 39552 - _LISTDEPLOYMENTSREQUEST._serialized_start = 39554 - _LISTDEPLOYMENTSREQUEST._serialized_end = 39662 - _LISTDEPLOYMENTSRESPONSE._serialized_start = 39664 - _LISTDEPLOYMENTSRESPONSE._serialized_end = 39783 - _SETCURRENTDEPLOYMENTREQUEST._serialized_start = 39786 - _SETCURRENTDEPLOYMENTREQUEST._serialized_end = 39991 - _SETCURRENTDEPLOYMENTRESPONSE._serialized_start = 39994 - _SETCURRENTDEPLOYMENTRESPONSE._serialized_end = 40179 - _SETWORKERDEPLOYMENTCURRENTVERSIONREQUEST._serialized_start = 40182 - _SETWORKERDEPLOYMENTCURRENTVERSIONREQUEST._serialized_end = 40411 - _SETWORKERDEPLOYMENTCURRENTVERSIONRESPONSE._serialized_start = 40414 - _SETWORKERDEPLOYMENTCURRENTVERSIONRESPONSE._serialized_end = 40605 - _SETWORKERDEPLOYMENTRAMPINGVERSIONREQUEST._serialized_start = 40608 - _SETWORKERDEPLOYMENTRAMPINGVERSIONREQUEST._serialized_end = 40857 - _SETWORKERDEPLOYMENTRAMPINGVERSIONRESPONSE._serialized_start = 40860 - _SETWORKERDEPLOYMENTRAMPINGVERSIONRESPONSE._serialized_end = 41084 - _CREATEWORKERDEPLOYMENTREQUEST._serialized_start = 41086 - _CREATEWORKERDEPLOYMENTREQUEST._serialized_end = 41199 - _CREATEWORKERDEPLOYMENTRESPONSE._serialized_start = 41201 - _CREATEWORKERDEPLOYMENTRESPONSE._serialized_end = 41257 - _LISTWORKERDEPLOYMENTSREQUEST._serialized_start = 41259 - _LISTWORKERDEPLOYMENTSREQUEST._serialized_end = 41352 - _LISTWORKERDEPLOYMENTSRESPONSE._serialized_start = 41355 - _LISTWORKERDEPLOYMENTSRESPONSE._serialized_end = 42026 - _LISTWORKERDEPLOYMENTSRESPONSE_WORKERDEPLOYMENTSUMMARY._serialized_start = 41530 - _LISTWORKERDEPLOYMENTSRESPONSE_WORKERDEPLOYMENTSUMMARY._serialized_end = 42026 - _CREATEWORKERDEPLOYMENTVERSIONREQUEST._serialized_start = 42029 - _CREATEWORKERDEPLOYMENTVERSIONREQUEST._serialized_end = 42269 - _CREATEWORKERDEPLOYMENTVERSIONRESPONSE._serialized_start = 42271 - _CREATEWORKERDEPLOYMENTVERSIONRESPONSE._serialized_end = 42310 - _DELETEWORKERDEPLOYMENTVERSIONREQUEST._serialized_start = 42313 - _DELETEWORKERDEPLOYMENTVERSIONREQUEST._serialized_end = 42513 - _DELETEWORKERDEPLOYMENTVERSIONRESPONSE._serialized_start = 42515 - _DELETEWORKERDEPLOYMENTVERSIONRESPONSE._serialized_end = 42554 - _DELETEWORKERDEPLOYMENTREQUEST._serialized_start = 42556 - _DELETEWORKERDEPLOYMENTREQUEST._serialized_end = 42649 - _DELETEWORKERDEPLOYMENTRESPONSE._serialized_start = 42651 - _DELETEWORKERDEPLOYMENTRESPONSE._serialized_end = 42683 - _UPDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST._serialized_start = 42686 - _UPDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST._serialized_end = 43202 - _UPDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST_COMPUTECONFIGSCALINGGROUPSENTRY._serialized_start = 43079 - _UPDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST_COMPUTECONFIGSCALINGGROUPSENTRY._serialized_end = 43202 - _UPDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGRESPONSE._serialized_start = 43204 - _UPDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGRESPONSE._serialized_end = 43256 - _VALIDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST._serialized_start = 43259 - _VALIDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST._serialized_end = 43759 - _VALIDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST_COMPUTECONFIGSCALINGGROUPSENTRY._serialized_start = 43079 - _VALIDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST_COMPUTECONFIGSCALINGGROUPSENTRY._serialized_end = 43202 - _VALIDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGRESPONSE._serialized_start = 43761 - _VALIDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGRESPONSE._serialized_end = 43815 - _UPDATEWORKERDEPLOYMENTVERSIONMETADATAREQUEST._serialized_start = 43818 - _UPDATEWORKERDEPLOYMENTVERSIONMETADATAREQUEST._serialized_end = 44236 - _UPDATEWORKERDEPLOYMENTVERSIONMETADATAREQUEST_UPSERTENTRIESENTRY._serialized_start = 44151 + _UPDATEWORKERVERSIONINGRULESREQUEST_ADDCOMPATIBLEBUILDIDREDIRECTRULE._serialized_start = 29893 + _UPDATEWORKERVERSIONINGRULESREQUEST_ADDCOMPATIBLEBUILDIDREDIRECTRULE._serialized_end = 29999 + _UPDATEWORKERVERSIONINGRULESREQUEST_REPLACECOMPATIBLEBUILDIDREDIRECTRULE._serialized_start = 30001 + _UPDATEWORKERVERSIONINGRULESREQUEST_REPLACECOMPATIBLEBUILDIDREDIRECTRULE._serialized_end = 30111 + _UPDATEWORKERVERSIONINGRULESREQUEST_DELETECOMPATIBLEBUILDIDREDIRECTRULE._serialized_start = 30113 + _UPDATEWORKERVERSIONINGRULESREQUEST_DELETECOMPATIBLEBUILDIDREDIRECTRULE._serialized_end = 30175 + _UPDATEWORKERVERSIONINGRULESREQUEST_COMMITBUILDID._serialized_start = 30177 + _UPDATEWORKERVERSIONINGRULESREQUEST_COMMITBUILDID._serialized_end = 30232 + _UPDATEWORKERVERSIONINGRULESRESPONSE._serialized_start = 30248 + _UPDATEWORKERVERSIONINGRULESRESPONSE._serialized_end = 30500 + _GETWORKERVERSIONINGRULESREQUEST._serialized_start = 30502 + _GETWORKERVERSIONINGRULESREQUEST._serialized_end = 30574 + _GETWORKERVERSIONINGRULESRESPONSE._serialized_start = 30577 + _GETWORKERVERSIONINGRULESRESPONSE._serialized_end = 30826 + _GETWORKERTASKREACHABILITYREQUEST._serialized_start = 30829 + _GETWORKERTASKREACHABILITYREQUEST._serialized_end = 30985 + _GETWORKERTASKREACHABILITYRESPONSE._serialized_start = 30987 + _GETWORKERTASKREACHABILITYRESPONSE._serialized_end = 31101 + _UPDATEWORKFLOWEXECUTIONREQUEST._serialized_start = 31104 + _UPDATEWORKFLOWEXECUTIONREQUEST._serialized_end = 31365 + _UPDATEWORKFLOWEXECUTIONRESPONSE._serialized_start = 31368 + _UPDATEWORKFLOWEXECUTIONRESPONSE._serialized_end = 31627 + _STARTBATCHOPERATIONREQUEST._serialized_start = 31630 + _STARTBATCHOPERATIONREQUEST._serialized_end = 32996 + _STARTBATCHOPERATIONRESPONSE._serialized_start = 32998 + _STARTBATCHOPERATIONRESPONSE._serialized_end = 33027 + _STOPBATCHOPERATIONREQUEST._serialized_start = 33029 + _STOPBATCHOPERATIONREQUEST._serialized_end = 33125 + _STOPBATCHOPERATIONRESPONSE._serialized_start = 33127 + _STOPBATCHOPERATIONRESPONSE._serialized_end = 33155 + _DESCRIBEBATCHOPERATIONREQUEST._serialized_start = 33157 + _DESCRIBEBATCHOPERATIONREQUEST._serialized_end = 33223 + _DESCRIBEBATCHOPERATIONRESPONSE._serialized_start = 33226 + _DESCRIBEBATCHOPERATIONRESPONSE._serialized_end = 33698 + _LISTBATCHOPERATIONSREQUEST._serialized_start = 33700 + _LISTBATCHOPERATIONSREQUEST._serialized_end = 33791 + _LISTBATCHOPERATIONSRESPONSE._serialized_start = 33793 + _LISTBATCHOPERATIONSRESPONSE._serialized_end = 33914 + _POLLWORKFLOWEXECUTIONUPDATEREQUEST._serialized_start = 33917 + _POLLWORKFLOWEXECUTIONUPDATEREQUEST._serialized_end = 34102 + _POLLWORKFLOWEXECUTIONUPDATERESPONSE._serialized_start = 34105 + _POLLWORKFLOWEXECUTIONUPDATERESPONSE._serialized_end = 34324 + _POLLNEXUSTASKQUEUEREQUEST._serialized_start = 34327 + _POLLNEXUSTASKQUEUEREQUEST._serialized_end = 34743 + _POLLNEXUSTASKQUEUERESPONSE._serialized_start = 34746 + _POLLNEXUSTASKQUEUERESPONSE._serialized_end = 35100 + _RESPONDNEXUSTASKCOMPLETEDREQUEST._serialized_start = 35103 + _RESPONDNEXUSTASKCOMPLETEDREQUEST._serialized_end = 35270 + _RESPONDNEXUSTASKCOMPLETEDRESPONSE._serialized_start = 35272 + _RESPONDNEXUSTASKCOMPLETEDRESPONSE._serialized_end = 35307 + _RESPONDNEXUSTASKFAILEDREQUEST._serialized_start = 35310 + _RESPONDNEXUSTASKFAILEDREQUEST._serialized_end = 35530 + _RESPONDNEXUSTASKFAILEDRESPONSE._serialized_start = 35532 + _RESPONDNEXUSTASKFAILEDRESPONSE._serialized_end = 35564 + _EXECUTEMULTIOPERATIONREQUEST._serialized_start = 35567 + _EXECUTEMULTIOPERATIONREQUEST._serialized_end = 35939 + _EXECUTEMULTIOPERATIONREQUEST_OPERATION._serialized_start = 35733 + _EXECUTEMULTIOPERATIONREQUEST_OPERATION._serialized_end = 35939 + _EXECUTEMULTIOPERATIONRESPONSE._serialized_start = 35942 + _EXECUTEMULTIOPERATIONRESPONSE._serialized_end = 36274 + _EXECUTEMULTIOPERATIONRESPONSE_RESPONSE._serialized_start = 36068 + _EXECUTEMULTIOPERATIONRESPONSE_RESPONSE._serialized_end = 36274 + _UPDATEACTIVITYOPTIONSREQUEST._serialized_start = 36277 + _UPDATEACTIVITYOPTIONSREQUEST._serialized_end = 36613 + _UPDATEACTIVITYEXECUTIONOPTIONSREQUEST._serialized_start = 36616 + _UPDATEACTIVITYEXECUTIONOPTIONSREQUEST._serialized_end = 36935 + _UPDATEACTIVITYOPTIONSRESPONSE._serialized_start = 36937 + _UPDATEACTIVITYOPTIONSRESPONSE._serialized_end = 37037 + _UPDATEACTIVITYEXECUTIONOPTIONSRESPONSE._serialized_start = 37039 + _UPDATEACTIVITYEXECUTIONOPTIONSRESPONSE._serialized_end = 37148 + _PAUSEACTIVITYREQUEST._serialized_start = 37151 + _PAUSEACTIVITYREQUEST._serialized_end = 37350 + _PAUSEACTIVITYEXECUTIONREQUEST._serialized_start = 37353 + _PAUSEACTIVITYEXECUTIONREQUEST._serialized_end = 37536 + _PAUSEACTIVITYRESPONSE._serialized_start = 37538 + _PAUSEACTIVITYRESPONSE._serialized_end = 37561 + _PAUSEACTIVITYEXECUTIONRESPONSE._serialized_start = 37563 + _PAUSEACTIVITYEXECUTIONRESPONSE._serialized_end = 37595 + _UNPAUSEACTIVITYREQUEST._serialized_start = 37598 + _UNPAUSEACTIVITYREQUEST._serialized_end = 37878 + _UNPAUSEACTIVITYEXECUTIONREQUEST._serialized_start = 37881 + _UNPAUSEACTIVITYEXECUTIONREQUEST._serialized_end = 38154 + _UNPAUSEACTIVITYRESPONSE._serialized_start = 38156 + _UNPAUSEACTIVITYRESPONSE._serialized_end = 38181 + _UNPAUSEACTIVITYEXECUTIONRESPONSE._serialized_start = 38183 + _UNPAUSEACTIVITYEXECUTIONRESPONSE._serialized_end = 38217 + _RESETACTIVITYREQUEST._serialized_start = 38220 + _RESETACTIVITYREQUEST._serialized_end = 38527 + _RESETACTIVITYEXECUTIONREQUEST._serialized_start = 38530 + _RESETACTIVITYEXECUTIONREQUEST._serialized_end = 38820 + _RESETACTIVITYRESPONSE._serialized_start = 38822 + _RESETACTIVITYRESPONSE._serialized_end = 38845 + _RESETACTIVITYEXECUTIONRESPONSE._serialized_start = 38847 + _RESETACTIVITYEXECUTIONRESPONSE._serialized_end = 38879 + _UPDATEWORKFLOWEXECUTIONOPTIONSREQUEST._serialized_start = 38882 + _UPDATEWORKFLOWEXECUTIONOPTIONSREQUEST._serialized_end = 39166 + _UPDATEWORKFLOWEXECUTIONOPTIONSRESPONSE._serialized_start = 39169 + _UPDATEWORKFLOWEXECUTIONOPTIONSRESPONSE._serialized_end = 39346 + _DESCRIBEDEPLOYMENTREQUEST._serialized_start = 39348 + _DESCRIBEDEPLOYMENTREQUEST._serialized_end = 39454 + _DESCRIBEDEPLOYMENTRESPONSE._serialized_start = 39456 + _DESCRIBEDEPLOYMENTRESPONSE._serialized_end = 39553 + _DESCRIBEWORKERDEPLOYMENTVERSIONREQUEST._serialized_start = 39556 + _DESCRIBEWORKERDEPLOYMENTVERSIONREQUEST._serialized_end = 39750 + _DESCRIBEWORKERDEPLOYMENTVERSIONRESPONSE._serialized_start = 39753 + _DESCRIBEWORKERDEPLOYMENTVERSIONRESPONSE._serialized_end = 40405 + _DESCRIBEWORKERDEPLOYMENTVERSIONRESPONSE_VERSIONTASKQUEUE._serialized_start = 40014 + _DESCRIBEWORKERDEPLOYMENTVERSIONRESPONSE_VERSIONTASKQUEUE._serialized_end = 40405 + _DESCRIBEWORKERDEPLOYMENTVERSIONRESPONSE_VERSIONTASKQUEUE_STATSBYPRIORITYKEYENTRY._serialized_start = 23740 + _DESCRIBEWORKERDEPLOYMENTVERSIONRESPONSE_VERSIONTASKQUEUE_STATSBYPRIORITYKEYENTRY._serialized_end = 23840 + _DESCRIBEWORKERDEPLOYMENTREQUEST._serialized_start = 40407 + _DESCRIBEWORKERDEPLOYMENTREQUEST._serialized_end = 40484 + _DESCRIBEWORKERDEPLOYMENTRESPONSE._serialized_start = 40487 + _DESCRIBEWORKERDEPLOYMENTRESPONSE._serialized_end = 40627 + _LISTDEPLOYMENTSREQUEST._serialized_start = 40629 + _LISTDEPLOYMENTSREQUEST._serialized_end = 40737 + _LISTDEPLOYMENTSRESPONSE._serialized_start = 40739 + _LISTDEPLOYMENTSRESPONSE._serialized_end = 40858 + _SETCURRENTDEPLOYMENTREQUEST._serialized_start = 40861 + _SETCURRENTDEPLOYMENTREQUEST._serialized_end = 41066 + _SETCURRENTDEPLOYMENTRESPONSE._serialized_start = 41069 + _SETCURRENTDEPLOYMENTRESPONSE._serialized_end = 41254 + _SETWORKERDEPLOYMENTCURRENTVERSIONREQUEST._serialized_start = 41257 + _SETWORKERDEPLOYMENTCURRENTVERSIONREQUEST._serialized_end = 41486 + _SETWORKERDEPLOYMENTCURRENTVERSIONRESPONSE._serialized_start = 41489 + _SETWORKERDEPLOYMENTCURRENTVERSIONRESPONSE._serialized_end = 41680 + _SETWORKERDEPLOYMENTRAMPINGVERSIONREQUEST._serialized_start = 41683 + _SETWORKERDEPLOYMENTRAMPINGVERSIONREQUEST._serialized_end = 41932 + _SETWORKERDEPLOYMENTRAMPINGVERSIONRESPONSE._serialized_start = 41935 + _SETWORKERDEPLOYMENTRAMPINGVERSIONRESPONSE._serialized_end = 42159 + _CREATEWORKERDEPLOYMENTREQUEST._serialized_start = 42161 + _CREATEWORKERDEPLOYMENTREQUEST._serialized_end = 42274 + _CREATEWORKERDEPLOYMENTRESPONSE._serialized_start = 42276 + _CREATEWORKERDEPLOYMENTRESPONSE._serialized_end = 42332 + _LISTWORKERDEPLOYMENTSREQUEST._serialized_start = 42334 + _LISTWORKERDEPLOYMENTSREQUEST._serialized_end = 42427 + _LISTWORKERDEPLOYMENTSRESPONSE._serialized_start = 42430 + _LISTWORKERDEPLOYMENTSRESPONSE._serialized_end = 43101 + _LISTWORKERDEPLOYMENTSRESPONSE_WORKERDEPLOYMENTSUMMARY._serialized_start = 42605 + _LISTWORKERDEPLOYMENTSRESPONSE_WORKERDEPLOYMENTSUMMARY._serialized_end = 43101 + _CREATEWORKERDEPLOYMENTVERSIONREQUEST._serialized_start = 43104 + _CREATEWORKERDEPLOYMENTVERSIONREQUEST._serialized_end = 43344 + _CREATEWORKERDEPLOYMENTVERSIONRESPONSE._serialized_start = 43346 + _CREATEWORKERDEPLOYMENTVERSIONRESPONSE._serialized_end = 43385 + _DELETEWORKERDEPLOYMENTVERSIONREQUEST._serialized_start = 43388 + _DELETEWORKERDEPLOYMENTVERSIONREQUEST._serialized_end = 43588 + _DELETEWORKERDEPLOYMENTVERSIONRESPONSE._serialized_start = 43590 + _DELETEWORKERDEPLOYMENTVERSIONRESPONSE._serialized_end = 43629 + _DELETEWORKERDEPLOYMENTREQUEST._serialized_start = 43631 + _DELETEWORKERDEPLOYMENTREQUEST._serialized_end = 43724 + _DELETEWORKERDEPLOYMENTRESPONSE._serialized_start = 43726 + _DELETEWORKERDEPLOYMENTRESPONSE._serialized_end = 43758 + _UPDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST._serialized_start = 43761 + _UPDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST._serialized_end = 44277 + _UPDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST_COMPUTECONFIGSCALINGGROUPSENTRY._serialized_start = 44154 + _UPDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST_COMPUTECONFIGSCALINGGROUPSENTRY._serialized_end = 44277 + _UPDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGRESPONSE._serialized_start = 44279 + _UPDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGRESPONSE._serialized_end = 44331 + _VALIDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST._serialized_start = 44334 + _VALIDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST._serialized_end = 44834 + _VALIDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST_COMPUTECONFIGSCALINGGROUPSENTRY._serialized_start = 44154 + _VALIDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGREQUEST_COMPUTECONFIGSCALINGGROUPSENTRY._serialized_end = 44277 + _VALIDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGRESPONSE._serialized_start = 44836 + _VALIDATEWORKERDEPLOYMENTVERSIONCOMPUTECONFIGRESPONSE._serialized_end = 44890 + _UPDATEWORKERDEPLOYMENTVERSIONMETADATAREQUEST._serialized_start = 44893 + _UPDATEWORKERDEPLOYMENTVERSIONMETADATAREQUEST._serialized_end = 45311 + _UPDATEWORKERDEPLOYMENTVERSIONMETADATAREQUEST_UPSERTENTRIESENTRY._serialized_start = 45226 _UPDATEWORKERDEPLOYMENTVERSIONMETADATAREQUEST_UPSERTENTRIESENTRY._serialized_end = ( - 44236 + 45311 ) - _UPDATEWORKERDEPLOYMENTVERSIONMETADATARESPONSE._serialized_start = 44238 - _UPDATEWORKERDEPLOYMENTVERSIONMETADATARESPONSE._serialized_end = 44348 - _SETWORKERDEPLOYMENTMANAGERREQUEST._serialized_start = 44351 - _SETWORKERDEPLOYMENTMANAGERREQUEST._serialized_end = 44540 - _SETWORKERDEPLOYMENTMANAGERRESPONSE._serialized_start = 44542 - _SETWORKERDEPLOYMENTMANAGERRESPONSE._serialized_end = 44641 - _GETCURRENTDEPLOYMENTREQUEST._serialized_start = 44643 - _GETCURRENTDEPLOYMENTREQUEST._serialized_end = 44712 - _GETCURRENTDEPLOYMENTRESPONSE._serialized_start = 44714 - _GETCURRENTDEPLOYMENTRESPONSE._serialized_end = 44821 - _GETDEPLOYMENTREACHABILITYREQUEST._serialized_start = 44823 - _GETDEPLOYMENTREACHABILITYREQUEST._serialized_end = 44936 - _GETDEPLOYMENTREACHABILITYRESPONSE._serialized_start = 44939 - _GETDEPLOYMENTREACHABILITYRESPONSE._serialized_end = 45166 - _CREATEWORKFLOWRULEREQUEST._serialized_start = 45169 - _CREATEWORKFLOWRULEREQUEST._serialized_end = 45349 - _CREATEWORKFLOWRULERESPONSE._serialized_start = 45351 - _CREATEWORKFLOWRULERESPONSE._serialized_end = 45446 - _DESCRIBEWORKFLOWRULEREQUEST._serialized_start = 45448 - _DESCRIBEWORKFLOWRULEREQUEST._serialized_end = 45513 - _DESCRIBEWORKFLOWRULERESPONSE._serialized_start = 45515 - _DESCRIBEWORKFLOWRULERESPONSE._serialized_end = 45596 - _DELETEWORKFLOWRULEREQUEST._serialized_start = 45598 - _DELETEWORKFLOWRULEREQUEST._serialized_end = 45661 - _DELETEWORKFLOWRULERESPONSE._serialized_start = 45663 - _DELETEWORKFLOWRULERESPONSE._serialized_end = 45691 - _LISTWORKFLOWRULESREQUEST._serialized_start = 45693 - _LISTWORKFLOWRULESREQUEST._serialized_end = 45763 - _LISTWORKFLOWRULESRESPONSE._serialized_start = 45765 - _LISTWORKFLOWRULESRESPONSE._serialized_end = 45869 - _TRIGGERWORKFLOWRULEREQUEST._serialized_start = 45872 - _TRIGGERWORKFLOWRULEREQUEST._serialized_end = 46078 - _TRIGGERWORKFLOWRULERESPONSE._serialized_start = 46080 - _TRIGGERWORKFLOWRULERESPONSE._serialized_end = 46126 - _RECORDWORKERHEARTBEATREQUEST._serialized_start = 46129 - _RECORDWORKERHEARTBEATREQUEST._serialized_end = 46284 - _RECORDWORKERHEARTBEATRESPONSE._serialized_start = 46286 - _RECORDWORKERHEARTBEATRESPONSE._serialized_end = 46317 - _LISTWORKERSREQUEST._serialized_start = 46320 - _LISTWORKERSREQUEST._serialized_end = 46450 - _LISTWORKERSRESPONSE._serialized_start = 46453 - _LISTWORKERSRESPONSE._serialized_end = 46618 - _UPDATETASKQUEUECONFIGREQUEST._serialized_start = 46621 - _UPDATETASKQUEUECONFIGREQUEST._serialized_end = 47346 - _UPDATETASKQUEUECONFIGREQUEST_RATELIMITUPDATE._serialized_start = 47188 - _UPDATETASKQUEUECONFIGREQUEST_RATELIMITUPDATE._serialized_end = 47279 + _UPDATEWORKERDEPLOYMENTVERSIONMETADATARESPONSE._serialized_start = 45313 + _UPDATEWORKERDEPLOYMENTVERSIONMETADATARESPONSE._serialized_end = 45423 + _SETWORKERDEPLOYMENTMANAGERREQUEST._serialized_start = 45426 + _SETWORKERDEPLOYMENTMANAGERREQUEST._serialized_end = 45615 + _SETWORKERDEPLOYMENTMANAGERRESPONSE._serialized_start = 45617 + _SETWORKERDEPLOYMENTMANAGERRESPONSE._serialized_end = 45716 + _GETCURRENTDEPLOYMENTREQUEST._serialized_start = 45718 + _GETCURRENTDEPLOYMENTREQUEST._serialized_end = 45787 + _GETCURRENTDEPLOYMENTRESPONSE._serialized_start = 45789 + _GETCURRENTDEPLOYMENTRESPONSE._serialized_end = 45896 + _GETDEPLOYMENTREACHABILITYREQUEST._serialized_start = 45898 + _GETDEPLOYMENTREACHABILITYREQUEST._serialized_end = 46011 + _GETDEPLOYMENTREACHABILITYRESPONSE._serialized_start = 46014 + _GETDEPLOYMENTREACHABILITYRESPONSE._serialized_end = 46241 + _CREATEWORKFLOWRULEREQUEST._serialized_start = 46244 + _CREATEWORKFLOWRULEREQUEST._serialized_end = 46424 + _CREATEWORKFLOWRULERESPONSE._serialized_start = 46426 + _CREATEWORKFLOWRULERESPONSE._serialized_end = 46521 + _DESCRIBEWORKFLOWRULEREQUEST._serialized_start = 46523 + _DESCRIBEWORKFLOWRULEREQUEST._serialized_end = 46588 + _DESCRIBEWORKFLOWRULERESPONSE._serialized_start = 46590 + _DESCRIBEWORKFLOWRULERESPONSE._serialized_end = 46671 + _DELETEWORKFLOWRULEREQUEST._serialized_start = 46673 + _DELETEWORKFLOWRULEREQUEST._serialized_end = 46736 + _DELETEWORKFLOWRULERESPONSE._serialized_start = 46738 + _DELETEWORKFLOWRULERESPONSE._serialized_end = 46766 + _LISTWORKFLOWRULESREQUEST._serialized_start = 46768 + _LISTWORKFLOWRULESREQUEST._serialized_end = 46838 + _LISTWORKFLOWRULESRESPONSE._serialized_start = 46840 + _LISTWORKFLOWRULESRESPONSE._serialized_end = 46944 + _TRIGGERWORKFLOWRULEREQUEST._serialized_start = 46947 + _TRIGGERWORKFLOWRULEREQUEST._serialized_end = 47153 + _TRIGGERWORKFLOWRULERESPONSE._serialized_start = 47155 + _TRIGGERWORKFLOWRULERESPONSE._serialized_end = 47201 + _RECORDWORKERHEARTBEATREQUEST._serialized_start = 47204 + _RECORDWORKERHEARTBEATREQUEST._serialized_end = 47359 + _RECORDWORKERHEARTBEATRESPONSE._serialized_start = 47361 + _RECORDWORKERHEARTBEATRESPONSE._serialized_end = 47392 + _LISTWORKERSREQUEST._serialized_start = 47395 + _LISTWORKERSREQUEST._serialized_end = 47525 + _LISTWORKERSRESPONSE._serialized_start = 47528 + _LISTWORKERSRESPONSE._serialized_end = 47693 + _UPDATETASKQUEUECONFIGREQUEST._serialized_start = 47696 + _UPDATETASKQUEUECONFIGREQUEST._serialized_end = 48421 + _UPDATETASKQUEUECONFIGREQUEST_RATELIMITUPDATE._serialized_start = 48263 + _UPDATETASKQUEUECONFIGREQUEST_RATELIMITUPDATE._serialized_end = 48354 _UPDATETASKQUEUECONFIGREQUEST_SETFAIRNESSWEIGHTOVERRIDESENTRY._serialized_start = ( - 47281 + 48356 ) _UPDATETASKQUEUECONFIGREQUEST_SETFAIRNESSWEIGHTOVERRIDESENTRY._serialized_end = ( - 47346 + 48421 ) - _UPDATETASKQUEUECONFIGRESPONSE._serialized_start = 47348 - _UPDATETASKQUEUECONFIGRESPONSE._serialized_end = 47439 - _FETCHWORKERCONFIGREQUEST._serialized_start = 47442 - _FETCHWORKERCONFIGREQUEST._serialized_end = 47600 - _FETCHWORKERCONFIGRESPONSE._serialized_start = 47602 - _FETCHWORKERCONFIGRESPONSE._serialized_end = 47687 - _UPDATEWORKERCONFIGREQUEST._serialized_start = 47690 - _UPDATEWORKERCONFIGREQUEST._serialized_end = 47956 - _UPDATEWORKERCONFIGRESPONSE._serialized_start = 47958 - _UPDATEWORKERCONFIGRESPONSE._serialized_end = 48058 - _DESCRIBEWORKERREQUEST._serialized_start = 48060 - _DESCRIBEWORKERREQUEST._serialized_end = 48131 - _DESCRIBEWORKERRESPONSE._serialized_start = 48133 - _DESCRIBEWORKERRESPONSE._serialized_end = 48214 - _PAUSEWORKFLOWEXECUTIONREQUEST._serialized_start = 48217 - _PAUSEWORKFLOWEXECUTIONREQUEST._serialized_end = 48358 - _PAUSEWORKFLOWEXECUTIONRESPONSE._serialized_start = 48360 - _PAUSEWORKFLOWEXECUTIONRESPONSE._serialized_end = 48392 - _UNPAUSEWORKFLOWEXECUTIONREQUEST._serialized_start = 48395 - _UNPAUSEWORKFLOWEXECUTIONREQUEST._serialized_end = 48538 - _UNPAUSEWORKFLOWEXECUTIONRESPONSE._serialized_start = 48540 - _UNPAUSEWORKFLOWEXECUTIONRESPONSE._serialized_end = 48574 - _STARTACTIVITYEXECUTIONREQUEST._serialized_start = 48577 - _STARTACTIVITYEXECUTIONREQUEST._serialized_end = 49754 - _STARTACTIVITYEXECUTIONRESPONSE._serialized_start = 49756 - _STARTACTIVITYEXECUTIONRESPONSE._serialized_end = 49865 - _DESCRIBEACTIVITYEXECUTIONREQUEST._serialized_start = 49868 - _DESCRIBEACTIVITYEXECUTIONREQUEST._serialized_end = 50096 - _DESCRIBEACTIVITYEXECUTIONRESPONSE._serialized_start = 50099 - _DESCRIBEACTIVITYEXECUTIONRESPONSE._serialized_end = 50415 - _POLLACTIVITYEXECUTIONREQUEST._serialized_start = 50417 - _POLLACTIVITYEXECUTIONREQUEST._serialized_end = 50503 - _POLLACTIVITYEXECUTIONRESPONSE._serialized_start = 50505 - _POLLACTIVITYEXECUTIONRESPONSE._serialized_end = 50621 - _LISTACTIVITYEXECUTIONSREQUEST._serialized_start = 50623 - _LISTACTIVITYEXECUTIONSREQUEST._serialized_end = 50732 - _LISTACTIVITYEXECUTIONSRESPONSE._serialized_start = 50735 - _LISTACTIVITYEXECUTIONSRESPONSE._serialized_end = 50865 - _STARTNEXUSOPERATIONEXECUTIONREQUEST._serialized_start = 50868 - _STARTNEXUSOPERATIONEXECUTIONREQUEST._serialized_end = 51717 - _STARTNEXUSOPERATIONEXECUTIONREQUEST_NEXUSHEADERENTRY._serialized_start = 51667 - _STARTNEXUSOPERATIONEXECUTIONREQUEST_NEXUSHEADERENTRY._serialized_end = 51717 - _STARTNEXUSOPERATIONEXECUTIONRESPONSE._serialized_start = 51719 - _STARTNEXUSOPERATIONEXECUTIONRESPONSE._serialized_end = 51790 - _DESCRIBENEXUSOPERATIONEXECUTIONREQUEST._serialized_start = 51793 - _DESCRIBENEXUSOPERATIONEXECUTIONREQUEST._serialized_end = 51963 - _DESCRIBENEXUSOPERATIONEXECUTIONRESPONSE._serialized_start = 51966 - _DESCRIBENEXUSOPERATIONEXECUTIONRESPONSE._serialized_end = 52277 - _POLLNEXUSOPERATIONEXECUTIONREQUEST._serialized_start = 52280 - _POLLNEXUSOPERATIONEXECUTIONREQUEST._serialized_end = 52441 - _POLLNEXUSOPERATIONEXECUTIONRESPONSE._serialized_start = 52444 - _POLLNEXUSOPERATIONEXECUTIONRESPONSE._serialized_end = 52705 - _LISTNEXUSOPERATIONEXECUTIONSREQUEST._serialized_start = 52707 - _LISTNEXUSOPERATIONEXECUTIONSREQUEST._serialized_end = 52822 - _LISTNEXUSOPERATIONEXECUTIONSRESPONSE._serialized_start = 52825 - _LISTNEXUSOPERATIONEXECUTIONSRESPONSE._serialized_end = 52964 - _COUNTACTIVITYEXECUTIONSREQUEST._serialized_start = 52966 - _COUNTACTIVITYEXECUTIONSREQUEST._serialized_end = 53032 - _COUNTACTIVITYEXECUTIONSRESPONSE._serialized_start = 53035 - _COUNTACTIVITYEXECUTIONSRESPONSE._serialized_end = 53272 - _COUNTACTIVITYEXECUTIONSRESPONSE_AGGREGATIONGROUP._serialized_start = 19751 - _COUNTACTIVITYEXECUTIONSRESPONSE_AGGREGATIONGROUP._serialized_end = 19839 - _COUNTNEXUSOPERATIONEXECUTIONSREQUEST._serialized_start = 53274 - _COUNTNEXUSOPERATIONEXECUTIONSREQUEST._serialized_end = 53346 - _COUNTNEXUSOPERATIONEXECUTIONSRESPONSE._serialized_start = 53349 - _COUNTNEXUSOPERATIONEXECUTIONSRESPONSE._serialized_end = 53598 - _COUNTNEXUSOPERATIONEXECUTIONSRESPONSE_AGGREGATIONGROUP._serialized_start = 19751 - _COUNTNEXUSOPERATIONEXECUTIONSRESPONSE_AGGREGATIONGROUP._serialized_end = 19839 - _REQUESTCANCELACTIVITYEXECUTIONREQUEST._serialized_start = 53601 - _REQUESTCANCELACTIVITYEXECUTIONREQUEST._serialized_end = 53750 - _REQUESTCANCELACTIVITYEXECUTIONRESPONSE._serialized_start = 53752 - _REQUESTCANCELACTIVITYEXECUTIONRESPONSE._serialized_end = 53792 - _TERMINATEACTIVITYEXECUTIONREQUEST._serialized_start = 53795 - _TERMINATEACTIVITYEXECUTIONREQUEST._serialized_end = 53940 - _TERMINATEACTIVITYEXECUTIONRESPONSE._serialized_start = 53942 - _TERMINATEACTIVITYEXECUTIONRESPONSE._serialized_end = 53978 - _DELETEACTIVITYEXECUTIONREQUEST._serialized_start = 53980 - _DELETEACTIVITYEXECUTIONREQUEST._serialized_end = 54068 - _DELETEACTIVITYEXECUTIONRESPONSE._serialized_start = 54070 - _DELETEACTIVITYEXECUTIONRESPONSE._serialized_end = 54103 - _REQUESTCANCELNEXUSOPERATIONEXECUTIONREQUEST._serialized_start = 54106 - _REQUESTCANCELNEXUSOPERATIONEXECUTIONREQUEST._serialized_end = 54262 - _REQUESTCANCELNEXUSOPERATIONEXECUTIONRESPONSE._serialized_start = 54264 - _REQUESTCANCELNEXUSOPERATIONEXECUTIONRESPONSE._serialized_end = 54310 - _TERMINATENEXUSOPERATIONEXECUTIONREQUEST._serialized_start = 54313 - _TERMINATENEXUSOPERATIONEXECUTIONREQUEST._serialized_end = 54465 - _TERMINATENEXUSOPERATIONEXECUTIONRESPONSE._serialized_start = 54467 - _TERMINATENEXUSOPERATIONEXECUTIONRESPONSE._serialized_end = 54509 - _DELETENEXUSOPERATIONEXECUTIONREQUEST._serialized_start = 54511 - _DELETENEXUSOPERATIONEXECUTIONREQUEST._serialized_end = 54606 - _DELETENEXUSOPERATIONEXECUTIONRESPONSE._serialized_start = 54608 - _DELETENEXUSOPERATIONEXECUTIONRESPONSE._serialized_end = 54647 + _UPDATETASKQUEUECONFIGRESPONSE._serialized_start = 48423 + _UPDATETASKQUEUECONFIGRESPONSE._serialized_end = 48514 + _FETCHWORKERCONFIGREQUEST._serialized_start = 48517 + _FETCHWORKERCONFIGREQUEST._serialized_end = 48675 + _FETCHWORKERCONFIGRESPONSE._serialized_start = 48677 + _FETCHWORKERCONFIGRESPONSE._serialized_end = 48762 + _UPDATEWORKERCONFIGREQUEST._serialized_start = 48765 + _UPDATEWORKERCONFIGREQUEST._serialized_end = 49031 + _UPDATEWORKERCONFIGRESPONSE._serialized_start = 49033 + _UPDATEWORKERCONFIGRESPONSE._serialized_end = 49133 + _DESCRIBEWORKERREQUEST._serialized_start = 49135 + _DESCRIBEWORKERREQUEST._serialized_end = 49206 + _DESCRIBEWORKERRESPONSE._serialized_start = 49208 + _DESCRIBEWORKERRESPONSE._serialized_end = 49289 + _COUNTWORKERSREQUEST._serialized_start = 49291 + _COUNTWORKERSREQUEST._serialized_end = 49378 + _COUNTWORKERSRESPONSE._serialized_start = 49380 + _COUNTWORKERSRESPONSE._serialized_end = 49417 + _PAUSEWORKFLOWEXECUTIONREQUEST._serialized_start = 49420 + _PAUSEWORKFLOWEXECUTIONREQUEST._serialized_end = 49561 + _PAUSEWORKFLOWEXECUTIONRESPONSE._serialized_start = 49563 + _PAUSEWORKFLOWEXECUTIONRESPONSE._serialized_end = 49595 + _UNPAUSEWORKFLOWEXECUTIONREQUEST._serialized_start = 49598 + _UNPAUSEWORKFLOWEXECUTIONREQUEST._serialized_end = 49741 + _UNPAUSEWORKFLOWEXECUTIONRESPONSE._serialized_start = 49743 + _UNPAUSEWORKFLOWEXECUTIONRESPONSE._serialized_end = 49777 + _STARTACTIVITYEXECUTIONREQUEST._serialized_start = 49780 + _STARTACTIVITYEXECUTIONREQUEST._serialized_end = 50957 + _STARTACTIVITYEXECUTIONRESPONSE._serialized_start = 50959 + _STARTACTIVITYEXECUTIONRESPONSE._serialized_end = 51068 + _DESCRIBEACTIVITYEXECUTIONREQUEST._serialized_start = 51071 + _DESCRIBEACTIVITYEXECUTIONREQUEST._serialized_end = 51299 + _DESCRIBEACTIVITYEXECUTIONRESPONSE._serialized_start = 51302 + _DESCRIBEACTIVITYEXECUTIONRESPONSE._serialized_end = 51618 + _POLLACTIVITYEXECUTIONREQUEST._serialized_start = 51620 + _POLLACTIVITYEXECUTIONREQUEST._serialized_end = 51706 + _POLLACTIVITYEXECUTIONRESPONSE._serialized_start = 51708 + _POLLACTIVITYEXECUTIONRESPONSE._serialized_end = 51824 + _LISTACTIVITYEXECUTIONSREQUEST._serialized_start = 51826 + _LISTACTIVITYEXECUTIONSREQUEST._serialized_end = 51935 + _LISTACTIVITYEXECUTIONSRESPONSE._serialized_start = 51938 + _LISTACTIVITYEXECUTIONSRESPONSE._serialized_end = 52068 + _STARTNEXUSOPERATIONEXECUTIONREQUEST._serialized_start = 52071 + _STARTNEXUSOPERATIONEXECUTIONREQUEST._serialized_end = 52920 + _STARTNEXUSOPERATIONEXECUTIONREQUEST_NEXUSHEADERENTRY._serialized_start = 52870 + _STARTNEXUSOPERATIONEXECUTIONREQUEST_NEXUSHEADERENTRY._serialized_end = 52920 + _STARTNEXUSOPERATIONEXECUTIONRESPONSE._serialized_start = 52922 + _STARTNEXUSOPERATIONEXECUTIONRESPONSE._serialized_end = 52993 + _DESCRIBENEXUSOPERATIONEXECUTIONREQUEST._serialized_start = 52996 + _DESCRIBENEXUSOPERATIONEXECUTIONREQUEST._serialized_end = 53166 + _DESCRIBENEXUSOPERATIONEXECUTIONRESPONSE._serialized_start = 53169 + _DESCRIBENEXUSOPERATIONEXECUTIONRESPONSE._serialized_end = 53480 + _POLLNEXUSOPERATIONEXECUTIONREQUEST._serialized_start = 53483 + _POLLNEXUSOPERATIONEXECUTIONREQUEST._serialized_end = 53644 + _POLLNEXUSOPERATIONEXECUTIONRESPONSE._serialized_start = 53647 + _POLLNEXUSOPERATIONEXECUTIONRESPONSE._serialized_end = 53908 + _LISTNEXUSOPERATIONEXECUTIONSREQUEST._serialized_start = 53910 + _LISTNEXUSOPERATIONEXECUTIONSREQUEST._serialized_end = 54025 + _LISTNEXUSOPERATIONEXECUTIONSRESPONSE._serialized_start = 54028 + _LISTNEXUSOPERATIONEXECUTIONSRESPONSE._serialized_end = 54167 + _COUNTACTIVITYEXECUTIONSREQUEST._serialized_start = 54169 + _COUNTACTIVITYEXECUTIONSREQUEST._serialized_end = 54235 + _COUNTACTIVITYEXECUTIONSRESPONSE._serialized_start = 54238 + _COUNTACTIVITYEXECUTIONSRESPONSE._serialized_end = 54475 + _COUNTACTIVITYEXECUTIONSRESPONSE_AGGREGATIONGROUP._serialized_start = 20134 + _COUNTACTIVITYEXECUTIONSRESPONSE_AGGREGATIONGROUP._serialized_end = 20222 + _COUNTNEXUSOPERATIONEXECUTIONSREQUEST._serialized_start = 54477 + _COUNTNEXUSOPERATIONEXECUTIONSREQUEST._serialized_end = 54549 + _COUNTNEXUSOPERATIONEXECUTIONSRESPONSE._serialized_start = 54552 + _COUNTNEXUSOPERATIONEXECUTIONSRESPONSE._serialized_end = 54801 + _COUNTNEXUSOPERATIONEXECUTIONSRESPONSE_AGGREGATIONGROUP._serialized_start = 20134 + _COUNTNEXUSOPERATIONEXECUTIONSRESPONSE_AGGREGATIONGROUP._serialized_end = 20222 + _REQUESTCANCELACTIVITYEXECUTIONREQUEST._serialized_start = 54804 + _REQUESTCANCELACTIVITYEXECUTIONREQUEST._serialized_end = 54953 + _REQUESTCANCELACTIVITYEXECUTIONRESPONSE._serialized_start = 54955 + _REQUESTCANCELACTIVITYEXECUTIONRESPONSE._serialized_end = 54995 + _TERMINATEACTIVITYEXECUTIONREQUEST._serialized_start = 54998 + _TERMINATEACTIVITYEXECUTIONREQUEST._serialized_end = 55143 + _TERMINATEACTIVITYEXECUTIONRESPONSE._serialized_start = 55145 + _TERMINATEACTIVITYEXECUTIONRESPONSE._serialized_end = 55181 + _DELETEACTIVITYEXECUTIONREQUEST._serialized_start = 55183 + _DELETEACTIVITYEXECUTIONREQUEST._serialized_end = 55271 + _DELETEACTIVITYEXECUTIONRESPONSE._serialized_start = 55273 + _DELETEACTIVITYEXECUTIONRESPONSE._serialized_end = 55306 + _REQUESTCANCELNEXUSOPERATIONEXECUTIONREQUEST._serialized_start = 55309 + _REQUESTCANCELNEXUSOPERATIONEXECUTIONREQUEST._serialized_end = 55465 + _REQUESTCANCELNEXUSOPERATIONEXECUTIONRESPONSE._serialized_start = 55467 + _REQUESTCANCELNEXUSOPERATIONEXECUTIONRESPONSE._serialized_end = 55513 + _TERMINATENEXUSOPERATIONEXECUTIONREQUEST._serialized_start = 55516 + _TERMINATENEXUSOPERATIONEXECUTIONREQUEST._serialized_end = 55668 + _TERMINATENEXUSOPERATIONEXECUTIONRESPONSE._serialized_start = 55670 + _TERMINATENEXUSOPERATIONEXECUTIONRESPONSE._serialized_end = 55712 + _DELETENEXUSOPERATIONEXECUTIONREQUEST._serialized_start = 55714 + _DELETENEXUSOPERATIONEXECUTIONREQUEST._serialized_end = 55809 + _DELETENEXUSOPERATIONEXECUTIONRESPONSE._serialized_start = 55811 + _DELETENEXUSOPERATIONEXECUTIONRESPONSE._serialized_end = 55850 + _POLLWORKFLOWEXECUTIONTIMESKIPPINGREQUEST._serialized_start = 55853 + _POLLWORKFLOWEXECUTIONTIMESKIPPINGREQUEST._serialized_end = 56010 + _POLLWORKFLOWEXECUTIONTIMESKIPPINGRESPONSE._serialized_start = 56013 + _POLLWORKFLOWEXECUTIONTIMESKIPPINGRESPONSE._serialized_end = 56245 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/workflowservice/v1/request_response_pb2.pyi b/temporalio/api/workflowservice/v1/request_response_pb2.pyi index 1e92d50e1..eb9243716 100644 --- a/temporalio/api/workflowservice/v1/request_response_pb2.pyi +++ b/temporalio/api/workflowservice/v1/request_response_pb2.pyi @@ -30,6 +30,7 @@ import temporalio.api.enums.v1.nexus_pb2 import temporalio.api.enums.v1.query_pb2 import temporalio.api.enums.v1.reset_pb2 import temporalio.api.enums.v1.task_queue_pb2 +import temporalio.api.enums.v1.time_skipping_pb2 import temporalio.api.enums.v1.update_pb2 import temporalio.api.enums.v1.workflow_pb2 import temporalio.api.failure.v1.message_pb2 @@ -305,6 +306,7 @@ class DescribeNamespaceResponse(google.protobuf.message.Message): IS_GLOBAL_NAMESPACE_FIELD_NUMBER: builtins.int FAILOVER_HISTORY_FIELD_NUMBER: builtins.int POLLER_GROUP_INFOS_FIELD_NUMBER: builtins.int + POLLER_GROUPS_INFO_FIELD_NUMBER: builtins.int @property def namespace_info( self, @@ -332,10 +334,20 @@ class DescribeNamespaceResponse(google.protobuf.message.Message): ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ temporalio.api.taskqueue.v1.message_pb2.PollerGroupInfo ]: - """The initial info that client should use for poller group assignment. This information is + """Deprecated. Use `poller_groups_info` instead, which carries a version so the client can + ignore stale updates. + The initial info that client should use for poller group assignment. This information is updated through poll response. Client is supposed to use the info received in the latest poll response. """ + @property + def poller_groups_info( + self, + ) -> temporalio.api.taskqueue.v1.message_pb2.PollerGroupsInfo: + """The initial, versioned info that client should use for poller group assignment. This + information is updated through poll responses. Client is supposed to use the info with the + highest version it has received. + """ def __init__( self, *, @@ -354,6 +366,8 @@ class DescribeNamespaceResponse(google.protobuf.message.Message): temporalio.api.taskqueue.v1.message_pb2.PollerGroupInfo ] | None = ..., + poller_groups_info: temporalio.api.taskqueue.v1.message_pb2.PollerGroupsInfo + | None = ..., ) -> None: ... def HasField( self, @@ -362,6 +376,8 @@ class DescribeNamespaceResponse(google.protobuf.message.Message): b"config", "namespace_info", b"namespace_info", + "poller_groups_info", + b"poller_groups_info", "replication_config", b"replication_config", ], @@ -381,6 +397,8 @@ class DescribeNamespaceResponse(google.protobuf.message.Message): b"namespace_info", "poller_group_infos", b"poller_group_infos", + "poller_groups_info", + b"poller_groups_info", "replication_config", b"replication_config", ], @@ -656,8 +674,8 @@ class StartWorkflowExecutionRequest(google.protobuf.message.Message): ) -> temporalio.api.common.v1.message_pb2.Payloads: ... @property def workflow_start_delay(self) -> google.protobuf.duration_pb2.Duration: - """Time to wait before dispatching the first workflow task. Cannot be used with `cron_schedule`. - If the workflow gets a signal before the delay, a workflow task will be dispatched and the rest + """Time to wait before making the first workflow task available for dispatch. Cannot be used with `cron_schedule`. + If the workflow gets a signal before the delay, a workflow task will be made available for dispatch and the rest of the delay will be ignored. """ @property @@ -710,7 +728,7 @@ class StartWorkflowExecutionRequest(google.protobuf.message.Message): @property def time_skipping_config( self, - ) -> temporalio.api.workflow.v1.message_pb2.TimeSkippingConfig: + ) -> temporalio.api.common.v1.message_pb2.TimeSkippingConfig: """Time-skipping configuration. If not set, time skipping is disabled.""" def __init__( self, @@ -753,7 +771,7 @@ class StartWorkflowExecutionRequest(google.protobuf.message.Message): priority: temporalio.api.common.v1.message_pb2.Priority | None = ..., eager_worker_deployment_options: temporalio.api.deployment.v1.message_pb2.WorkerDeploymentOptions | None = ..., - time_skipping_config: temporalio.api.workflow.v1.message_pb2.TimeSkippingConfig + time_skipping_config: temporalio.api.common.v1.message_pb2.TimeSkippingConfig | None = ..., ) -> None: ... def HasField( @@ -869,12 +887,15 @@ class StartWorkflowExecutionResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor RUN_ID_FIELD_NUMBER: builtins.int + FIRST_EXECUTION_RUN_ID_FIELD_NUMBER: builtins.int STARTED_FIELD_NUMBER: builtins.int STATUS_FIELD_NUMBER: builtins.int EAGER_WORKFLOW_TASK_FIELD_NUMBER: builtins.int LINK_FIELD_NUMBER: builtins.int run_id: builtins.str """The run id of the workflow that was started - or used (via WorkflowIdConflictPolicy USE_EXISTING).""" + first_execution_run_id: builtins.str + """If the workflow was started as a result of a de-dupe, this field will contain the run id of the first execution in the chain.""" started: builtins.bool """If true, a new workflow was started.""" status: temporalio.api.enums.v1.workflow_pb2.WorkflowExecutionStatus.ValueType @@ -894,6 +915,7 @@ class StartWorkflowExecutionResponse(google.protobuf.message.Message): self, *, run_id: builtins.str = ..., + first_execution_run_id: builtins.str = ..., started: builtins.bool = ..., status: temporalio.api.enums.v1.workflow_pb2.WorkflowExecutionStatus.ValueType = ..., eager_workflow_task: global___PollWorkflowTaskQueueResponse | None = ..., @@ -910,6 +932,8 @@ class StartWorkflowExecutionResponse(google.protobuf.message.Message): field_name: typing_extensions.Literal[ "eager_workflow_task", b"eager_workflow_task", + "first_execution_run_id", + b"first_execution_run_id", "link", b"link", "run_id", @@ -1253,6 +1277,7 @@ class PollWorkflowTaskQueueResponse(google.protobuf.message.Message): POLLER_SCALING_DECISION_FIELD_NUMBER: builtins.int POLLER_GROUP_ID_FIELD_NUMBER: builtins.int POLLER_GROUP_INFOS_FIELD_NUMBER: builtins.int + POLLER_GROUPS_INFO_FIELD_NUMBER: builtins.int task_token: builtins.bytes """A unique identifier for this task""" @property @@ -1346,13 +1371,27 @@ class PollWorkflowTaskQueueResponse(google.protobuf.message.Message): ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ temporalio.api.taskqueue.v1.message_pb2.PollerGroupInfo ]: - """The weighted list of poller groups IDs that client should use for future polls to this task + """Deprecated. Use `poller_groups_info` instead, which carries a version so the client can + ignore stale updates. + The weighted list of poller groups IDs that client should use for future polls to this task queue. Client is expected to: 1. Maintain minimum number of pollers no less than the number of groups. 2. Try to assign the next poll to a group without any pending polls, 3. If every group has some pending polls, assign the next poll to a group randomly according to the weights. """ + @property + def poller_groups_info( + self, + ) -> temporalio.api.taskqueue.v1.message_pb2.PollerGroupsInfo: + """The weighted, versioned list of poller groups IDs that client should use for future polls to + this task queue. Client should ignore this if it has already applied a snapshot with a + version greater than or equal to `poller_groups_info.version`. Client is expected to: + 1. Maintain minimum number of pollers no less than the number of groups. + 2. Try to assign the next poll to a group without any pending polls, + 3. If every group has some pending polls, assign the next poll to a group randomly + according to the weights. + """ def __init__( self, *, @@ -1386,12 +1425,16 @@ class PollWorkflowTaskQueueResponse(google.protobuf.message.Message): temporalio.api.taskqueue.v1.message_pb2.PollerGroupInfo ] | None = ..., + poller_groups_info: temporalio.api.taskqueue.v1.message_pb2.PollerGroupsInfo + | None = ..., ) -> None: ... def HasField( self, field_name: typing_extensions.Literal[ "history", b"history", + "poller_groups_info", + b"poller_groups_info", "poller_scaling_decision", b"poller_scaling_decision", "query", @@ -1425,6 +1468,8 @@ class PollWorkflowTaskQueueResponse(google.protobuf.message.Message): b"poller_group_id", "poller_group_infos", b"poller_group_infos", + "poller_groups_info", + b"poller_groups_info", "poller_scaling_decision", b"poller_scaling_decision", "previous_started_event_id", @@ -1524,6 +1569,8 @@ class RespondWorkflowTaskCompletedRequest(google.protobuf.message.Message): DEPLOYMENT_OPTIONS_FIELD_NUMBER: builtins.int WORKER_INSTANCE_KEY_FIELD_NUMBER: builtins.int WORKER_CONTROL_TASK_QUEUE_FIELD_NUMBER: builtins.int + PAGE_NUMBER_FIELD_NUMBER: builtins.int + INTERMEDIATE_PAGE_FIELD_NUMBER: builtins.int task_token: builtins.bytes """The task token as received in `PollWorkflowTaskQueueResponse`""" @property @@ -1622,6 +1669,16 @@ class RespondWorkflowTaskCompletedRequest(google.protobuf.message.Message): """A dedicated per-worker Nexus task queue on which the server sends control tasks (e.g. activity cancellation) to this specific worker instance. """ + page_number: builtins.int + """0-indexed page number when the workflow task completion is split across multiple + requests ("pages"). 0 for single-page requests. May only be set to non-zero value + when the namespace capability workflow_task_completion_pagination is true. + """ + intermediate_page: builtins.bool + """True for non-final pages of a paginated workflow task completion. The final page's + `page_number` tells the server how many intermediate pages (0..page_number-1) preceded it. + May only be used when the namespace capability workflow_task_completion_pagination is true. + """ def __init__( self, *, @@ -1660,6 +1717,8 @@ class RespondWorkflowTaskCompletedRequest(google.protobuf.message.Message): | None = ..., worker_instance_key: builtins.str = ..., worker_control_task_queue: builtins.str = ..., + page_number: builtins.int = ..., + intermediate_page: builtins.bool = ..., ) -> None: ... def HasField( self, @@ -1697,12 +1756,16 @@ class RespondWorkflowTaskCompletedRequest(google.protobuf.message.Message): b"force_create_new_workflow_task", "identity", b"identity", + "intermediate_page", + b"intermediate_page", "messages", b"messages", "metering_metadata", b"metering_metadata", "namespace", b"namespace", + "page_number", + b"page_number", "query_results", b"query_results", "resource_id", @@ -2030,6 +2093,7 @@ class PollActivityTaskQueueResponse(google.protobuf.message.Message): PRIORITY_FIELD_NUMBER: builtins.int ACTIVITY_RUN_ID_FIELD_NUMBER: builtins.int POLLER_GROUP_INFOS_FIELD_NUMBER: builtins.int + POLLER_GROUPS_INFO_FIELD_NUMBER: builtins.int task_token: builtins.bytes """A unique identifier for this task""" workflow_namespace: builtins.str @@ -2123,6 +2187,18 @@ class PollActivityTaskQueueResponse(google.protobuf.message.Message): 3. If every group has some pending polls, assign the next poll to a group randomly according to the weights. """ + @property + def poller_groups_info( + self, + ) -> temporalio.api.taskqueue.v1.message_pb2.PollerGroupsInfo: + """The weighted, versioned list of poller groups IDs that client should use for future polls to + this task queue. Client should ignore this if it has already applied a snapshot with a + version greater than or equal to `poller_groups_info.version`. Client is expected to: + 1. Maintain minimum number of pollers no less than the number of groups. + 2. Try to assign the next poll to a group without any pending polls, + 3. If every group has some pending polls, assign the next poll to a group randomly + according to the weights. + """ def __init__( self, *, @@ -2153,6 +2229,8 @@ class PollActivityTaskQueueResponse(google.protobuf.message.Message): temporalio.api.taskqueue.v1.message_pb2.PollerGroupInfo ] | None = ..., + poller_groups_info: temporalio.api.taskqueue.v1.message_pb2.PollerGroupsInfo + | None = ..., ) -> None: ... def HasField( self, @@ -2169,6 +2247,8 @@ class PollActivityTaskQueueResponse(google.protobuf.message.Message): b"heartbeat_timeout", "input", b"input", + "poller_groups_info", + b"poller_groups_info", "poller_scaling_decision", b"poller_scaling_decision", "priority", @@ -2212,6 +2292,8 @@ class PollActivityTaskQueueResponse(google.protobuf.message.Message): b"input", "poller_group_infos", b"poller_group_infos", + "poller_groups_info", + b"poller_groups_info", "poller_scaling_decision", b"poller_scaling_decision", "priority", @@ -3310,9 +3392,9 @@ class SignalWithStartWorkflowExecutionRequest(google.protobuf.message.Message): def header(self) -> temporalio.api.common.v1.message_pb2.Header: ... @property def workflow_start_delay(self) -> google.protobuf.duration_pb2.Duration: - """Time to wait before dispatching the first workflow task. Cannot be used with `cron_schedule`. + """Time to wait before making the first workflow task available for dispatch. Cannot be used with `cron_schedule`. Note that the signal will be delivered with the first workflow task. If the workflow gets - another SignalWithStartWorkflow before the delay a workflow task will be dispatched immediately + another SignalWithStartWorkflow before the delay a workflow task will be made available for dispatch immediately and the rest of the delay period will be ignored, even if that request also had a delay. Signal via SignalWorkflowExecution will not unblock the workflow. """ @@ -3342,7 +3424,7 @@ class SignalWithStartWorkflowExecutionRequest(google.protobuf.message.Message): @property def time_skipping_config( self, - ) -> temporalio.api.workflow.v1.message_pb2.TimeSkippingConfig: + ) -> temporalio.api.common.v1.message_pb2.TimeSkippingConfig: """Time-skipping configuration. If not set, time skipping is disabled.""" def __init__( self, @@ -3376,7 +3458,7 @@ class SignalWithStartWorkflowExecutionRequest(google.protobuf.message.Message): versioning_override: temporalio.api.workflow.v1.message_pb2.VersioningOverride | None = ..., priority: temporalio.api.common.v1.message_pb2.Priority | None = ..., - time_skipping_config: temporalio.api.workflow.v1.message_pb2.TimeSkippingConfig + time_skipping_config: temporalio.api.common.v1.message_pb2.TimeSkippingConfig | None = ..., ) -> None: ... def HasField( @@ -3482,10 +3564,13 @@ class SignalWithStartWorkflowExecutionResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor RUN_ID_FIELD_NUMBER: builtins.int + FIRST_EXECUTION_RUN_ID_FIELD_NUMBER: builtins.int STARTED_FIELD_NUMBER: builtins.int SIGNAL_LINK_FIELD_NUMBER: builtins.int run_id: builtins.str """The run id of the workflow that was started - or just signaled, if it was already running.""" + first_execution_run_id: builtins.str + """If the workflow was started as a result of a de-dupe, this field will contain the run id of the first execution in the chain.""" started: builtins.bool """If true, a new workflow was started.""" @property @@ -3498,6 +3583,7 @@ class SignalWithStartWorkflowExecutionResponse(google.protobuf.message.Message): self, *, run_id: builtins.str = ..., + first_execution_run_id: builtins.str = ..., started: builtins.bool = ..., signal_link: temporalio.api.common.v1.message_pb2.Link | None = ..., ) -> None: ... @@ -3507,7 +3593,14 @@ class SignalWithStartWorkflowExecutionResponse(google.protobuf.message.Message): def ClearField( self, field_name: typing_extensions.Literal[ - "run_id", b"run_id", "signal_link", b"signal_link", "started", b"started" + "first_execution_run_id", + b"first_execution_run_id", + "run_id", + b"run_id", + "signal_link", + b"signal_link", + "started", + b"started", ], ) -> None: ... @@ -4594,26 +4687,41 @@ class QueryWorkflowResponse(google.protobuf.message.Message): QUERY_RESULT_FIELD_NUMBER: builtins.int QUERY_REJECTED_FIELD_NUMBER: builtins.int + LINK_FIELD_NUMBER: builtins.int @property def query_result(self) -> temporalio.api.common.v1.message_pb2.Payloads: ... @property def query_rejected(self) -> temporalio.api.query.v1.message_pb2.QueryRejected: ... + @property + def link(self) -> temporalio.api.common.v1.message_pb2.Link: + """Holds the link to the Workflow execution that processed the Query.""" def __init__( self, *, query_result: temporalio.api.common.v1.message_pb2.Payloads | None = ..., query_rejected: temporalio.api.query.v1.message_pb2.QueryRejected | None = ..., + link: temporalio.api.common.v1.message_pb2.Link | None = ..., ) -> None: ... def HasField( self, field_name: typing_extensions.Literal[ - "query_rejected", b"query_rejected", "query_result", b"query_result" + "link", + b"link", + "query_rejected", + b"query_rejected", + "query_result", + b"query_result", ], ) -> builtins.bool: ... def ClearField( self, field_name: typing_extensions.Literal[ - "query_rejected", b"query_rejected", "query_result", b"query_result" + "link", + b"link", + "query_rejected", + b"query_rejected", + "query_result", + b"query_result", ], ) -> None: ... @@ -5229,6 +5337,7 @@ class GetSystemInfoResponse(google.protobuf.message.Message): COUNT_GROUP_BY_EXECUTION_STATUS_FIELD_NUMBER: builtins.int NEXUS_FIELD_NUMBER: builtins.int SERVER_SCALED_DEPLOYMENTS_FIELD_NUMBER: builtins.int + SERVER_SCALED_PROVIDER_CLOUD_RUN_FIELD_NUMBER: builtins.int signal_and_query_header: builtins.bool """True if signal and query headers are supported.""" internal_error_differentiation: builtins.bool @@ -5270,6 +5379,11 @@ class GetSystemInfoResponse(google.protobuf.message.Message): This flag is dependent both on server version and for server-scaled deployments to be enabled via server configuration. """ + server_scaled_provider_cloud_run: builtins.bool + """True if the server supports the Cloud Run compute provider for + server-scaled deployments. Dependent on server version and the + provider being enabled via server configuration. + """ def __init__( self, *, @@ -5285,6 +5399,7 @@ class GetSystemInfoResponse(google.protobuf.message.Message): count_group_by_execution_status: builtins.bool = ..., nexus: builtins.bool = ..., server_scaled_deployments: builtins.bool = ..., + server_scaled_provider_cloud_run: builtins.bool = ..., ) -> None: ... def ClearField( self, @@ -5307,6 +5422,8 @@ class GetSystemInfoResponse(google.protobuf.message.Message): b"sdk_metadata", "server_scaled_deployments", b"server_scaled_deployments", + "server_scaled_provider_cloud_run", + b"server_scaled_provider_cloud_run", "signal_and_query_header", b"signal_and_query_header", "supports_schedules", @@ -7032,6 +7149,7 @@ class StartBatchOperationRequest(google.protobuf.message.Message): JOB_ID_FIELD_NUMBER: builtins.int REASON_FIELD_NUMBER: builtins.int EXECUTIONS_FIELD_NUMBER: builtins.int + TARGET_EXECUTIONS_FIELD_NUMBER: builtins.int MAX_OPERATIONS_PER_SECOND_FIELD_NUMBER: builtins.int TERMINATION_OPERATION_FIELD_NUMBER: builtins.int SIGNAL_OPERATION_FIELD_NUMBER: builtins.int @@ -7042,6 +7160,9 @@ class StartBatchOperationRequest(google.protobuf.message.Message): UNPAUSE_ACTIVITIES_OPERATION_FIELD_NUMBER: builtins.int RESET_ACTIVITIES_OPERATION_FIELD_NUMBER: builtins.int UPDATE_ACTIVITY_OPTIONS_OPERATION_FIELD_NUMBER: builtins.int + CANCEL_ACTIVITIES_OPERATION_FIELD_NUMBER: builtins.int + TERMINATE_ACTIVITIES_OPERATION_FIELD_NUMBER: builtins.int + DELETE_ACTIVITIES_OPERATION_FIELD_NUMBER: builtins.int namespace: builtins.str """Namespace that contains the batch operation""" visibility_query: builtins.str @@ -7060,6 +7181,16 @@ class StartBatchOperationRequest(google.protobuf.message.Message): ]: """Executions to apply the batch operation This field and `visibility_query` are mutually exclusive + DEPRECATED: Use `target_executions` instead. + """ + @property + def target_executions( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ + temporalio.api.common.v1.message_pb2.Execution + ]: + """Target executions to apply the batch operation. This field and `visibility_query` + are mutually exclusive. """ max_operations_per_second: builtins.float """Limit for the number of operations processed per second within this batch. @@ -7107,6 +7238,18 @@ class StartBatchOperationRequest(google.protobuf.message.Message): def update_activity_options_operation( self, ) -> temporalio.api.batch.v1.message_pb2.BatchOperationUpdateActivityOptions: ... + @property + def cancel_activities_operation( + self, + ) -> temporalio.api.batch.v1.message_pb2.BatchOperationCancelActivities: ... + @property + def terminate_activities_operation( + self, + ) -> temporalio.api.batch.v1.message_pb2.BatchOperationTerminateActivities: ... + @property + def delete_activities_operation( + self, + ) -> temporalio.api.batch.v1.message_pb2.BatchOperationDeleteActivities: ... def __init__( self, *, @@ -7118,6 +7261,10 @@ class StartBatchOperationRequest(google.protobuf.message.Message): temporalio.api.common.v1.message_pb2.WorkflowExecution ] | None = ..., + target_executions: collections.abc.Iterable[ + temporalio.api.common.v1.message_pb2.Execution + ] + | None = ..., max_operations_per_second: builtins.float = ..., termination_operation: temporalio.api.batch.v1.message_pb2.BatchOperationTermination | None = ..., @@ -7137,12 +7284,22 @@ class StartBatchOperationRequest(google.protobuf.message.Message): | None = ..., update_activity_options_operation: temporalio.api.batch.v1.message_pb2.BatchOperationUpdateActivityOptions | None = ..., + cancel_activities_operation: temporalio.api.batch.v1.message_pb2.BatchOperationCancelActivities + | None = ..., + terminate_activities_operation: temporalio.api.batch.v1.message_pb2.BatchOperationTerminateActivities + | None = ..., + delete_activities_operation: temporalio.api.batch.v1.message_pb2.BatchOperationDeleteActivities + | None = ..., ) -> None: ... def HasField( self, field_name: typing_extensions.Literal[ + "cancel_activities_operation", + b"cancel_activities_operation", "cancellation_operation", b"cancellation_operation", + "delete_activities_operation", + b"delete_activities_operation", "deletion_operation", b"deletion_operation", "operation", @@ -7153,6 +7310,8 @@ class StartBatchOperationRequest(google.protobuf.message.Message): b"reset_operation", "signal_operation", b"signal_operation", + "terminate_activities_operation", + b"terminate_activities_operation", "termination_operation", b"termination_operation", "unpause_activities_operation", @@ -7166,8 +7325,12 @@ class StartBatchOperationRequest(google.protobuf.message.Message): def ClearField( self, field_name: typing_extensions.Literal[ + "cancel_activities_operation", + b"cancel_activities_operation", "cancellation_operation", b"cancellation_operation", + "delete_activities_operation", + b"delete_activities_operation", "deletion_operation", b"deletion_operation", "executions", @@ -7188,6 +7351,10 @@ class StartBatchOperationRequest(google.protobuf.message.Message): b"reset_operation", "signal_operation", b"signal_operation", + "target_executions", + b"target_executions", + "terminate_activities_operation", + b"terminate_activities_operation", "termination_operation", b"termination_operation", "unpause_activities_operation", @@ -7213,6 +7380,9 @@ class StartBatchOperationRequest(google.protobuf.message.Message): "unpause_activities_operation", "reset_activities_operation", "update_activity_options_operation", + "cancel_activities_operation", + "terminate_activities_operation", + "delete_activities_operation", ] | None ): ... @@ -7313,6 +7483,8 @@ class DescribeBatchOperationResponse(google.protobuf.message.Message): FAILURE_OPERATION_COUNT_FIELD_NUMBER: builtins.int IDENTITY_FIELD_NUMBER: builtins.int REASON_FIELD_NUMBER: builtins.int + QUERY_FIELD_NUMBER: builtins.int + EXECUTIONS_FIELD_NUMBER: builtins.int operation_type: ( temporalio.api.enums.v1.batch_operation_pb2.BatchOperationType.ValueType ) @@ -7337,6 +7509,15 @@ class DescribeBatchOperationResponse(google.protobuf.message.Message): """Identity indicates the operator identity""" reason: builtins.str """Reason indicates the reason to stop a operation""" + query: builtins.str + """Query is the visibility query that defines the group of workflow to apply the batch operation""" + @property + def executions( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ + temporalio.api.common.v1.message_pb2.Execution + ]: + """Executions is the list of workflow OR standalone activity executions to apply the batch operation""" def __init__( self, *, @@ -7350,6 +7531,11 @@ class DescribeBatchOperationResponse(google.protobuf.message.Message): failure_operation_count: builtins.int = ..., identity: builtins.str = ..., reason: builtins.str = ..., + query: builtins.str = ..., + executions: collections.abc.Iterable[ + temporalio.api.common.v1.message_pb2.Execution + ] + | None = ..., ) -> None: ... def HasField( self, @@ -7364,6 +7550,8 @@ class DescribeBatchOperationResponse(google.protobuf.message.Message): b"close_time", "complete_operation_count", b"complete_operation_count", + "executions", + b"executions", "failure_operation_count", b"failure_operation_count", "identity", @@ -7372,6 +7560,8 @@ class DescribeBatchOperationResponse(google.protobuf.message.Message): b"job_id", "operation_type", b"operation_type", + "query", + b"query", "reason", b"reason", "start_time", @@ -7657,6 +7847,7 @@ class PollNexusTaskQueueResponse(google.protobuf.message.Message): POLLER_SCALING_DECISION_FIELD_NUMBER: builtins.int POLLER_GROUP_ID_FIELD_NUMBER: builtins.int POLLER_GROUP_INFOS_FIELD_NUMBER: builtins.int + POLLER_GROUPS_INFO_FIELD_NUMBER: builtins.int task_token: builtins.bytes """An opaque unique identifier for this task for correlating a completion request the embedded request.""" @property @@ -7686,6 +7877,18 @@ class PollNexusTaskQueueResponse(google.protobuf.message.Message): 3. If every group has some pending polls, assign the next poll to a group randomly according to the weights. """ + @property + def poller_groups_info( + self, + ) -> temporalio.api.taskqueue.v1.message_pb2.PollerGroupsInfo: + """The weighted, versioned list of poller groups IDs that client should use for future polls to + this task queue. Client should ignore this if it has already applied a snapshot with a + version greater than or equal to `poller_groups_info.version`. Client is expected to: + 1. Maintain minimum number of pollers no less than the number of groups. + 2. Try to assign the next poll to a group without any pending polls, + 3. If every group has some pending polls, assign the next poll to a group randomly + according to the weights. + """ def __init__( self, *, @@ -7698,11 +7901,18 @@ class PollNexusTaskQueueResponse(google.protobuf.message.Message): temporalio.api.taskqueue.v1.message_pb2.PollerGroupInfo ] | None = ..., + poller_groups_info: temporalio.api.taskqueue.v1.message_pb2.PollerGroupsInfo + | None = ..., ) -> None: ... def HasField( self, field_name: typing_extensions.Literal[ - "poller_scaling_decision", b"poller_scaling_decision", "request", b"request" + "poller_groups_info", + b"poller_groups_info", + "poller_scaling_decision", + b"poller_scaling_decision", + "request", + b"request", ], ) -> builtins.bool: ... def ClearField( @@ -7712,6 +7922,8 @@ class PollNexusTaskQueueResponse(google.protobuf.message.Message): b"poller_group_id", "poller_group_infos", b"poller_group_infos", + "poller_groups_info", + b"poller_groups_info", "poller_scaling_decision", b"poller_scaling_decision", "request", @@ -8140,6 +8352,7 @@ class UpdateActivityExecutionOptionsRequest(google.protobuf.message.Message): UPDATE_MASK_FIELD_NUMBER: builtins.int RESTORE_ORIGINAL_FIELD_NUMBER: builtins.int RESOURCE_ID_FIELD_NUMBER: builtins.int + REQUEST_ID_FIELD_NUMBER: builtins.int namespace: builtins.str """Namespace of the workflow which scheduled this activity""" workflow_id: builtins.str @@ -8149,7 +8362,7 @@ class UpdateActivityExecutionOptionsRequest(google.protobuf.message.Message): activity_id: builtins.str """The ID of the activity to target.""" run_id: builtins.str - """Run ID of the workflow or standalone activity.""" + """Run ID of the workflow or standalone activity. If empty, targets the latest run.""" identity: builtins.str """The identity of the client who initiated this request""" @property @@ -8169,6 +8382,8 @@ class UpdateActivityExecutionOptionsRequest(google.protobuf.message.Message): """ resource_id: builtins.str """Resource ID for routing. Contains "workflow:{workflow_id}" for workflow activities or "activity:{activity_id}" for standalone activities.""" + request_id: builtins.str + """Used to de-dupe update requests.""" def __init__( self, *, @@ -8182,6 +8397,7 @@ class UpdateActivityExecutionOptionsRequest(google.protobuf.message.Message): update_mask: google.protobuf.field_mask_pb2.FieldMask | None = ..., restore_original: builtins.bool = ..., resource_id: builtins.str = ..., + request_id: builtins.str = ..., ) -> None: ... def HasField( self, @@ -8200,6 +8416,8 @@ class UpdateActivityExecutionOptionsRequest(google.protobuf.message.Message): b"identity", "namespace", b"namespace", + "request_id", + b"request_id", "resource_id", b"resource_id", "restore_original", @@ -8369,7 +8587,7 @@ class PauseActivityExecutionRequest(google.protobuf.message.Message): activity_id: builtins.str """The ID of the activity to target.""" run_id: builtins.str - """Run ID of the workflow or standalone activity.""" + """Run ID of the workflow or standalone activity. If empty, targets the latest run.""" identity: builtins.str """The identity of the client who initiated this request.""" reason: builtins.str @@ -8537,11 +8755,10 @@ class UnpauseActivityExecutionRequest(google.protobuf.message.Message): ACTIVITY_ID_FIELD_NUMBER: builtins.int RUN_ID_FIELD_NUMBER: builtins.int IDENTITY_FIELD_NUMBER: builtins.int - RESET_ATTEMPTS_FIELD_NUMBER: builtins.int - RESET_HEARTBEAT_FIELD_NUMBER: builtins.int REASON_FIELD_NUMBER: builtins.int JITTER_FIELD_NUMBER: builtins.int RESOURCE_ID_FIELD_NUMBER: builtins.int + REQUEST_ID_FIELD_NUMBER: builtins.int namespace: builtins.str """Namespace of the workflow which scheduled this activity.""" workflow_id: builtins.str @@ -8551,13 +8768,9 @@ class UnpauseActivityExecutionRequest(google.protobuf.message.Message): activity_id: builtins.str """The ID of the activity to target.""" run_id: builtins.str - """Run ID of the workflow or standalone activity.""" + """Run ID of the workflow or standalone activity. If empty, targets the latest run.""" identity: builtins.str """The identity of the client who initiated this request.""" - reset_attempts: builtins.bool - """Providing this flag will also reset the number of attempts.""" - reset_heartbeat: builtins.bool - """Providing this flag will also reset the heartbeat details.""" reason: builtins.str """Reason to unpause the activity.""" @property @@ -8565,6 +8778,8 @@ class UnpauseActivityExecutionRequest(google.protobuf.message.Message): """If set, the activity will start at a random time within the specified jitter duration.""" resource_id: builtins.str """Resource ID for routing. Contains "workflow:{workflow_id}" for workflow activities or "activity:{activity_id}" for standalone activities.""" + request_id: builtins.str + """Used to de-dupe unpause requests.""" def __init__( self, *, @@ -8573,11 +8788,10 @@ class UnpauseActivityExecutionRequest(google.protobuf.message.Message): activity_id: builtins.str = ..., run_id: builtins.str = ..., identity: builtins.str = ..., - reset_attempts: builtins.bool = ..., - reset_heartbeat: builtins.bool = ..., reason: builtins.str = ..., jitter: google.protobuf.duration_pb2.Duration | None = ..., resource_id: builtins.str = ..., + request_id: builtins.str = ..., ) -> None: ... def HasField( self, field_name: typing_extensions.Literal["jitter", b"jitter"] @@ -8595,10 +8809,8 @@ class UnpauseActivityExecutionRequest(google.protobuf.message.Message): b"namespace", "reason", b"reason", - "reset_attempts", - b"reset_attempts", - "reset_heartbeat", - b"reset_heartbeat", + "request_id", + b"request_id", "resource_id", b"resource_id", "run_id", @@ -8748,11 +8960,12 @@ class ResetActivityExecutionRequest(google.protobuf.message.Message): ACTIVITY_ID_FIELD_NUMBER: builtins.int RUN_ID_FIELD_NUMBER: builtins.int IDENTITY_FIELD_NUMBER: builtins.int - RESET_HEARTBEAT_FIELD_NUMBER: builtins.int KEEP_PAUSED_FIELD_NUMBER: builtins.int JITTER_FIELD_NUMBER: builtins.int RESTORE_ORIGINAL_OPTIONS_FIELD_NUMBER: builtins.int RESOURCE_ID_FIELD_NUMBER: builtins.int + REQUEST_ID_FIELD_NUMBER: builtins.int + RESET_HEARTBEAT_FIELD_NUMBER: builtins.int namespace: builtins.str """Namespace of the workflow which scheduled this activity.""" workflow_id: builtins.str @@ -8762,13 +8975,9 @@ class ResetActivityExecutionRequest(google.protobuf.message.Message): activity_id: builtins.str """The ID of the activity to target.""" run_id: builtins.str - """Run ID of the workflow or standalone activity.""" + """Run ID of the workflow or standalone activity. If empty, targets the latest run.""" identity: builtins.str """The identity of the client who initiated this request.""" - reset_heartbeat: builtins.bool - """Indicates that activity should reset heartbeat details. - This flag will be applied only to the new instance of the activity. - """ keep_paused: builtins.bool """If activity is paused, it will remain paused after reset""" @property @@ -8783,6 +8992,13 @@ class ResetActivityExecutionRequest(google.protobuf.message.Message): """ resource_id: builtins.str """Resource ID for routing. Contains "workflow:{workflow_id}" for workflow activities or "activity:{activity_id}" for standalone activities.""" + request_id: builtins.str + """Used to de-dupe reset requests.""" + reset_heartbeat: builtins.bool + """Reset persisted heartbeat details. + Reset always resets the attempt counter. Passing this flag causes reset to additionally + discard any persisted heartbeat details. + """ def __init__( self, *, @@ -8791,11 +9007,12 @@ class ResetActivityExecutionRequest(google.protobuf.message.Message): activity_id: builtins.str = ..., run_id: builtins.str = ..., identity: builtins.str = ..., - reset_heartbeat: builtins.bool = ..., keep_paused: builtins.bool = ..., jitter: google.protobuf.duration_pb2.Duration | None = ..., restore_original_options: builtins.bool = ..., resource_id: builtins.str = ..., + request_id: builtins.str = ..., + reset_heartbeat: builtins.bool = ..., ) -> None: ... def HasField( self, field_name: typing_extensions.Literal["jitter", b"jitter"] @@ -8813,6 +9030,8 @@ class ResetActivityExecutionRequest(google.protobuf.message.Message): b"keep_paused", "namespace", b"namespace", + "request_id", + b"request_id", "reset_heartbeat", b"reset_heartbeat", "resource_id", @@ -8927,27 +9146,43 @@ class UpdateWorkflowExecutionOptionsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor WORKFLOW_EXECUTION_OPTIONS_FIELD_NUMBER: builtins.int + UPDATE_TIME_FIELD_NUMBER: builtins.int @property def workflow_execution_options( self, ) -> temporalio.api.workflow.v1.message_pb2.WorkflowExecutionOptions: """Workflow Execution options after update.""" + @property + def update_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """The Workflow Execution time when the options were updated. When time skipping is + enabled, this is the workflow's virtual time rather than wall-clock time. + + This timestamp cannot be used for time-skipping fast-forward verification, + use `fast_forward_id` in `PollWorkflowExecutionTimeSkippingRequest` instead. + """ def __init__( self, *, workflow_execution_options: temporalio.api.workflow.v1.message_pb2.WorkflowExecutionOptions | None = ..., + update_time: google.protobuf.timestamp_pb2.Timestamp | None = ..., ) -> None: ... def HasField( self, field_name: typing_extensions.Literal[ - "workflow_execution_options", b"workflow_execution_options" + "update_time", + b"update_time", + "workflow_execution_options", + b"workflow_execution_options", ], ) -> builtins.bool: ... def ClearField( self, field_name: typing_extensions.Literal[ - "workflow_execution_options", b"workflow_execution_options" + "update_time", + b"update_time", + "workflow_execution_options", + b"workflow_execution_options", ], ) -> None: ... @@ -11545,6 +11780,59 @@ class DescribeWorkerResponse(google.protobuf.message.Message): global___DescribeWorkerResponse = DescribeWorkerResponse +class CountWorkersRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAMESPACE_FIELD_NUMBER: builtins.int + QUERY_FIELD_NUMBER: builtins.int + INCLUDE_SYSTEM_WORKERS_FIELD_NUMBER: builtins.int + namespace: builtins.str + query: builtins.str + """Query to filter workers before counting. + Supported filter fields are the same as in ListWorkersRequest. + """ + include_system_workers: builtins.bool + """When true, the count will include system workers that are created implicitly + by the server and not by the user. By default, system workers are excluded. + """ + def __init__( + self, + *, + namespace: builtins.str = ..., + query: builtins.str = ..., + include_system_workers: builtins.bool = ..., + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "include_system_workers", + b"include_system_workers", + "namespace", + b"namespace", + "query", + b"query", + ], + ) -> None: ... + +global___CountWorkersRequest = CountWorkersRequest + +class CountWorkersResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + COUNT_FIELD_NUMBER: builtins.int + count: builtins.int + """Number of workers matching the query.""" + def __init__( + self, + *, + count: builtins.int = ..., + ) -> None: ... + def ClearField( + self, field_name: typing_extensions.Literal["count", b"count"] + ) -> None: ... + +global___CountWorkersResponse = CountWorkersResponse + class PauseWorkflowExecutionRequest(google.protobuf.message.Message): """Request to pause a workflow execution.""" @@ -11798,7 +12086,7 @@ class StartActivityExecutionRequest(google.protobuf.message.Message): """Options for handling conflicts when using ACTIVITY_ID_CONFLICT_POLICY_USE_EXISTING.""" @property def start_delay(self) -> google.protobuf.duration_pb2.Duration: - """Time to wait before dispatching the first activity task. This delay is not applied to retry attempts.""" + """Time to wait before making the first activity task available for dispatch. This delay is not applied to retry attempts.""" def __init__( self, *, @@ -12942,7 +13230,7 @@ class RequestCancelActivityExecutionRequest(google.protobuf.message.Message): namespace: builtins.str activity_id: builtins.str run_id: builtins.str - """Activity run ID, targets the latest run if run_id is empty.""" + """Activity run ID. If empty, targets the latest run.""" identity: builtins.str """The identity of the worker/client.""" request_id: builtins.str @@ -13002,7 +13290,7 @@ class TerminateActivityExecutionRequest(google.protobuf.message.Message): namespace: builtins.str activity_id: builtins.str run_id: builtins.str - """Activity run ID, targets the latest run if run_id is empty.""" + """Activity run ID. If empty, targets the latest run.""" identity: builtins.str """The identity of the worker/client.""" request_id: builtins.str @@ -13251,3 +13539,103 @@ class DeleteNexusOperationExecutionResponse(google.protobuf.message.Message): ) -> None: ... global___DeleteNexusOperationExecutionResponse = DeleteNexusOperationExecutionResponse + +class PollWorkflowExecutionTimeSkippingRequest(google.protobuf.message.Message): + """A long-poll request that blocks according to a time-skipping waiting policy on the workflow + execution. Currently the only supported policy is waiting for completion of the fast-forward + identified by `fast_forward_id`; the poll also returns once anything else settles that outcome + (e.g. the execution ends or time skipping is disabled). + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAMESPACE_FIELD_NUMBER: builtins.int + WORKFLOW_EXECUTION_FIELD_NUMBER: builtins.int + FAST_FORWARD_ID_FIELD_NUMBER: builtins.int + namespace: builtins.str + @property + def workflow_execution( + self, + ) -> temporalio.api.common.v1.message_pb2.WorkflowExecution: ... + fast_forward_id: builtins.str + """Required. Identifies the fast-forward whose completion the caller wants to wait for. + Must match the `fast_forward_id` set in the execution's TimeSkippingConfig. + """ + def __init__( + self, + *, + namespace: builtins.str = ..., + workflow_execution: temporalio.api.common.v1.message_pb2.WorkflowExecution + | None = ..., + fast_forward_id: builtins.str = ..., + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "workflow_execution", b"workflow_execution" + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "fast_forward_id", + b"fast_forward_id", + "namespace", + b"namespace", + "workflow_execution", + b"workflow_execution", + ], + ) -> None: ... + +global___PollWorkflowExecutionTimeSkippingRequest = ( + PollWorkflowExecutionTimeSkippingRequest +) + +class PollWorkflowExecutionTimeSkippingResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + FAST_FORWARD_POLLING_RESULT_FIELD_NUMBER: builtins.int + FAILED_REASON_FIELD_NUMBER: builtins.int + FAST_FORWARD_INFO_FIELD_NUMBER: builtins.int + fast_forward_polling_result: ( + temporalio.api.enums.v1.time_skipping_pb2.FastForwardPollingResult.ValueType + ) + """The outcome of the poll for the fast-forward identified by the request's `fast_forward_id`.""" + failed_reason: builtins.str + """Set only when the result is FAST_FORWARD_POLLING_RESULT_FAST_FORWARD_FAILED; explains why + the fast-forward can no longer complete. + """ + @property + def fast_forward_info( + self, + ) -> temporalio.api.common.v1.message_pb2.TimeSkippingFastForwardInfo: + """The execution's current fast-forward, if any.""" + def __init__( + self, + *, + fast_forward_polling_result: temporalio.api.enums.v1.time_skipping_pb2.FastForwardPollingResult.ValueType = ..., + failed_reason: builtins.str = ..., + fast_forward_info: temporalio.api.common.v1.message_pb2.TimeSkippingFastForwardInfo + | None = ..., + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "fast_forward_info", b"fast_forward_info" + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "failed_reason", + b"failed_reason", + "fast_forward_info", + b"fast_forward_info", + "fast_forward_polling_result", + b"fast_forward_polling_result", + ], + ) -> None: ... + +global___PollWorkflowExecutionTimeSkippingResponse = ( + PollWorkflowExecutionTimeSkippingResponse +) diff --git a/temporalio/api/workflowservice/v1/service_pb2.py b/temporalio/api/workflowservice/v1/service_pb2.py index bc9ca40a4..92da5d6c2 100644 --- a/temporalio/api/workflowservice/v1/service_pb2.py +++ b/temporalio/api/workflowservice/v1/service_pb2.py @@ -27,7 +27,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n-temporal/api/workflowservice/v1/service.proto\x12\x1ftemporal.api.workflowservice.v1\x1a\x1cgoogle/api/annotations.proto\x1a!nexusannotations/v1/options.proto\x1a+temporal/api/protometa/v1/annotations.proto\x1a\x36temporal/api/workflowservice/v1/request_response.proto2\xbd\xad\x02\n\x0fWorkflowService\x12\xc3\x01\n\x11RegisterNamespace\x12\x39.temporal.api.workflowservice.v1.RegisterNamespaceRequest\x1a:.temporal.api.workflowservice.v1.RegisterNamespaceResponse"7\x82\xd3\xe4\x93\x02\x31"\x13/cluster/namespaces:\x01*Z\x17"\x12/api/v1/namespaces:\x01*\x12\xd5\x01\n\x11\x44\x65scribeNamespace\x12\x39.temporal.api.workflowservice.v1.DescribeNamespaceRequest\x1a:.temporal.api.workflowservice.v1.DescribeNamespaceResponse"I\x82\xd3\xe4\x93\x02\x43\x12\x1f/cluster/namespaces/{namespace}Z \x12\x1e/api/v1/namespaces/{namespace}\x12\xb4\x01\n\x0eListNamespaces\x12\x36.temporal.api.workflowservice.v1.ListNamespacesRequest\x1a\x37.temporal.api.workflowservice.v1.ListNamespacesResponse"1\x82\xd3\xe4\x93\x02+\x12\x13/cluster/namespacesZ\x14\x12\x12/api/v1/namespaces\x12\xe3\x01\n\x0fUpdateNamespace\x12\x37.temporal.api.workflowservice.v1.UpdateNamespaceRequest\x1a\x38.temporal.api.workflowservice.v1.UpdateNamespaceResponse"]\x82\xd3\xe4\x93\x02W"&/cluster/namespaces/{namespace}/update:\x01*Z*"%/api/v1/namespaces/{namespace}/update:\x01*\x12\x8f\x01\n\x12\x44\x65precateNamespace\x12:.temporal.api.workflowservice.v1.DeprecateNamespaceRequest\x1a;.temporal.api.workflowservice.v1.DeprecateNamespaceResponse"\x00\x12\xc6\x02\n\x16StartWorkflowExecution\x12>.temporal.api.workflowservice.v1.StartWorkflowExecutionRequest\x1a?.temporal.api.workflowservice.v1.StartWorkflowExecutionResponse"\xaa\x01\x82\xd3\xe4\x93\x02q"//namespaces/{namespace}/workflows/{workflow_id}:\x01*Z;"6/api/v1/namespaces/{namespace}/workflows/{workflow_id}:\x01*\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16workflow:{workflow_id}\x12\xc2\x01\n\x15\x45xecuteMultiOperation\x12=.temporal.api.workflowservice.v1.ExecuteMultiOperationRequest\x1a>.temporal.api.workflowservice.v1.ExecuteMultiOperationResponse"*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xfe\x02\n\x1bGetWorkflowExecutionHistory\x12\x43.temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest\x1a\x44.temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse"\xd3\x01\x82\xd3\xe4\x93\x02\x8f\x01\x12\x41/namespaces/{namespace}/workflows/{execution.workflow_id}/historyZJ\x12H/api/v1/namespaces/{namespace}/workflows/{execution.workflow_id}/history\x8a\x9d\xcc\x1b\x38\n\x14temporal-resource-id\x12 workflow:{execution.workflow_id}\x12\xa3\x03\n"GetWorkflowExecutionHistoryReverse\x12J.temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest\x1aK.temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse"\xe3\x01\x82\xd3\xe4\x93\x02\x9f\x01\x12I/namespaces/{namespace}/workflows/{execution.workflow_id}/history-reverseZR\x12P/api/v1/namespaces/{namespace}/workflows/{execution.workflow_id}/history-reverse\x8a\x9d\xcc\x1b\x38\n\x14temporal-resource-id\x12 workflow:{execution.workflow_id}\x12\xcd\x01\n\x15PollWorkflowTaskQueue\x12=.temporal.api.workflowservice.v1.PollWorkflowTaskQueueRequest\x1a>.temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse"5\x8a\x9d\xcc\x1b\x30\n\x14temporal-resource-id\x12\x18poller:{poller_group_id}\x12\xd7\x01\n\x1cRespondWorkflowTaskCompleted\x12\x44.temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest\x1a\x45.temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedResponse"*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xce\x01\n\x19RespondWorkflowTaskFailed\x12\x41.temporal.api.workflowservice.v1.RespondWorkflowTaskFailedRequest\x1a\x42.temporal.api.workflowservice.v1.RespondWorkflowTaskFailedResponse"*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xcd\x01\n\x15PollActivityTaskQueue\x12=.temporal.api.workflowservice.v1.PollActivityTaskQueueRequest\x1a>.temporal.api.workflowservice.v1.PollActivityTaskQueueResponse"5\x8a\x9d\xcc\x1b\x30\n\x14temporal-resource-id\x12\x18poller:{poller_group_id}\x12\xc2\x02\n\x1bRecordActivityTaskHeartbeat\x12\x43.temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatRequest\x1a\x44.temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatResponse"\x97\x01\x82\xd3\xe4\x93\x02g"*/namespaces/{namespace}/activity-heartbeat:\x01*Z6"1/api/v1/namespaces/{namespace}/activity-heartbeat:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xa8\x04\n\x1fRecordActivityTaskHeartbeatById\x12G.temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatByIdRequest\x1aH.temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatByIdResponse"\xf1\x02\x82\xd3\xe4\x93\x02\xc0\x02":/namespaces/{namespace}/activities/{activity_id}/heartbeat:\x01*ZF"A/api/v1/namespaces/{namespace}/activities/{activity_id}/heartbeat:\x01*ZW"R/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/heartbeat:\x01*Z^"Y/api/v1/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/heartbeat:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xc3\x02\n\x1cRespondActivityTaskCompleted\x12\x44.temporal.api.workflowservice.v1.RespondActivityTaskCompletedRequest\x1a\x45.temporal.api.workflowservice.v1.RespondActivityTaskCompletedResponse"\x95\x01\x82\xd3\xe4\x93\x02\x65")/namespaces/{namespace}/activity-complete:\x01*Z5"0/api/v1/namespaces/{namespace}/activity-complete:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xa7\x04\n RespondActivityTaskCompletedById\x12H.temporal.api.workflowservice.v1.RespondActivityTaskCompletedByIdRequest\x1aI.temporal.api.workflowservice.v1.RespondActivityTaskCompletedByIdResponse"\xed\x02\x82\xd3\xe4\x93\x02\xbc\x02"9/namespaces/{namespace}/activities/{activity_id}/complete:\x01*ZE"@/api/v1/namespaces/{namespace}/activities/{activity_id}/complete:\x01*ZV"Q/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/complete:\x01*Z]"X/api/v1/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/complete:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xb2\x02\n\x19RespondActivityTaskFailed\x12\x41.temporal.api.workflowservice.v1.RespondActivityTaskFailedRequest\x1a\x42.temporal.api.workflowservice.v1.RespondActivityTaskFailedResponse"\x8d\x01\x82\xd3\xe4\x93\x02]"%/namespaces/{namespace}/activity-fail:\x01*Z1",/api/v1/namespaces/{namespace}/activity-fail:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\x8e\x04\n\x1dRespondActivityTaskFailedById\x12\x45.temporal.api.workflowservice.v1.RespondActivityTaskFailedByIdRequest\x1a\x46.temporal.api.workflowservice.v1.RespondActivityTaskFailedByIdResponse"\xdd\x02\x82\xd3\xe4\x93\x02\xac\x02"5/namespaces/{namespace}/activities/{activity_id}/fail:\x01*ZA".temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest\x1a?.temporal.api.workflowservice.v1.ResetWorkflowExecutionResponse"\xf0\x01\x82\xd3\xe4\x93\x02\xa3\x01"H/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/reset:\x01*ZT"O/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/reset:\x01*\x8a\x9d\xcc\x1b\x41\n\x14temporal-resource-id\x12)workflow:{workflow_execution.workflow_id}\x12\xa0\x03\n\x1aTerminateWorkflowExecution\x12\x42.temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest\x1a\x43.temporal.api.workflowservice.v1.TerminateWorkflowExecutionResponse"\xf8\x01\x82\xd3\xe4\x93\x02\xab\x01"L/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/terminate:\x01*ZX"S/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/terminate:\x01*\x8a\x9d\xcc\x1b\x41\n\x14temporal-resource-id\x12)workflow:{workflow_execution.workflow_id}\x12\xe4\x01\n\x17\x44\x65leteWorkflowExecution\x12?.temporal.api.workflowservice.v1.DeleteWorkflowExecutionRequest\x1a@.temporal.api.workflowservice.v1.DeleteWorkflowExecutionResponse"F\x8a\x9d\xcc\x1b\x41\n\x14temporal-resource-id\x12)workflow:{workflow_execution.workflow_id}\x12\xa7\x01\n\x1aListOpenWorkflowExecutions\x12\x42.temporal.api.workflowservice.v1.ListOpenWorkflowExecutionsRequest\x1a\x43.temporal.api.workflowservice.v1.ListOpenWorkflowExecutionsResponse"\x00\x12\xad\x01\n\x1cListClosedWorkflowExecutions\x12\x44.temporal.api.workflowservice.v1.ListClosedWorkflowExecutionsRequest\x1a\x45.temporal.api.workflowservice.v1.ListClosedWorkflowExecutionsResponse"\x00\x12\xf0\x01\n\x16ListWorkflowExecutions\x12>.temporal.api.workflowservice.v1.ListWorkflowExecutionsRequest\x1a?.temporal.api.workflowservice.v1.ListWorkflowExecutionsResponse"U\x82\xd3\xe4\x93\x02O\x12!/namespaces/{namespace}/workflowsZ*\x12(/api/v1/namespaces/{namespace}/workflows\x12\x9a\x02\n\x1eListArchivedWorkflowExecutions\x12\x46.temporal.api.workflowservice.v1.ListArchivedWorkflowExecutionsRequest\x1aG.temporal.api.workflowservice.v1.ListArchivedWorkflowExecutionsResponse"g\x82\xd3\xe4\x93\x02\x61\x12*/namespaces/{namespace}/archived-workflowsZ3\x12\x31/api/v1/namespaces/{namespace}/archived-workflows\x12\x9b\x01\n\x16ScanWorkflowExecutions\x12>.temporal.api.workflowservice.v1.ScanWorkflowExecutionsRequest\x1a?.temporal.api.workflowservice.v1.ScanWorkflowExecutionsResponse"\x00\x12\xfd\x01\n\x17\x43ountWorkflowExecutions\x12?.temporal.api.workflowservice.v1.CountWorkflowExecutionsRequest\x1a@.temporal.api.workflowservice.v1.CountWorkflowExecutionsResponse"_\x82\xd3\xe4\x93\x02Y\x12&/namespaces/{namespace}/workflow-countZ/\x12-/api/v1/namespaces/{namespace}/workflow-count\x12\x92\x01\n\x13GetSearchAttributes\x12;.temporal.api.workflowservice.v1.GetSearchAttributesRequest\x1a<.temporal.api.workflowservice.v1.GetSearchAttributesResponse"\x00\x12\xd9\x01\n\x19RespondQueryTaskCompleted\x12\x41.temporal.api.workflowservice.v1.RespondQueryTaskCompletedRequest\x1a\x42.temporal.api.workflowservice.v1.RespondQueryTaskCompletedResponse"5\x8a\x9d\xcc\x1b\x30\n\x14temporal-resource-id\x12\x18poller:{poller_group_id}\x12\xd2\x01\n\x14ResetStickyTaskQueue\x12<.temporal.api.workflowservice.v1.ResetStickyTaskQueueRequest\x1a=.temporal.api.workflowservice.v1.ResetStickyTaskQueueResponse"=\x8a\x9d\xcc\x1b\x38\n\x14temporal-resource-id\x12 workflow:{execution.workflow_id}\x12\x83\x01\n\x0eShutdownWorker\x12\x36.temporal.api.workflowservice.v1.ShutdownWorkerRequest\x1a\x37.temporal.api.workflowservice.v1.ShutdownWorkerResponse"\x00\x12\xfc\x02\n\rQueryWorkflow\x12\x35.temporal.api.workflowservice.v1.QueryWorkflowRequest\x1a\x36.temporal.api.workflowservice.v1.QueryWorkflowResponse"\xfb\x01\x82\xd3\xe4\x93\x02\xb7\x01"R/namespaces/{namespace}/workflows/{execution.workflow_id}/query/{query.query_type}:\x01*Z^"Y/api/v1/namespaces/{namespace}/workflows/{execution.workflow_id}/query/{query.query_type}:\x01*\x8a\x9d\xcc\x1b\x38\n\x14temporal-resource-id\x12 workflow:{execution.workflow_id}\x12\xe7\x02\n\x19\x44\x65scribeWorkflowExecution\x12\x41.temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest\x1a\x42.temporal.api.workflowservice.v1.DescribeWorkflowExecutionResponse"\xc2\x01\x82\xd3\xe4\x93\x02\x7f\x12\x39/namespaces/{namespace}/workflows/{execution.workflow_id}ZB\x12@/api/v1/namespaces/{namespace}/workflows/{execution.workflow_id}\x8a\x9d\xcc\x1b\x38\n\x14temporal-resource-id\x12 workflow:{execution.workflow_id}\x12\xc2\x02\n\x11\x44\x65scribeTaskQueue\x12\x39.temporal.api.workflowservice.v1.DescribeTaskQueueRequest\x1a:.temporal.api.workflowservice.v1.DescribeTaskQueueResponse"\xb5\x01\x82\xd3\xe4\x93\x02w\x12\x35/namespaces/{namespace}/task-queues/{task_queue.name}Z>\x12/namespaces/{namespace}/schedules/{schedule_id}/matching-timesZG\x12\x45/api/v1/namespaces/{namespace}/schedules/{schedule_id}/matching-times\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16schedule:{schedule_id}\x12\xa8\x02\n\x0e\x44\x65leteSchedule\x12\x36.temporal.api.workflowservice.v1.DeleteScheduleRequest\x1a\x37.temporal.api.workflowservice.v1.DeleteScheduleResponse"\xa4\x01\x82\xd3\xe4\x93\x02k*//namespaces/{namespace}/schedules/{schedule_id}Z8*6/api/v1/namespaces/{namespace}/schedules/{schedule_id}\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16schedule:{schedule_id}\x12\xd5\x01\n\rListSchedules\x12\x35.temporal.api.workflowservice.v1.ListSchedulesRequest\x1a\x36.temporal.api.workflowservice.v1.ListSchedulesResponse"U\x82\xd3\xe4\x93\x02O\x12!/namespaces/{namespace}/schedulesZ*\x12(/api/v1/namespaces/{namespace}/schedules\x12\xe2\x01\n\x0e\x43ountSchedules\x12\x36.temporal.api.workflowservice.v1.CountSchedulesRequest\x1a\x37.temporal.api.workflowservice.v1.CountSchedulesResponse"_\x82\xd3\xe4\x93\x02Y\x12&/namespaces/{namespace}/schedule-countZ/\x12-/api/v1/namespaces/{namespace}/schedule-count\x12\xb9\x01\n UpdateWorkerBuildIdCompatibility\x12H.temporal.api.workflowservice.v1.UpdateWorkerBuildIdCompatibilityRequest\x1aI.temporal.api.workflowservice.v1.UpdateWorkerBuildIdCompatibilityResponse"\x00\x12\xe1\x02\n\x1dGetWorkerBuildIdCompatibility\x12\x45.temporal.api.workflowservice.v1.GetWorkerBuildIdCompatibilityRequest\x1a\x46.temporal.api.workflowservice.v1.GetWorkerBuildIdCompatibilityResponse"\xb0\x01\x82\xd3\xe4\x93\x02\xa9\x01\x12N/namespaces/{namespace}/task-queues/{task_queue}/worker-build-id-compatibilityZW\x12U/api/v1/namespaces/{namespace}/task-queues/{task_queue}/worker-build-id-compatibility\x12\xaa\x01\n\x1bUpdateWorkerVersioningRules\x12\x43.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest\x1a\x44.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesResponse"\x00\x12\xc6\x02\n\x18GetWorkerVersioningRules\x12@.temporal.api.workflowservice.v1.GetWorkerVersioningRulesRequest\x1a\x41.temporal.api.workflowservice.v1.GetWorkerVersioningRulesResponse"\xa4\x01\x82\xd3\xe4\x93\x02\x9d\x01\x12H/namespaces/{namespace}/task-queues/{task_queue}/worker-versioning-rulesZQ\x12O/api/v1/namespaces/{namespace}/task-queues/{task_queue}/worker-versioning-rules\x12\x97\x02\n\x19GetWorkerTaskReachability\x12\x41.temporal.api.workflowservice.v1.GetWorkerTaskReachabilityRequest\x1a\x42.temporal.api.workflowservice.v1.GetWorkerTaskReachabilityResponse"s\x82\xd3\xe4\x93\x02m\x12\x30/namespaces/{namespace}/worker-task-reachabilityZ9\x12\x37/api/v1/namespaces/{namespace}/worker-task-reachability\x12\xc8\x02\n\x12\x44\x65scribeDeployment\x12:.temporal.api.workflowservice.v1.DescribeDeploymentRequest\x1a;.temporal.api.workflowservice.v1.DescribeDeploymentResponse"\xb8\x01\x82\xd3\xe4\x93\x02\xb1\x01\x12R/namespaces/{namespace}/deployments/{deployment.series_name}/{deployment.build_id}Z[\x12Y/api/v1/namespaces/{namespace}/deployments/{deployment.series_name}/{deployment.build_id}\x12\x81\x04\n\x1f\x44\x65scribeWorkerDeploymentVersion\x12G.temporal.api.workflowservice.v1.DescribeWorkerDeploymentVersionRequest\x1aH.temporal.api.workflowservice.v1.DescribeWorkerDeploymentVersionResponse"\xca\x02\x82\xd3\xe4\x93\x02\xf7\x01\x12u/namespaces/{namespace}/worker-deployment-versions/{deployment_version.deployment_name}/{deployment_version.build_id}Z~\x12|/api/v1/namespaces/{namespace}/worker-deployment-versions/{deployment_version.deployment_name}/{deployment_version.build_id}\x8a\x9d\xcc\x1bG\n\x14temporal-resource-id\x12/deployment:{deployment_version.deployment_name}\x12\xdf\x01\n\x0fListDeployments\x12\x37.temporal.api.workflowservice.v1.ListDeploymentsRequest\x1a\x38.temporal.api.workflowservice.v1.ListDeploymentsResponse"Y\x82\xd3\xe4\x93\x02S\x12#/namespaces/{namespace}/deploymentsZ,\x12*/api/v1/namespaces/{namespace}/deployments\x12\xf7\x02\n\x19GetDeploymentReachability\x12\x41.temporal.api.workflowservice.v1.GetDeploymentReachabilityRequest\x1a\x42.temporal.api.workflowservice.v1.GetDeploymentReachabilityResponse"\xd2\x01\x82\xd3\xe4\x93\x02\xcb\x01\x12_/namespaces/{namespace}/deployments/{deployment.series_name}/{deployment.build_id}/reachabilityZh\x12\x66/api/v1/namespaces/{namespace}/deployments/{deployment.series_name}/{deployment.build_id}/reachability\x12\x99\x02\n\x14GetCurrentDeployment\x12<.temporal.api.workflowservice.v1.GetCurrentDeploymentRequest\x1a=.temporal.api.workflowservice.v1.GetCurrentDeploymentResponse"\x83\x01\x82\xd3\xe4\x93\x02}\x12\x38/namespaces/{namespace}/current-deployment/{series_name}ZA\x12?/api/v1/namespaces/{namespace}/current-deployment/{series_name}\x12\xb6\x02\n\x14SetCurrentDeployment\x12<.temporal.api.workflowservice.v1.SetCurrentDeploymentRequest\x1a=.temporal.api.workflowservice.v1.SetCurrentDeploymentResponse"\xa0\x01\x82\xd3\xe4\x93\x02\x99\x01"C/namespaces/{namespace}/current-deployment/{deployment.series_name}:\x01*ZO"J/api/v1/namespaces/{namespace}/current-deployment/{deployment.series_name}:\x01*\x12\xb0\x03\n!SetWorkerDeploymentCurrentVersion\x12I.temporal.api.workflowservice.v1.SetWorkerDeploymentCurrentVersionRequest\x1aJ.temporal.api.workflowservice.v1.SetWorkerDeploymentCurrentVersionResponse"\xf3\x01\x82\xd3\xe4\x93\x02\xb3\x01"P/namespaces/{namespace}/worker-deployments/{deployment_name}/set-current-version:\x01*Z\\"W/api/v1/namespaces/{namespace}/worker-deployments/{deployment_name}/set-current-version:\x01*\x8a\x9d\xcc\x1b\x34\n\x14temporal-resource-id\x12\x1c\x64\x65ployment:{deployment_name}\x12\xe7\x02\n\x18\x44\x65scribeWorkerDeployment\x12@.temporal.api.workflowservice.v1.DescribeWorkerDeploymentRequest\x1a\x41.temporal.api.workflowservice.v1.DescribeWorkerDeploymentResponse"\xc5\x01\x82\xd3\xe4\x93\x02\x85\x01\x12.temporal.api.workflowservice.v1.DeleteWorkerDeploymentRequest\x1a?.temporal.api.workflowservice.v1.DeleteWorkerDeploymentResponse"\xc5\x01\x82\xd3\xe4\x93\x02\x85\x01*.temporal.api.workflowservice.v1.ListWorkerDeploymentsResponse"g\x82\xd3\xe4\x93\x02\x61\x12*/namespaces/{namespace}/worker-deploymentsZ3\x12\x31/api/v1/namespaces/{namespace}/worker-deployments\x12\xae\x02\n\x16\x43reateWorkerDeployment\x12>.temporal.api.workflowservice.v1.CreateWorkerDeploymentRequest\x1a?.temporal.api.workflowservice.v1.CreateWorkerDeploymentResponse"\x92\x01\x82\xd3\xe4\x93\x02\x8b\x01".temporal.api.workflowservice.v1.DescribeBatchOperationRequest\x1a?.temporal.api.workflowservice.v1.DescribeBatchOperationResponse"\xa0\x01\x82\xd3\xe4\x93\x02o\x12\x31/namespaces/{namespace}/batch-operations/{job_id}Z:\x12\x38/api/v1/namespaces/{namespace}/batch-operations/{job_id}\x8a\x9d\xcc\x1b&\n\x14temporal-resource-id\x12\x0e\x62\x61tch:{job_id}\x12\xf5\x01\n\x13ListBatchOperations\x12;.temporal.api.workflowservice.v1.ListBatchOperationsRequest\x1a<.temporal.api.workflowservice.v1.ListBatchOperationsResponse"c\x82\xd3\xe4\x93\x02]\x12(/namespaces/{namespace}/batch-operationsZ1\x12//api/v1/namespaces/{namespace}/batch-operations\x12\xc4\x01\n\x12PollNexusTaskQueue\x12:.temporal.api.workflowservice.v1.PollNexusTaskQueueRequest\x1a;.temporal.api.workflowservice.v1.PollNexusTaskQueueResponse"5\x8a\x9d\xcc\x1b\x30\n\x14temporal-resource-id\x12\x18poller:{poller_group_id}\x12\xd9\x01\n\x19RespondNexusTaskCompleted\x12\x41.temporal.api.workflowservice.v1.RespondNexusTaskCompletedRequest\x1a\x42.temporal.api.workflowservice.v1.RespondNexusTaskCompletedResponse"5\x8a\x9d\xcc\x1b\x30\n\x14temporal-resource-id\x12\x18poller:{poller_group_id}\x12\xd0\x01\n\x16RespondNexusTaskFailed\x12>.temporal.api.workflowservice.v1.RespondNexusTaskFailedRequest\x1a?.temporal.api.workflowservice.v1.RespondNexusTaskFailedResponse"5\x8a\x9d\xcc\x1b\x30\n\x14temporal-resource-id\x12\x18poller:{poller_group_id}\x12\xe8\x02\n\x15UpdateActivityOptions\x12=.temporal.api.workflowservice.v1.UpdateActivityOptionsRequest\x1a>.temporal.api.workflowservice.v1.UpdateActivityOptionsResponse"\xcf\x01\x82\xd3\xe4\x93\x02\x8b\x01".temporal.api.workflowservice.v1.RecordWorkerHeartbeatResponse"\x95\x01\x82\xd3\xe4\x93\x02\x65")/namespaces/{namespace}/workers/heartbeat:\x01*Z5"0/api/v1/namespaces/{namespace}/workers/heartbeat:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xcb\x01\n\x0bListWorkers\x12\x33.temporal.api.workflowservice.v1.ListWorkersRequest\x1a\x34.temporal.api.workflowservice.v1.ListWorkersResponse"Q\x82\xd3\xe4\x93\x02K\x12\x1f/namespaces/{namespace}/workersZ(\x12&/api/v1/namespaces/{namespace}/workers\x12\xe2\x02\n\x15UpdateTaskQueueConfig\x12=.temporal.api.workflowservice.v1.UpdateTaskQueueConfigRequest\x1a>.temporal.api.workflowservice.v1.UpdateTaskQueueConfigResponse"\xc9\x01\x82\xd3\xe4\x93\x02\x8f\x01">/namespaces/{namespace}/task-queues/{task_queue}/update-config:\x01*ZJ"E/api/v1/namespaces/{namespace}/task-queues/{task_queue}/update-config:\x01*\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16taskqueue:{task_queue}\x12\xa8\x02\n\x11\x46\x65tchWorkerConfig\x12\x39.temporal.api.workflowservice.v1.FetchWorkerConfigRequest\x1a:.temporal.api.workflowservice.v1.FetchWorkerConfigResponse"\x9b\x01\x82\xd3\xe4\x93\x02k",/namespaces/{namespace}/workers/fetch-config:\x01*Z8"3/api/v1/namespaces/{namespace}/workers/fetch-config:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xad\x02\n\x12UpdateWorkerConfig\x12:.temporal.api.workflowservice.v1.UpdateWorkerConfigRequest\x1a;.temporal.api.workflowservice.v1.UpdateWorkerConfigResponse"\x9d\x01\x82\xd3\xe4\x93\x02m"-/namespaces/{namespace}/workers/update-config:\x01*Z9"4/api/v1/namespaces/{namespace}/workers/update-config:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xcd\x02\n\x0e\x44\x65scribeWorker\x12\x36.temporal.api.workflowservice.v1.DescribeWorkerRequest\x1a\x37.temporal.api.workflowservice.v1.DescribeWorkerResponse"\xc9\x01\x82\xd3\xe4\x93\x02\x89\x01\x12>/namespaces/{namespace}/workers/describe/{worker_instance_key}ZG\x12\x45/api/v1/namespaces/{namespace}/workers/describe/{worker_instance_key}\x8a\x9d\xcc\x1b\x34\n\x14temporal-resource-id\x12\x1cworker:{worker_instance_key}\x12\xd2\x02\n\x16PauseWorkflowExecution\x12>.temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest\x1a?.temporal.api.workflowservice.v1.PauseWorkflowExecutionResponse"\xb6\x01\x82\xd3\xe4\x93\x02}"5/namespaces/{namespace}/workflows/{workflow_id}/pause:\x01*ZA"/api/v1/namespaces/{namespace}/workflows/{workflow_id}/unpause:\x01*\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16workflow:{workflow_id}\x12\xc8\x02\n\x16StartActivityExecution\x12>.temporal.api.workflowservice.v1.StartActivityExecutionRequest\x1a?.temporal.api.workflowservice.v1.StartActivityExecutionResponse"\xac\x01\x82\xd3\xe4\x93\x02s"0/namespaces/{namespace}/activities/{activity_id}:\x01*Z<"7/api/v1/namespaces/{namespace}/activities/{activity_id}:\x01*\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16\x61\x63tivity:{activity_id}\x12\xb6\x02\n\x1cStartNexusOperationExecution\x12\x44.temporal.api.workflowservice.v1.StartNexusOperationExecutionRequest\x1a\x45.temporal.api.workflowservice.v1.StartNexusOperationExecutionResponse"\x88\x01\x82\xd3\xe4\x93\x02\x81\x01"7/namespaces/{namespace}/nexus-operations/{operation_id}:\x01*ZC">/api/v1/namespaces/{namespace}/nexus-operations/{operation_id}:\x01*\x12\xcb\x02\n\x19\x44\x65scribeActivityExecution\x12\x41.temporal.api.workflowservice.v1.DescribeActivityExecutionRequest\x1a\x42.temporal.api.workflowservice.v1.DescribeActivityExecutionResponse"\xa6\x01\x82\xd3\xe4\x93\x02m\x12\x30/namespaces/{namespace}/activities/{activity_id}Z9\x12\x37/api/v1/namespaces/{namespace}/activities/{activity_id}\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16\x61\x63tivity:{activity_id}\x12\xb8\x02\n\x1f\x44\x65scribeNexusOperationExecution\x12G.temporal.api.workflowservice.v1.DescribeNexusOperationExecutionRequest\x1aH.temporal.api.workflowservice.v1.DescribeNexusOperationExecutionResponse"\x81\x01\x82\xd3\xe4\x93\x02{\x12\x37/namespaces/{namespace}/nexus-operations/{operation_id}Z@\x12>/api/v1/namespaces/{namespace}/nexus-operations/{operation_id}\x12\xcf\x02\n\x15PollActivityExecution\x12=.temporal.api.workflowservice.v1.PollActivityExecutionRequest\x1a>.temporal.api.workflowservice.v1.PollActivityExecutionResponse"\xb6\x01\x82\xd3\xe4\x93\x02}\x12\x38/namespaces/{namespace}/activities/{activity_id}/outcomeZA\x12?/api/v1/namespaces/{namespace}/activities/{activity_id}/outcome\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16\x61\x63tivity:{activity_id}\x12\xb7\x02\n\x1bPollNexusOperationExecution\x12\x43.temporal.api.workflowservice.v1.PollNexusOperationExecutionRequest\x1a\x44.temporal.api.workflowservice.v1.PollNexusOperationExecutionResponse"\x8c\x01\x82\xd3\xe4\x93\x02\x85\x01\x12.temporal.api.workflowservice.v1.ListActivityExecutionsRequest\x1a?.temporal.api.workflowservice.v1.ListActivityExecutionsResponse"W\x82\xd3\xe4\x93\x02Q\x12"/namespaces/{namespace}/activitiesZ+\x12)/api/v1/namespaces/{namespace}/activities\x12\x90\x02\n\x1cListNexusOperationExecutions\x12\x44.temporal.api.workflowservice.v1.ListNexusOperationExecutionsRequest\x1a\x45.temporal.api.workflowservice.v1.ListNexusOperationExecutionsResponse"c\x82\xd3\xe4\x93\x02]\x12(/namespaces/{namespace}/nexus-operationsZ1\x12//api/v1/namespaces/{namespace}/nexus-operations\x12\xfd\x01\n\x17\x43ountActivityExecutions\x12?.temporal.api.workflowservice.v1.CountActivityExecutionsRequest\x1a@.temporal.api.workflowservice.v1.CountActivityExecutionsResponse"_\x82\xd3\xe4\x93\x02Y\x12&/namespaces/{namespace}/activity-countZ/\x12-/api/v1/namespaces/{namespace}/activity-count\x12\x9d\x02\n\x1d\x43ountNexusOperationExecutions\x12\x45.temporal.api.workflowservice.v1.CountNexusOperationExecutionsRequest\x1a\x46.temporal.api.workflowservice.v1.CountNexusOperationExecutionsResponse"m\x82\xd3\xe4\x93\x02g\x12-/namespaces/{namespace}/nexus-operation-countZ6\x12\x34/api/v1/namespaces/{namespace}/nexus-operation-count\x12\xef\x02\n\x1eRequestCancelActivityExecution\x12\x46.temporal.api.workflowservice.v1.RequestCancelActivityExecutionRequest\x1aG.temporal.api.workflowservice.v1.RequestCancelActivityExecutionResponse"\xbb\x01\x82\xd3\xe4\x93\x02\x81\x01"7/namespaces/{namespace}/activities/{activity_id}/cancel:\x01*ZC">/api/v1/namespaces/{namespace}/activities/{activity_id}/cancel:\x01*\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16\x61\x63tivity:{activity_id}\x12\xdc\x02\n$RequestCancelNexusOperationExecution\x12L.temporal.api.workflowservice.v1.RequestCancelNexusOperationExecutionRequest\x1aM.temporal.api.workflowservice.v1.RequestCancelNexusOperationExecutionResponse"\x96\x01\x82\xd3\xe4\x93\x02\x8f\x01">/namespaces/{namespace}/nexus-operations/{operation_id}/cancel:\x01*ZJ"E/api/v1/namespaces/{namespace}/nexus-operations/{operation_id}/cancel:\x01*\x12\xe9\x02\n\x1aTerminateActivityExecution\x12\x42.temporal.api.workflowservice.v1.TerminateActivityExecutionRequest\x1a\x43.temporal.api.workflowservice.v1.TerminateActivityExecutionResponse"\xc1\x01\x82\xd3\xe4\x93\x02\x87\x01":/namespaces/{namespace}/activities/{activity_id}/terminate:\x01*ZF"A/api/v1/namespaces/{namespace}/activities/{activity_id}/terminate:\x01*\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16\x61\x63tivity:{activity_id}\x12\x9e\x01\n\x17\x44\x65leteActivityExecution\x12?.temporal.api.workflowservice.v1.DeleteActivityExecutionRequest\x1a@.temporal.api.workflowservice.v1.DeleteActivityExecutionResponse"\x00\x12\xfd\x03\n\x16PauseActivityExecution\x12>.temporal.api.workflowservice.v1.PauseActivityExecutionRequest\x1a?.temporal.api.workflowservice.v1.PauseActivityExecutionResponse"\xe1\x02\x82\xd3\xe4\x93\x02\xb0\x02"6/namespaces/{namespace}/activities/{activity_id}/pause:\x01*ZB"=/api/v1/namespaces/{namespace}/activities/{activity_id}/pause:\x01*ZS"N/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/pause:\x01*ZZ"U/api/v1/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/pause:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xfd\x03\n\x16ResetActivityExecution\x12>.temporal.api.workflowservice.v1.ResetActivityExecutionRequest\x1a?.temporal.api.workflowservice.v1.ResetActivityExecutionResponse"\xe1\x02\x82\xd3\xe4\x93\x02\xb0\x02"6/namespaces/{namespace}/activities/{activity_id}/reset:\x01*ZB"=/api/v1/namespaces/{namespace}/activities/{activity_id}/reset:\x01*ZS"N/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/reset:\x01*ZZ"U/api/v1/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/reset:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\x8b\x04\n\x18UnpauseActivityExecution\x12@.temporal.api.workflowservice.v1.UnpauseActivityExecutionRequest\x1a\x41.temporal.api.workflowservice.v1.UnpauseActivityExecutionResponse"\xe9\x02\x82\xd3\xe4\x93\x02\xb8\x02"8/namespaces/{namespace}/activities/{activity_id}/unpause:\x01*ZD"?/api/v1/namespaces/{namespace}/activities/{activity_id}/unpause:\x01*ZU"P/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/unpause:\x01*Z\\"W/api/v1/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/unpause:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xb9\x04\n\x1eUpdateActivityExecutionOptions\x12\x46.temporal.api.workflowservice.v1.UpdateActivityExecutionOptionsRequest\x1aG.temporal.api.workflowservice.v1.UpdateActivityExecutionOptionsResponse"\x85\x03\x82\xd3\xe4\x93\x02\xd4\x02"?/namespaces/{namespace}/activities/{activity_id}/update-options:\x01*ZK"F/api/v1/namespaces/{namespace}/activities/{activity_id}/update-options:\x01*Z\\"W/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/update-options:\x01*Zc"^/api/v1/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/update-options:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xd6\x02\n TerminateNexusOperationExecution\x12H.temporal.api.workflowservice.v1.TerminateNexusOperationExecutionRequest\x1aI.temporal.api.workflowservice.v1.TerminateNexusOperationExecutionResponse"\x9c\x01\x82\xd3\xe4\x93\x02\x95\x01"A/namespaces/{namespace}/nexus-operations/{operation_id}/terminate:\x01*ZM"H/api/v1/namespaces/{namespace}/nexus-operations/{operation_id}/terminate:\x01*\x12\xb0\x01\n\x1d\x44\x65leteNexusOperationExecution\x12\x45.temporal.api.workflowservice.v1.DeleteNexusOperationExecutionRequest\x1a\x46.temporal.api.workflowservice.v1.DeleteNexusOperationExecutionResponse"\x00\x42\xb6\x01\n"io.temporal.api.workflowservice.v1B\x0cServiceProtoP\x01Z5go.temporal.io/api/workflowservice/v1;workflowservice\xaa\x02!Temporalio.Api.WorkflowService.V1\xea\x02$Temporalio::Api::WorkflowService::V1b\x06proto3' + b'\n-temporal/api/workflowservice/v1/service.proto\x12\x1ftemporal.api.workflowservice.v1\x1a\x1cgoogle/api/annotations.proto\x1a!nexusannotations/v1/options.proto\x1a+temporal/api/protometa/v1/annotations.proto\x1a\x36temporal/api/workflowservice/v1/request_response.proto2\xdc\xb2\x02\n\x0fWorkflowService\x12\xc3\x01\n\x11RegisterNamespace\x12\x39.temporal.api.workflowservice.v1.RegisterNamespaceRequest\x1a:.temporal.api.workflowservice.v1.RegisterNamespaceResponse"7\x82\xd3\xe4\x93\x02\x31"\x13/cluster/namespaces:\x01*Z\x17"\x12/api/v1/namespaces:\x01*\x12\xd5\x01\n\x11\x44\x65scribeNamespace\x12\x39.temporal.api.workflowservice.v1.DescribeNamespaceRequest\x1a:.temporal.api.workflowservice.v1.DescribeNamespaceResponse"I\x82\xd3\xe4\x93\x02\x43\x12\x1f/cluster/namespaces/{namespace}Z \x12\x1e/api/v1/namespaces/{namespace}\x12\xb4\x01\n\x0eListNamespaces\x12\x36.temporal.api.workflowservice.v1.ListNamespacesRequest\x1a\x37.temporal.api.workflowservice.v1.ListNamespacesResponse"1\x82\xd3\xe4\x93\x02+\x12\x13/cluster/namespacesZ\x14\x12\x12/api/v1/namespaces\x12\xe3\x01\n\x0fUpdateNamespace\x12\x37.temporal.api.workflowservice.v1.UpdateNamespaceRequest\x1a\x38.temporal.api.workflowservice.v1.UpdateNamespaceResponse"]\x82\xd3\xe4\x93\x02W"&/cluster/namespaces/{namespace}/update:\x01*Z*"%/api/v1/namespaces/{namespace}/update:\x01*\x12\x8f\x01\n\x12\x44\x65precateNamespace\x12:.temporal.api.workflowservice.v1.DeprecateNamespaceRequest\x1a;.temporal.api.workflowservice.v1.DeprecateNamespaceResponse"\x00\x12\xc6\x02\n\x16StartWorkflowExecution\x12>.temporal.api.workflowservice.v1.StartWorkflowExecutionRequest\x1a?.temporal.api.workflowservice.v1.StartWorkflowExecutionResponse"\xaa\x01\x82\xd3\xe4\x93\x02q"//namespaces/{namespace}/workflows/{workflow_id}:\x01*Z;"6/api/v1/namespaces/{namespace}/workflows/{workflow_id}:\x01*\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16workflow:{workflow_id}\x12\xc2\x01\n\x15\x45xecuteMultiOperation\x12=.temporal.api.workflowservice.v1.ExecuteMultiOperationRequest\x1a>.temporal.api.workflowservice.v1.ExecuteMultiOperationResponse"*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xfe\x02\n\x1bGetWorkflowExecutionHistory\x12\x43.temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryRequest\x1a\x44.temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse"\xd3\x01\x82\xd3\xe4\x93\x02\x8f\x01\x12\x41/namespaces/{namespace}/workflows/{execution.workflow_id}/historyZJ\x12H/api/v1/namespaces/{namespace}/workflows/{execution.workflow_id}/history\x8a\x9d\xcc\x1b\x38\n\x14temporal-resource-id\x12 workflow:{execution.workflow_id}\x12\xa3\x03\n"GetWorkflowExecutionHistoryReverse\x12J.temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseRequest\x1aK.temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryReverseResponse"\xe3\x01\x82\xd3\xe4\x93\x02\x9f\x01\x12I/namespaces/{namespace}/workflows/{execution.workflow_id}/history-reverseZR\x12P/api/v1/namespaces/{namespace}/workflows/{execution.workflow_id}/history-reverse\x8a\x9d\xcc\x1b\x38\n\x14temporal-resource-id\x12 workflow:{execution.workflow_id}\x12\xcd\x01\n\x15PollWorkflowTaskQueue\x12=.temporal.api.workflowservice.v1.PollWorkflowTaskQueueRequest\x1a>.temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse"5\x8a\x9d\xcc\x1b\x30\n\x14temporal-resource-id\x12\x18poller:{poller_group_id}\x12\xd7\x01\n\x1cRespondWorkflowTaskCompleted\x12\x44.temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedRequest\x1a\x45.temporal.api.workflowservice.v1.RespondWorkflowTaskCompletedResponse"*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xce\x01\n\x19RespondWorkflowTaskFailed\x12\x41.temporal.api.workflowservice.v1.RespondWorkflowTaskFailedRequest\x1a\x42.temporal.api.workflowservice.v1.RespondWorkflowTaskFailedResponse"*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xcd\x01\n\x15PollActivityTaskQueue\x12=.temporal.api.workflowservice.v1.PollActivityTaskQueueRequest\x1a>.temporal.api.workflowservice.v1.PollActivityTaskQueueResponse"5\x8a\x9d\xcc\x1b\x30\n\x14temporal-resource-id\x12\x18poller:{poller_group_id}\x12\xc2\x02\n\x1bRecordActivityTaskHeartbeat\x12\x43.temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatRequest\x1a\x44.temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatResponse"\x97\x01\x82\xd3\xe4\x93\x02g"*/namespaces/{namespace}/activity-heartbeat:\x01*Z6"1/api/v1/namespaces/{namespace}/activity-heartbeat:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xa8\x04\n\x1fRecordActivityTaskHeartbeatById\x12G.temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatByIdRequest\x1aH.temporal.api.workflowservice.v1.RecordActivityTaskHeartbeatByIdResponse"\xf1\x02\x82\xd3\xe4\x93\x02\xc0\x02":/namespaces/{namespace}/activities/{activity_id}/heartbeat:\x01*ZF"A/api/v1/namespaces/{namespace}/activities/{activity_id}/heartbeat:\x01*ZW"R/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/heartbeat:\x01*Z^"Y/api/v1/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/heartbeat:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xc3\x02\n\x1cRespondActivityTaskCompleted\x12\x44.temporal.api.workflowservice.v1.RespondActivityTaskCompletedRequest\x1a\x45.temporal.api.workflowservice.v1.RespondActivityTaskCompletedResponse"\x95\x01\x82\xd3\xe4\x93\x02\x65")/namespaces/{namespace}/activity-complete:\x01*Z5"0/api/v1/namespaces/{namespace}/activity-complete:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xa7\x04\n RespondActivityTaskCompletedById\x12H.temporal.api.workflowservice.v1.RespondActivityTaskCompletedByIdRequest\x1aI.temporal.api.workflowservice.v1.RespondActivityTaskCompletedByIdResponse"\xed\x02\x82\xd3\xe4\x93\x02\xbc\x02"9/namespaces/{namespace}/activities/{activity_id}/complete:\x01*ZE"@/api/v1/namespaces/{namespace}/activities/{activity_id}/complete:\x01*ZV"Q/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/complete:\x01*Z]"X/api/v1/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/complete:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xb2\x02\n\x19RespondActivityTaskFailed\x12\x41.temporal.api.workflowservice.v1.RespondActivityTaskFailedRequest\x1a\x42.temporal.api.workflowservice.v1.RespondActivityTaskFailedResponse"\x8d\x01\x82\xd3\xe4\x93\x02]"%/namespaces/{namespace}/activity-fail:\x01*Z1",/api/v1/namespaces/{namespace}/activity-fail:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\x8e\x04\n\x1dRespondActivityTaskFailedById\x12\x45.temporal.api.workflowservice.v1.RespondActivityTaskFailedByIdRequest\x1a\x46.temporal.api.workflowservice.v1.RespondActivityTaskFailedByIdResponse"\xdd\x02\x82\xd3\xe4\x93\x02\xac\x02"5/namespaces/{namespace}/activities/{activity_id}/fail:\x01*ZA".temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest\x1a?.temporal.api.workflowservice.v1.ResetWorkflowExecutionResponse"\xf0\x01\x82\xd3\xe4\x93\x02\xa3\x01"H/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/reset:\x01*ZT"O/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/reset:\x01*\x8a\x9d\xcc\x1b\x41\n\x14temporal-resource-id\x12)workflow:{workflow_execution.workflow_id}\x12\xa0\x03\n\x1aTerminateWorkflowExecution\x12\x42.temporal.api.workflowservice.v1.TerminateWorkflowExecutionRequest\x1a\x43.temporal.api.workflowservice.v1.TerminateWorkflowExecutionResponse"\xf8\x01\x82\xd3\xe4\x93\x02\xab\x01"L/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/terminate:\x01*ZX"S/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/terminate:\x01*\x8a\x9d\xcc\x1b\x41\n\x14temporal-resource-id\x12)workflow:{workflow_execution.workflow_id}\x12\xe4\x01\n\x17\x44\x65leteWorkflowExecution\x12?.temporal.api.workflowservice.v1.DeleteWorkflowExecutionRequest\x1a@.temporal.api.workflowservice.v1.DeleteWorkflowExecutionResponse"F\x8a\x9d\xcc\x1b\x41\n\x14temporal-resource-id\x12)workflow:{workflow_execution.workflow_id}\x12\xa7\x01\n\x1aListOpenWorkflowExecutions\x12\x42.temporal.api.workflowservice.v1.ListOpenWorkflowExecutionsRequest\x1a\x43.temporal.api.workflowservice.v1.ListOpenWorkflowExecutionsResponse"\x00\x12\xad\x01\n\x1cListClosedWorkflowExecutions\x12\x44.temporal.api.workflowservice.v1.ListClosedWorkflowExecutionsRequest\x1a\x45.temporal.api.workflowservice.v1.ListClosedWorkflowExecutionsResponse"\x00\x12\xf0\x01\n\x16ListWorkflowExecutions\x12>.temporal.api.workflowservice.v1.ListWorkflowExecutionsRequest\x1a?.temporal.api.workflowservice.v1.ListWorkflowExecutionsResponse"U\x82\xd3\xe4\x93\x02O\x12!/namespaces/{namespace}/workflowsZ*\x12(/api/v1/namespaces/{namespace}/workflows\x12\x9a\x02\n\x1eListArchivedWorkflowExecutions\x12\x46.temporal.api.workflowservice.v1.ListArchivedWorkflowExecutionsRequest\x1aG.temporal.api.workflowservice.v1.ListArchivedWorkflowExecutionsResponse"g\x82\xd3\xe4\x93\x02\x61\x12*/namespaces/{namespace}/archived-workflowsZ3\x12\x31/api/v1/namespaces/{namespace}/archived-workflows\x12\x9b\x01\n\x16ScanWorkflowExecutions\x12>.temporal.api.workflowservice.v1.ScanWorkflowExecutionsRequest\x1a?.temporal.api.workflowservice.v1.ScanWorkflowExecutionsResponse"\x00\x12\xfd\x01\n\x17\x43ountWorkflowExecutions\x12?.temporal.api.workflowservice.v1.CountWorkflowExecutionsRequest\x1a@.temporal.api.workflowservice.v1.CountWorkflowExecutionsResponse"_\x82\xd3\xe4\x93\x02Y\x12&/namespaces/{namespace}/workflow-countZ/\x12-/api/v1/namespaces/{namespace}/workflow-count\x12\x92\x01\n\x13GetSearchAttributes\x12;.temporal.api.workflowservice.v1.GetSearchAttributesRequest\x1a<.temporal.api.workflowservice.v1.GetSearchAttributesResponse"\x00\x12\xd9\x01\n\x19RespondQueryTaskCompleted\x12\x41.temporal.api.workflowservice.v1.RespondQueryTaskCompletedRequest\x1a\x42.temporal.api.workflowservice.v1.RespondQueryTaskCompletedResponse"5\x8a\x9d\xcc\x1b\x30\n\x14temporal-resource-id\x12\x18poller:{poller_group_id}\x12\xd2\x01\n\x14ResetStickyTaskQueue\x12<.temporal.api.workflowservice.v1.ResetStickyTaskQueueRequest\x1a=.temporal.api.workflowservice.v1.ResetStickyTaskQueueResponse"=\x8a\x9d\xcc\x1b\x38\n\x14temporal-resource-id\x12 workflow:{execution.workflow_id}\x12\x83\x01\n\x0eShutdownWorker\x12\x36.temporal.api.workflowservice.v1.ShutdownWorkerRequest\x1a\x37.temporal.api.workflowservice.v1.ShutdownWorkerResponse"\x00\x12\xfc\x02\n\rQueryWorkflow\x12\x35.temporal.api.workflowservice.v1.QueryWorkflowRequest\x1a\x36.temporal.api.workflowservice.v1.QueryWorkflowResponse"\xfb\x01\x82\xd3\xe4\x93\x02\xb7\x01"R/namespaces/{namespace}/workflows/{execution.workflow_id}/query/{query.query_type}:\x01*Z^"Y/api/v1/namespaces/{namespace}/workflows/{execution.workflow_id}/query/{query.query_type}:\x01*\x8a\x9d\xcc\x1b\x38\n\x14temporal-resource-id\x12 workflow:{execution.workflow_id}\x12\xe7\x02\n\x19\x44\x65scribeWorkflowExecution\x12\x41.temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest\x1a\x42.temporal.api.workflowservice.v1.DescribeWorkflowExecutionResponse"\xc2\x01\x82\xd3\xe4\x93\x02\x7f\x12\x39/namespaces/{namespace}/workflows/{execution.workflow_id}ZB\x12@/api/v1/namespaces/{namespace}/workflows/{execution.workflow_id}\x8a\x9d\xcc\x1b\x38\n\x14temporal-resource-id\x12 workflow:{execution.workflow_id}\x12\xc2\x02\n\x11\x44\x65scribeTaskQueue\x12\x39.temporal.api.workflowservice.v1.DescribeTaskQueueRequest\x1a:.temporal.api.workflowservice.v1.DescribeTaskQueueResponse"\xb5\x01\x82\xd3\xe4\x93\x02w\x12\x35/namespaces/{namespace}/task-queues/{task_queue.name}Z>\x12/namespaces/{namespace}/schedules/{schedule_id}/matching-timesZG\x12\x45/api/v1/namespaces/{namespace}/schedules/{schedule_id}/matching-times\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16schedule:{schedule_id}\x12\xa8\x02\n\x0e\x44\x65leteSchedule\x12\x36.temporal.api.workflowservice.v1.DeleteScheduleRequest\x1a\x37.temporal.api.workflowservice.v1.DeleteScheduleResponse"\xa4\x01\x82\xd3\xe4\x93\x02k*//namespaces/{namespace}/schedules/{schedule_id}Z8*6/api/v1/namespaces/{namespace}/schedules/{schedule_id}\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16schedule:{schedule_id}\x12\xd5\x01\n\rListSchedules\x12\x35.temporal.api.workflowservice.v1.ListSchedulesRequest\x1a\x36.temporal.api.workflowservice.v1.ListSchedulesResponse"U\x82\xd3\xe4\x93\x02O\x12!/namespaces/{namespace}/schedulesZ*\x12(/api/v1/namespaces/{namespace}/schedules\x12\xe2\x01\n\x0e\x43ountSchedules\x12\x36.temporal.api.workflowservice.v1.CountSchedulesRequest\x1a\x37.temporal.api.workflowservice.v1.CountSchedulesResponse"_\x82\xd3\xe4\x93\x02Y\x12&/namespaces/{namespace}/schedule-countZ/\x12-/api/v1/namespaces/{namespace}/schedule-count\x12\xb9\x01\n UpdateWorkerBuildIdCompatibility\x12H.temporal.api.workflowservice.v1.UpdateWorkerBuildIdCompatibilityRequest\x1aI.temporal.api.workflowservice.v1.UpdateWorkerBuildIdCompatibilityResponse"\x00\x12\xe1\x02\n\x1dGetWorkerBuildIdCompatibility\x12\x45.temporal.api.workflowservice.v1.GetWorkerBuildIdCompatibilityRequest\x1a\x46.temporal.api.workflowservice.v1.GetWorkerBuildIdCompatibilityResponse"\xb0\x01\x82\xd3\xe4\x93\x02\xa9\x01\x12N/namespaces/{namespace}/task-queues/{task_queue}/worker-build-id-compatibilityZW\x12U/api/v1/namespaces/{namespace}/task-queues/{task_queue}/worker-build-id-compatibility\x12\xaa\x01\n\x1bUpdateWorkerVersioningRules\x12\x43.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesRequest\x1a\x44.temporal.api.workflowservice.v1.UpdateWorkerVersioningRulesResponse"\x00\x12\xc6\x02\n\x18GetWorkerVersioningRules\x12@.temporal.api.workflowservice.v1.GetWorkerVersioningRulesRequest\x1a\x41.temporal.api.workflowservice.v1.GetWorkerVersioningRulesResponse"\xa4\x01\x82\xd3\xe4\x93\x02\x9d\x01\x12H/namespaces/{namespace}/task-queues/{task_queue}/worker-versioning-rulesZQ\x12O/api/v1/namespaces/{namespace}/task-queues/{task_queue}/worker-versioning-rules\x12\x97\x02\n\x19GetWorkerTaskReachability\x12\x41.temporal.api.workflowservice.v1.GetWorkerTaskReachabilityRequest\x1a\x42.temporal.api.workflowservice.v1.GetWorkerTaskReachabilityResponse"s\x82\xd3\xe4\x93\x02m\x12\x30/namespaces/{namespace}/worker-task-reachabilityZ9\x12\x37/api/v1/namespaces/{namespace}/worker-task-reachability\x12\xc8\x02\n\x12\x44\x65scribeDeployment\x12:.temporal.api.workflowservice.v1.DescribeDeploymentRequest\x1a;.temporal.api.workflowservice.v1.DescribeDeploymentResponse"\xb8\x01\x82\xd3\xe4\x93\x02\xb1\x01\x12R/namespaces/{namespace}/deployments/{deployment.series_name}/{deployment.build_id}Z[\x12Y/api/v1/namespaces/{namespace}/deployments/{deployment.series_name}/{deployment.build_id}\x12\x81\x04\n\x1f\x44\x65scribeWorkerDeploymentVersion\x12G.temporal.api.workflowservice.v1.DescribeWorkerDeploymentVersionRequest\x1aH.temporal.api.workflowservice.v1.DescribeWorkerDeploymentVersionResponse"\xca\x02\x82\xd3\xe4\x93\x02\xf7\x01\x12u/namespaces/{namespace}/worker-deployment-versions/{deployment_version.deployment_name}/{deployment_version.build_id}Z~\x12|/api/v1/namespaces/{namespace}/worker-deployment-versions/{deployment_version.deployment_name}/{deployment_version.build_id}\x8a\x9d\xcc\x1bG\n\x14temporal-resource-id\x12/deployment:{deployment_version.deployment_name}\x12\xdf\x01\n\x0fListDeployments\x12\x37.temporal.api.workflowservice.v1.ListDeploymentsRequest\x1a\x38.temporal.api.workflowservice.v1.ListDeploymentsResponse"Y\x82\xd3\xe4\x93\x02S\x12#/namespaces/{namespace}/deploymentsZ,\x12*/api/v1/namespaces/{namespace}/deployments\x12\xf7\x02\n\x19GetDeploymentReachability\x12\x41.temporal.api.workflowservice.v1.GetDeploymentReachabilityRequest\x1a\x42.temporal.api.workflowservice.v1.GetDeploymentReachabilityResponse"\xd2\x01\x82\xd3\xe4\x93\x02\xcb\x01\x12_/namespaces/{namespace}/deployments/{deployment.series_name}/{deployment.build_id}/reachabilityZh\x12\x66/api/v1/namespaces/{namespace}/deployments/{deployment.series_name}/{deployment.build_id}/reachability\x12\x99\x02\n\x14GetCurrentDeployment\x12<.temporal.api.workflowservice.v1.GetCurrentDeploymentRequest\x1a=.temporal.api.workflowservice.v1.GetCurrentDeploymentResponse"\x83\x01\x82\xd3\xe4\x93\x02}\x12\x38/namespaces/{namespace}/current-deployment/{series_name}ZA\x12?/api/v1/namespaces/{namespace}/current-deployment/{series_name}\x12\xb6\x02\n\x14SetCurrentDeployment\x12<.temporal.api.workflowservice.v1.SetCurrentDeploymentRequest\x1a=.temporal.api.workflowservice.v1.SetCurrentDeploymentResponse"\xa0\x01\x82\xd3\xe4\x93\x02\x99\x01"C/namespaces/{namespace}/current-deployment/{deployment.series_name}:\x01*ZO"J/api/v1/namespaces/{namespace}/current-deployment/{deployment.series_name}:\x01*\x12\xb0\x03\n!SetWorkerDeploymentCurrentVersion\x12I.temporal.api.workflowservice.v1.SetWorkerDeploymentCurrentVersionRequest\x1aJ.temporal.api.workflowservice.v1.SetWorkerDeploymentCurrentVersionResponse"\xf3\x01\x82\xd3\xe4\x93\x02\xb3\x01"P/namespaces/{namespace}/worker-deployments/{deployment_name}/set-current-version:\x01*Z\\"W/api/v1/namespaces/{namespace}/worker-deployments/{deployment_name}/set-current-version:\x01*\x8a\x9d\xcc\x1b\x34\n\x14temporal-resource-id\x12\x1c\x64\x65ployment:{deployment_name}\x12\xe7\x02\n\x18\x44\x65scribeWorkerDeployment\x12@.temporal.api.workflowservice.v1.DescribeWorkerDeploymentRequest\x1a\x41.temporal.api.workflowservice.v1.DescribeWorkerDeploymentResponse"\xc5\x01\x82\xd3\xe4\x93\x02\x85\x01\x12.temporal.api.workflowservice.v1.DeleteWorkerDeploymentRequest\x1a?.temporal.api.workflowservice.v1.DeleteWorkerDeploymentResponse"\xc5\x01\x82\xd3\xe4\x93\x02\x85\x01*.temporal.api.workflowservice.v1.ListWorkerDeploymentsResponse"g\x82\xd3\xe4\x93\x02\x61\x12*/namespaces/{namespace}/worker-deploymentsZ3\x12\x31/api/v1/namespaces/{namespace}/worker-deployments\x12\xae\x02\n\x16\x43reateWorkerDeployment\x12>.temporal.api.workflowservice.v1.CreateWorkerDeploymentRequest\x1a?.temporal.api.workflowservice.v1.CreateWorkerDeploymentResponse"\x92\x01\x82\xd3\xe4\x93\x02\x8b\x01".temporal.api.workflowservice.v1.DescribeBatchOperationRequest\x1a?.temporal.api.workflowservice.v1.DescribeBatchOperationResponse"\xa0\x01\x82\xd3\xe4\x93\x02o\x12\x31/namespaces/{namespace}/batch-operations/{job_id}Z:\x12\x38/api/v1/namespaces/{namespace}/batch-operations/{job_id}\x8a\x9d\xcc\x1b&\n\x14temporal-resource-id\x12\x0e\x62\x61tch:{job_id}\x12\xf5\x01\n\x13ListBatchOperations\x12;.temporal.api.workflowservice.v1.ListBatchOperationsRequest\x1a<.temporal.api.workflowservice.v1.ListBatchOperationsResponse"c\x82\xd3\xe4\x93\x02]\x12(/namespaces/{namespace}/batch-operationsZ1\x12//api/v1/namespaces/{namespace}/batch-operations\x12\xc4\x01\n\x12PollNexusTaskQueue\x12:.temporal.api.workflowservice.v1.PollNexusTaskQueueRequest\x1a;.temporal.api.workflowservice.v1.PollNexusTaskQueueResponse"5\x8a\x9d\xcc\x1b\x30\n\x14temporal-resource-id\x12\x18poller:{poller_group_id}\x12\xd9\x01\n\x19RespondNexusTaskCompleted\x12\x41.temporal.api.workflowservice.v1.RespondNexusTaskCompletedRequest\x1a\x42.temporal.api.workflowservice.v1.RespondNexusTaskCompletedResponse"5\x8a\x9d\xcc\x1b\x30\n\x14temporal-resource-id\x12\x18poller:{poller_group_id}\x12\xd0\x01\n\x16RespondNexusTaskFailed\x12>.temporal.api.workflowservice.v1.RespondNexusTaskFailedRequest\x1a?.temporal.api.workflowservice.v1.RespondNexusTaskFailedResponse"5\x8a\x9d\xcc\x1b\x30\n\x14temporal-resource-id\x12\x18poller:{poller_group_id}\x12\xe8\x02\n\x15UpdateActivityOptions\x12=.temporal.api.workflowservice.v1.UpdateActivityOptionsRequest\x1a>.temporal.api.workflowservice.v1.UpdateActivityOptionsResponse"\xcf\x01\x82\xd3\xe4\x93\x02\x8b\x01".temporal.api.workflowservice.v1.RecordWorkerHeartbeatResponse"\x95\x01\x82\xd3\xe4\x93\x02\x65")/namespaces/{namespace}/workers/heartbeat:\x01*Z5"0/api/v1/namespaces/{namespace}/workers/heartbeat:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xcb\x01\n\x0bListWorkers\x12\x33.temporal.api.workflowservice.v1.ListWorkersRequest\x1a\x34.temporal.api.workflowservice.v1.ListWorkersResponse"Q\x82\xd3\xe4\x93\x02K\x12\x1f/namespaces/{namespace}/workersZ(\x12&/api/v1/namespaces/{namespace}/workers\x12\xd8\x01\n\x0c\x43ountWorkers\x12\x34.temporal.api.workflowservice.v1.CountWorkersRequest\x1a\x35.temporal.api.workflowservice.v1.CountWorkersResponse"[\x82\xd3\xe4\x93\x02U\x12$/namespaces/{namespace}/worker-countZ-\x12+/api/v1/namespaces/{namespace}/worker-count\x12\xe2\x02\n\x15UpdateTaskQueueConfig\x12=.temporal.api.workflowservice.v1.UpdateTaskQueueConfigRequest\x1a>.temporal.api.workflowservice.v1.UpdateTaskQueueConfigResponse"\xc9\x01\x82\xd3\xe4\x93\x02\x8f\x01">/namespaces/{namespace}/task-queues/{task_queue}/update-config:\x01*ZJ"E/api/v1/namespaces/{namespace}/task-queues/{task_queue}/update-config:\x01*\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16taskqueue:{task_queue}\x12\xa8\x02\n\x11\x46\x65tchWorkerConfig\x12\x39.temporal.api.workflowservice.v1.FetchWorkerConfigRequest\x1a:.temporal.api.workflowservice.v1.FetchWorkerConfigResponse"\x9b\x01\x82\xd3\xe4\x93\x02k",/namespaces/{namespace}/workers/fetch-config:\x01*Z8"3/api/v1/namespaces/{namespace}/workers/fetch-config:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xad\x02\n\x12UpdateWorkerConfig\x12:.temporal.api.workflowservice.v1.UpdateWorkerConfigRequest\x1a;.temporal.api.workflowservice.v1.UpdateWorkerConfigResponse"\x9d\x01\x82\xd3\xe4\x93\x02m"-/namespaces/{namespace}/workers/update-config:\x01*Z9"4/api/v1/namespaces/{namespace}/workers/update-config:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xcd\x02\n\x0e\x44\x65scribeWorker\x12\x36.temporal.api.workflowservice.v1.DescribeWorkerRequest\x1a\x37.temporal.api.workflowservice.v1.DescribeWorkerResponse"\xc9\x01\x82\xd3\xe4\x93\x02\x89\x01\x12>/namespaces/{namespace}/workers/describe/{worker_instance_key}ZG\x12\x45/api/v1/namespaces/{namespace}/workers/describe/{worker_instance_key}\x8a\x9d\xcc\x1b\x34\n\x14temporal-resource-id\x12\x1cworker:{worker_instance_key}\x12\xd2\x02\n\x16PauseWorkflowExecution\x12>.temporal.api.workflowservice.v1.PauseWorkflowExecutionRequest\x1a?.temporal.api.workflowservice.v1.PauseWorkflowExecutionResponse"\xb6\x01\x82\xd3\xe4\x93\x02}"5/namespaces/{namespace}/workflows/{workflow_id}/pause:\x01*ZA"/api/v1/namespaces/{namespace}/workflows/{workflow_id}/unpause:\x01*\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16workflow:{workflow_id}\x12\xc8\x02\n\x16StartActivityExecution\x12>.temporal.api.workflowservice.v1.StartActivityExecutionRequest\x1a?.temporal.api.workflowservice.v1.StartActivityExecutionResponse"\xac\x01\x82\xd3\xe4\x93\x02s"0/namespaces/{namespace}/activities/{activity_id}:\x01*Z<"7/api/v1/namespaces/{namespace}/activities/{activity_id}:\x01*\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16\x61\x63tivity:{activity_id}\x12\xb6\x02\n\x1cStartNexusOperationExecution\x12\x44.temporal.api.workflowservice.v1.StartNexusOperationExecutionRequest\x1a\x45.temporal.api.workflowservice.v1.StartNexusOperationExecutionResponse"\x88\x01\x82\xd3\xe4\x93\x02\x81\x01"7/namespaces/{namespace}/nexus-operations/{operation_id}:\x01*ZC">/api/v1/namespaces/{namespace}/nexus-operations/{operation_id}:\x01*\x12\xcb\x02\n\x19\x44\x65scribeActivityExecution\x12\x41.temporal.api.workflowservice.v1.DescribeActivityExecutionRequest\x1a\x42.temporal.api.workflowservice.v1.DescribeActivityExecutionResponse"\xa6\x01\x82\xd3\xe4\x93\x02m\x12\x30/namespaces/{namespace}/activities/{activity_id}Z9\x12\x37/api/v1/namespaces/{namespace}/activities/{activity_id}\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16\x61\x63tivity:{activity_id}\x12\xb8\x02\n\x1f\x44\x65scribeNexusOperationExecution\x12G.temporal.api.workflowservice.v1.DescribeNexusOperationExecutionRequest\x1aH.temporal.api.workflowservice.v1.DescribeNexusOperationExecutionResponse"\x81\x01\x82\xd3\xe4\x93\x02{\x12\x37/namespaces/{namespace}/nexus-operations/{operation_id}Z@\x12>/api/v1/namespaces/{namespace}/nexus-operations/{operation_id}\x12\xcf\x02\n\x15PollActivityExecution\x12=.temporal.api.workflowservice.v1.PollActivityExecutionRequest\x1a>.temporal.api.workflowservice.v1.PollActivityExecutionResponse"\xb6\x01\x82\xd3\xe4\x93\x02}\x12\x38/namespaces/{namespace}/activities/{activity_id}/outcomeZA\x12?/api/v1/namespaces/{namespace}/activities/{activity_id}/outcome\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16\x61\x63tivity:{activity_id}\x12\xb7\x02\n\x1bPollNexusOperationExecution\x12\x43.temporal.api.workflowservice.v1.PollNexusOperationExecutionRequest\x1a\x44.temporal.api.workflowservice.v1.PollNexusOperationExecutionResponse"\x8c\x01\x82\xd3\xe4\x93\x02\x85\x01\x12.temporal.api.workflowservice.v1.ListActivityExecutionsRequest\x1a?.temporal.api.workflowservice.v1.ListActivityExecutionsResponse"W\x82\xd3\xe4\x93\x02Q\x12"/namespaces/{namespace}/activitiesZ+\x12)/api/v1/namespaces/{namespace}/activities\x12\x90\x02\n\x1cListNexusOperationExecutions\x12\x44.temporal.api.workflowservice.v1.ListNexusOperationExecutionsRequest\x1a\x45.temporal.api.workflowservice.v1.ListNexusOperationExecutionsResponse"c\x82\xd3\xe4\x93\x02]\x12(/namespaces/{namespace}/nexus-operationsZ1\x12//api/v1/namespaces/{namespace}/nexus-operations\x12\xfd\x01\n\x17\x43ountActivityExecutions\x12?.temporal.api.workflowservice.v1.CountActivityExecutionsRequest\x1a@.temporal.api.workflowservice.v1.CountActivityExecutionsResponse"_\x82\xd3\xe4\x93\x02Y\x12&/namespaces/{namespace}/activity-countZ/\x12-/api/v1/namespaces/{namespace}/activity-count\x12\x9d\x02\n\x1d\x43ountNexusOperationExecutions\x12\x45.temporal.api.workflowservice.v1.CountNexusOperationExecutionsRequest\x1a\x46.temporal.api.workflowservice.v1.CountNexusOperationExecutionsResponse"m\x82\xd3\xe4\x93\x02g\x12-/namespaces/{namespace}/nexus-operation-countZ6\x12\x34/api/v1/namespaces/{namespace}/nexus-operation-count\x12\xef\x02\n\x1eRequestCancelActivityExecution\x12\x46.temporal.api.workflowservice.v1.RequestCancelActivityExecutionRequest\x1aG.temporal.api.workflowservice.v1.RequestCancelActivityExecutionResponse"\xbb\x01\x82\xd3\xe4\x93\x02\x81\x01"7/namespaces/{namespace}/activities/{activity_id}/cancel:\x01*ZC">/api/v1/namespaces/{namespace}/activities/{activity_id}/cancel:\x01*\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16\x61\x63tivity:{activity_id}\x12\xdc\x02\n$RequestCancelNexusOperationExecution\x12L.temporal.api.workflowservice.v1.RequestCancelNexusOperationExecutionRequest\x1aM.temporal.api.workflowservice.v1.RequestCancelNexusOperationExecutionResponse"\x96\x01\x82\xd3\xe4\x93\x02\x8f\x01">/namespaces/{namespace}/nexus-operations/{operation_id}/cancel:\x01*ZJ"E/api/v1/namespaces/{namespace}/nexus-operations/{operation_id}/cancel:\x01*\x12\xe9\x02\n\x1aTerminateActivityExecution\x12\x42.temporal.api.workflowservice.v1.TerminateActivityExecutionRequest\x1a\x43.temporal.api.workflowservice.v1.TerminateActivityExecutionResponse"\xc1\x01\x82\xd3\xe4\x93\x02\x87\x01":/namespaces/{namespace}/activities/{activity_id}/terminate:\x01*ZF"A/api/v1/namespaces/{namespace}/activities/{activity_id}/terminate:\x01*\x8a\x9d\xcc\x1b.\n\x14temporal-resource-id\x12\x16\x61\x63tivity:{activity_id}\x12\x9e\x01\n\x17\x44\x65leteActivityExecution\x12?.temporal.api.workflowservice.v1.DeleteActivityExecutionRequest\x1a@.temporal.api.workflowservice.v1.DeleteActivityExecutionResponse"\x00\x12\xfd\x03\n\x16PauseActivityExecution\x12>.temporal.api.workflowservice.v1.PauseActivityExecutionRequest\x1a?.temporal.api.workflowservice.v1.PauseActivityExecutionResponse"\xe1\x02\x82\xd3\xe4\x93\x02\xb0\x02"6/namespaces/{namespace}/activities/{activity_id}/pause:\x01*ZB"=/api/v1/namespaces/{namespace}/activities/{activity_id}/pause:\x01*ZS"N/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/pause:\x01*ZZ"U/api/v1/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/pause:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xfd\x03\n\x16ResetActivityExecution\x12>.temporal.api.workflowservice.v1.ResetActivityExecutionRequest\x1a?.temporal.api.workflowservice.v1.ResetActivityExecutionResponse"\xe1\x02\x82\xd3\xe4\x93\x02\xb0\x02"6/namespaces/{namespace}/activities/{activity_id}/reset:\x01*ZB"=/api/v1/namespaces/{namespace}/activities/{activity_id}/reset:\x01*ZS"N/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/reset:\x01*ZZ"U/api/v1/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/reset:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\x8b\x04\n\x18UnpauseActivityExecution\x12@.temporal.api.workflowservice.v1.UnpauseActivityExecutionRequest\x1a\x41.temporal.api.workflowservice.v1.UnpauseActivityExecutionResponse"\xe9\x02\x82\xd3\xe4\x93\x02\xb8\x02"8/namespaces/{namespace}/activities/{activity_id}/unpause:\x01*ZD"?/api/v1/namespaces/{namespace}/activities/{activity_id}/unpause:\x01*ZU"P/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/unpause:\x01*Z\\"W/api/v1/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/unpause:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xb9\x04\n\x1eUpdateActivityExecutionOptions\x12\x46.temporal.api.workflowservice.v1.UpdateActivityExecutionOptionsRequest\x1aG.temporal.api.workflowservice.v1.UpdateActivityExecutionOptionsResponse"\x85\x03\x82\xd3\xe4\x93\x02\xd4\x02"?/namespaces/{namespace}/activities/{activity_id}/update-options:\x01*ZK"F/api/v1/namespaces/{namespace}/activities/{activity_id}/update-options:\x01*Z\\"W/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/update-options:\x01*Zc"^/api/v1/namespaces/{namespace}/workflows/{workflow_id}/activities/{activity_id}/update-options:\x01*\x8a\x9d\xcc\x1b%\n\x14temporal-resource-id\x12\r{resource_id}\x12\xd6\x02\n TerminateNexusOperationExecution\x12H.temporal.api.workflowservice.v1.TerminateNexusOperationExecutionRequest\x1aI.temporal.api.workflowservice.v1.TerminateNexusOperationExecutionResponse"\x9c\x01\x82\xd3\xe4\x93\x02\x95\x01"A/namespaces/{namespace}/nexus-operations/{operation_id}/terminate:\x01*ZM"H/api/v1/namespaces/{namespace}/nexus-operations/{operation_id}/terminate:\x01*\x12\xb0\x01\n\x1d\x44\x65leteNexusOperationExecution\x12\x45.temporal.api.workflowservice.v1.DeleteNexusOperationExecutionRequest\x1a\x46.temporal.api.workflowservice.v1.DeleteNexusOperationExecutionResponse"\x00\x12\xc1\x03\n!PollWorkflowExecutionTimeSkipping\x12I.temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingRequest\x1aJ.temporal.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingResponse"\x84\x02\x82\xd3\xe4\x93\x02\xb7\x01\x12U/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/time-skipping/pollZ^\x12\\/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/time-skipping/poll\x8a\x9d\xcc\x1b\x41\n\x14temporal-resource-id\x12)workflow:{workflow_execution.workflow_id}B\xb6\x01\n"io.temporal.api.workflowservice.v1B\x0cServiceProtoP\x01Z5go.temporal.io/api/workflowservice/v1;workflowservice\xaa\x02!Temporalio.Api.WorkflowService.V1\xea\x02$Temporalio::Api::WorkflowService::V1b\x06proto3' ) @@ -417,6 +417,10 @@ _WORKFLOWSERVICE.methods_by_name[ "ListWorkers" ]._serialized_options = b"\202\323\344\223\002K\022\037/namespaces/{namespace}/workersZ(\022&/api/v1/namespaces/{namespace}/workers" + _WORKFLOWSERVICE.methods_by_name["CountWorkers"]._options = None + _WORKFLOWSERVICE.methods_by_name[ + "CountWorkers" + ]._serialized_options = b"\202\323\344\223\002U\022$/namespaces/{namespace}/worker-countZ-\022+/api/v1/namespaces/{namespace}/worker-count" _WORKFLOWSERVICE.methods_by_name["UpdateTaskQueueConfig"]._options = None _WORKFLOWSERVICE.methods_by_name[ "UpdateTaskQueueConfig" @@ -515,6 +519,12 @@ _WORKFLOWSERVICE.methods_by_name[ "TerminateNexusOperationExecution" ]._serialized_options = b'\202\323\344\223\002\225\001"A/namespaces/{namespace}/nexus-operations/{operation_id}/terminate:\001*ZM"H/api/v1/namespaces/{namespace}/nexus-operations/{operation_id}/terminate:\001*' + _WORKFLOWSERVICE.methods_by_name[ + "PollWorkflowExecutionTimeSkipping" + ]._options = None + _WORKFLOWSERVICE.methods_by_name[ + "PollWorkflowExecutionTimeSkipping" + ]._serialized_options = b"\202\323\344\223\002\267\001\022U/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/time-skipping/pollZ^\022\\/api/v1/namespaces/{namespace}/workflows/{workflow_execution.workflow_id}/time-skipping/poll\212\235\314\033A\n\024temporal-resource-id\022)workflow:{workflow_execution.workflow_id}" _WORKFLOWSERVICE._serialized_start = 250 - _WORKFLOWSERVICE._serialized_end = 38839 + _WORKFLOWSERVICE._serialized_end = 39510 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/api/workflowservice/v1/service_pb2_grpc.py b/temporalio/api/workflowservice/v1/service_pb2_grpc.py index f0fcc6730..486c6a394 100644 --- a/temporalio/api/workflowservice/v1/service_pb2_grpc.py +++ b/temporalio/api/workflowservice/v1/service_pb2_grpc.py @@ -503,6 +503,11 @@ def __init__(self, channel): request_serializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.ListWorkersRequest.SerializeToString, response_deserializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.ListWorkersResponse.FromString, ) + self.CountWorkers = channel.unary_unary( + "/temporal.api.workflowservice.v1.WorkflowService/CountWorkers", + request_serializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.CountWorkersRequest.SerializeToString, + response_deserializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.CountWorkersResponse.FromString, + ) self.UpdateTaskQueueConfig = channel.unary_unary( "/temporal.api.workflowservice.v1.WorkflowService/UpdateTaskQueueConfig", request_serializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.UpdateTaskQueueConfigRequest.SerializeToString, @@ -633,6 +638,11 @@ def __init__(self, channel): request_serializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.DeleteNexusOperationExecutionRequest.SerializeToString, response_deserializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.DeleteNexusOperationExecutionResponse.FromString, ) + self.PollWorkflowExecutionTimeSkipping = channel.unary_unary( + "/temporal.api.workflowservice.v1.WorkflowService/PollWorkflowExecutionTimeSkipping", + request_serializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.PollWorkflowExecutionTimeSkippingRequest.SerializeToString, + response_deserializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.PollWorkflowExecutionTimeSkippingResponse.FromString, + ) class WorkflowServiceServicer(object): @@ -1650,6 +1660,12 @@ def ListWorkers(self, request, context): context.set_details("Method not implemented!") raise NotImplementedError("Method not implemented!") + def CountWorkers(self, request, context): + """CountWorkers counts the number of workers in a specific namespace.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") + def UpdateTaskQueueConfig(self, request, context): """Updates task queue configuration. For the overall queue rate limit: the rate limit set by this api overrides the worker-set rate limit, @@ -1915,6 +1931,12 @@ def DeleteNexusOperationExecution(self, request, context): context.set_details("Method not implemented!") raise NotImplementedError("Method not implemented!") + def PollWorkflowExecutionTimeSkipping(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") + def add_WorkflowServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -2393,6 +2415,11 @@ def add_WorkflowServiceServicer_to_server(servicer, server): request_deserializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.ListWorkersRequest.FromString, response_serializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.ListWorkersResponse.SerializeToString, ), + "CountWorkers": grpc.unary_unary_rpc_method_handler( + servicer.CountWorkers, + request_deserializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.CountWorkersRequest.FromString, + response_serializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.CountWorkersResponse.SerializeToString, + ), "UpdateTaskQueueConfig": grpc.unary_unary_rpc_method_handler( servicer.UpdateTaskQueueConfig, request_deserializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.UpdateTaskQueueConfigRequest.FromString, @@ -2523,6 +2550,11 @@ def add_WorkflowServiceServicer_to_server(servicer, server): request_deserializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.DeleteNexusOperationExecutionRequest.FromString, response_serializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.DeleteNexusOperationExecutionResponse.SerializeToString, ), + "PollWorkflowExecutionTimeSkipping": grpc.unary_unary_rpc_method_handler( + servicer.PollWorkflowExecutionTimeSkipping, + request_deserializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.PollWorkflowExecutionTimeSkippingRequest.FromString, + response_serializer=temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.PollWorkflowExecutionTimeSkippingResponse.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( "temporal.api.workflowservice.v1.WorkflowService", rpc_method_handlers @@ -5300,6 +5332,35 @@ def ListWorkers( metadata, ) + @staticmethod + def CountWorkers( + 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, + "/temporal.api.workflowservice.v1.WorkflowService/CountWorkers", + temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.CountWorkersRequest.SerializeToString, + temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.CountWorkersResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + ) + @staticmethod def UpdateTaskQueueConfig( request, @@ -6053,3 +6114,32 @@ def DeleteNexusOperationExecution( timeout, metadata, ) + + @staticmethod + def PollWorkflowExecutionTimeSkipping( + 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, + "/temporal.api.workflowservice.v1.WorkflowService/PollWorkflowExecutionTimeSkipping", + temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.PollWorkflowExecutionTimeSkippingRequest.SerializeToString, + temporal_dot_api_dot_workflowservice_dot_v1_dot_request__response__pb2.PollWorkflowExecutionTimeSkippingResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + ) diff --git a/temporalio/api/workflowservice/v1/service_pb2_grpc.pyi b/temporalio/api/workflowservice/v1/service_pb2_grpc.pyi index d6d94abb3..d25f044b4 100644 --- a/temporalio/api/workflowservice/v1/service_pb2_grpc.pyi +++ b/temporalio/api/workflowservice/v1/service_pb2_grpc.pyi @@ -930,6 +930,11 @@ class WorkflowServiceStub: temporalio.api.workflowservice.v1.request_response_pb2.ListWorkersResponse, ] """ListWorkers is a visibility API to list worker status information in a specific namespace.""" + CountWorkers: grpc.UnaryUnaryMultiCallable[ + temporalio.api.workflowservice.v1.request_response_pb2.CountWorkersRequest, + temporalio.api.workflowservice.v1.request_response_pb2.CountWorkersResponse, + ] + """CountWorkers counts the number of workers in a specific namespace.""" UpdateTaskQueueConfig: grpc.UnaryUnaryMultiCallable[ temporalio.api.workflowservice.v1.request_response_pb2.UpdateTaskQueueConfigRequest, temporalio.api.workflowservice.v1.request_response_pb2.UpdateTaskQueueConfigResponse, @@ -1169,6 +1174,10 @@ class WorkflowServiceStub: (-- api-linter: core::0127::http-annotation=disabled aip.dev/not-precedent: Nexus operation deletion not exposed to HTTP, users should use cancel or terminate. --) """ + PollWorkflowExecutionTimeSkipping: grpc.UnaryUnaryMultiCallable[ + temporalio.api.workflowservice.v1.request_response_pb2.PollWorkflowExecutionTimeSkippingRequest, + temporalio.api.workflowservice.v1.request_response_pb2.PollWorkflowExecutionTimeSkippingResponse, + ] class WorkflowServiceServicer(metaclass=abc.ABCMeta): """WorkflowService API defines how Temporal SDKs and other clients interact with the Temporal server @@ -2291,6 +2300,13 @@ class WorkflowServiceServicer(metaclass=abc.ABCMeta): ) -> temporalio.api.workflowservice.v1.request_response_pb2.ListWorkersResponse: """ListWorkers is a visibility API to list worker status information in a specific namespace.""" @abc.abstractmethod + def CountWorkers( + self, + request: temporalio.api.workflowservice.v1.request_response_pb2.CountWorkersRequest, + context: grpc.ServicerContext, + ) -> temporalio.api.workflowservice.v1.request_response_pb2.CountWorkersResponse: + """CountWorkers counts the number of workers in a specific namespace.""" + @abc.abstractmethod def UpdateTaskQueueConfig( self, request: temporalio.api.workflowservice.v1.request_response_pb2.UpdateTaskQueueConfigRequest, @@ -2583,6 +2599,12 @@ class WorkflowServiceServicer(metaclass=abc.ABCMeta): (-- api-linter: core::0127::http-annotation=disabled aip.dev/not-precedent: Nexus operation deletion not exposed to HTTP, users should use cancel or terminate. --) """ + @abc.abstractmethod + def PollWorkflowExecutionTimeSkipping( + self, + request: temporalio.api.workflowservice.v1.request_response_pb2.PollWorkflowExecutionTimeSkippingRequest, + context: grpc.ServicerContext, + ) -> temporalio.api.workflowservice.v1.request_response_pb2.PollWorkflowExecutionTimeSkippingResponse: ... def add_WorkflowServiceServicer_to_server( servicer: WorkflowServiceServicer, server: grpc.Server diff --git a/temporalio/bridge/Cargo.lock b/temporalio/bridge/Cargo.lock index b71dcd615..0c00e8ec5 100644 --- a/temporalio/bridge/Cargo.lock +++ b/temporalio/bridge/Cargo.lock @@ -31,19 +31,19 @@ checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -58,26 +58,44 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "aws-lc-fips-sys" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "118303cd75f63d1933a90c2ceb7e697281ac6acbdbcc490b46419f25a527ab90" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", + "regex", +] + [[package]] name = "aws-lc-rs" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" dependencies = [ + "aws-lc-fips-sys", "aws-lc-sys", + "untrusted 0.7.1", "zeroize", ] [[package]] name = "aws-lc-sys" -version = "0.41.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" dependencies = [ "cc", "cmake", "dunce", "fs_extra", + "pkg-config", ] [[package]] @@ -124,14 +142,12 @@ dependencies = [ ] [[package]] -name = "backoff" -version = "0.4.0" +name = "backon" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" dependencies = [ - "getrandom 0.2.17", - "instant", - "rand 0.8.6", + "fastrand", ] [[package]] @@ -140,11 +156,31 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex 1.3.0", + "syn 2.0.119", +] + [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "bon" @@ -168,7 +204,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.119", ] [[package]] @@ -179,9 +215,9 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytes" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "bzip2" @@ -194,14 +230,23 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.65" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" dependencies = [ "find-msvc-tools", "jobserver", "libc", - "shlex", + "shlex 2.0.1", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", ] [[package]] @@ -212,9 +257,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" @@ -237,6 +282,17 @@ dependencies = [ "serde", ] +[[package]] +name = "clang-sys" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "cmake" version = "0.1.58" @@ -301,18 +357,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "darling" @@ -334,7 +390,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.119", ] [[package]] @@ -345,7 +401,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -367,7 +423,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn", + "syn 2.0.119", "unicode-xid", ] @@ -389,7 +445,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -400,7 +456,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -423,9 +479,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" [[package]] name = "enum-iterator" @@ -444,7 +500,7 @@ checksum = "685adfa4d6f3d765a26bc5dbc936577de9abf756c1feeb3089b01dd395034842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -456,7 +512,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -483,14 +539,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "filetime" @@ -569,9 +625,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" dependencies = [ "futures-channel", "futures-core", @@ -584,9 +640,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" dependencies = [ "futures-core", "futures-sink", @@ -594,15 +650,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" dependencies = [ "futures-core", "futures-task", @@ -611,19 +667,19 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -639,21 +695,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" dependencies = [ "futures-channel", "futures-core", @@ -696,11 +752,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 5.3.0", "wasip2", - "wasm-bindgen", ] [[package]] @@ -710,11 +764,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", + "wasm-bindgen", ] +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + [[package]] name = "h2" version = "0.4.15" @@ -772,9 +834,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", "http", @@ -782,9 +844,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" dependencies = [ "bytes", "futures-core", @@ -807,9 +869,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" dependencies = [ "atomic-waker", "bytes", @@ -997,15 +1059,6 @@ dependencies = [ "hashbrown 0.17.1", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "inventory" version = "0.3.24" @@ -1021,6 +1074,15 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -1063,7 +1125,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn", + "syn 2.0.119", ] [[package]] @@ -1082,16 +1144,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] @@ -1120,15 +1182,25 @@ checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] [[package]] name = "libredox" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ "libc", ] @@ -1162,9 +1234,9 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "lru" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a860605968fce16869fd239cf4237a82f3ac470723415db603b0e8b6c8d4fb9" +checksum = "0b6180140927ee907000b0aa540091f6ea512ead4447c92b8fc35bc72788a5a6" dependencies = [ "hashbrown 0.17.1", ] @@ -1192,9 +1264,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "mime" @@ -1202,6 +1274,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1214,9 +1292,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "wasi", @@ -1246,7 +1324,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1255,6 +1333,16 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "ntapi" version = "0.4.3" @@ -1270,7 +1358,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1336,7 +1424,7 @@ dependencies = [ "bytes", "http", "opentelemetry", - "reqwest 0.13.4", + "reqwest", ] [[package]] @@ -1351,7 +1439,7 @@ dependencies = [ "opentelemetry-proto", "opentelemetry_sdk", "prost", - "reqwest 0.13.4", + "reqwest", "thiserror", "tokio", "tonic", @@ -1383,7 +1471,7 @@ dependencies = [ "opentelemetry", "percent-encoding", "portable-atomic", - "rand 0.9.4", + "rand 0.9.5", "thiserror", "tokio", "tokio-stream", @@ -1435,7 +1523,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ed4d5c6ae95e08ac768883c8401cf0e8deb4e6e1d6a4e1fd3d2ec4f0ec63200" dependencies = [ "heck", - "itertools", + "itertools 0.14.0", "prost", "prost-types", ] @@ -1483,7 +1571,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1500,9 +1588,9 @@ checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" [[package]] name = "portable-atomic-util" @@ -1564,14 +1652,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.119", ] [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -1607,7 +1695,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ "heck", - "itertools", + "itertools 0.14.0", "log", "multimap", "petgraph", @@ -1617,7 +1705,7 @@ dependencies = [ "pulldown-cmark", "pulldown-cmark-to-cmark", "regex", - "syn", + "syn 2.0.119", "tempfile", ] @@ -1628,10 +1716,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", - "itertools", + "itertools 0.14.0", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1767,7 +1855,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1779,7 +1867,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1814,15 +1902,16 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.15" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ "aws-lc-rs", "bytes", - "getrandom 0.3.4", + "getrandom 0.4.3", "lru-slab", - "rand 0.9.4", + "rand 0.10.2", + "rand_pcg", "ring", "rustc-hash", "rustls", @@ -1836,23 +1925,23 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ "cfg_aliases", "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -1871,46 +1960,25 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ - "rand_chacha 0.9.0", + "rand_chacha", "rand_core 0.9.5", ] [[package]] name = "rand" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" dependencies = [ "chacha20", "getrandom 0.4.3", "rand_core 0.10.1", ] -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - [[package]] name = "rand_chacha" version = "0.9.0" @@ -1921,15 +1989,6 @@ dependencies = [ "rand_core 0.9.5", ] -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - [[package]] name = "rand_core" version = "0.9.5" @@ -1945,6 +2004,15 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -1967,9 +2035,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.4" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -1979,9 +2047,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" dependencies = [ "aho-corasick", "memchr", @@ -1994,38 +2062,6 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64", - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "reqwest" version = "0.13.4" @@ -2078,15 +2114,15 @@ dependencies = [ "cfg-if", "getrandom 0.2.17", "libc", - "untrusted", + "untrusted 0.9.0", "windows-sys 0.52.0", ] [[package]] name = "ringbuf" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3ecbcab081b935fb9c618b07654924f27686b4aac8818e700580a83eedcb7f" +checksum = "a158e09ede21a14b172ca6cdd6208386c6ae2cb6acef58d774368ef8c450dfa7" dependencies = [ "crossbeam-utils", "portable-atomic", @@ -2095,9 +2131,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc_version" @@ -2118,14 +2154,14 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.41" +version = "0.23.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" dependencies = [ "aws-lc-rs", "log", @@ -2151,9 +2187,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "web-time", "zeroize", @@ -2177,7 +2213,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2195,14 +2231,14 @@ dependencies = [ "aws-lc-rs", "ring", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "ryu" @@ -2265,9 +2301,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -2275,29 +2311,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -2336,6 +2372,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "shlex" version = "2.0.1" @@ -2354,15 +2396,15 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "simd_cesu8" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" dependencies = [ "rustc_version", "simdutf8", @@ -2403,12 +2445,12 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2431,9 +2473,20 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.118" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" dependencies = [ "proc-macro2", "quote", @@ -2457,7 +2510,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2501,7 +2554,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2510,6 +2563,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "aws-lc-rs", "futures", "prost", "pyo3", @@ -2519,6 +2573,7 @@ dependencies = [ "temporalio-common", "temporalio-sdk-core", "tokio", + "tokio-rustls", "tokio-stream", "tonic", "tracing", @@ -2527,11 +2582,11 @@ dependencies = [ [[package]] name = "temporalio-client" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "async-trait", - "backoff", + "backon", "base64", "bon", "bytes", @@ -2544,7 +2599,8 @@ dependencies = [ "hyper", "hyper-util", "parking_lot", - "rand 0.10.1", + "rand 0.10.2", + "serde_json", "temporalio-common", "thiserror", "tokio", @@ -2558,7 +2614,7 @@ dependencies = [ [[package]] name = "temporalio-common" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "async-trait", @@ -2579,8 +2635,9 @@ dependencies = [ "prometheus", "prost", "prost-types", - "reqwest 0.12.28", + "reqwest", "ringbuf", + "rustls", "serde", "serde_json", "temporalio-common-wasm", @@ -2598,17 +2655,19 @@ dependencies = [ [[package]] name = "temporalio-common-wasm" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "async-trait", "bon", + "chrono", "crc32fast", "derive_more", "erased-serde", "futures", "parking_lot", "prost", + "prost-wkt-types", "serde", "serde_json", "temporalio-protos", @@ -2621,16 +2680,16 @@ dependencies = [ [[package]] name = "temporalio-macros" -version = "0.5.0" +version = "0.6.0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "temporalio-protos" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "base64", @@ -2651,11 +2710,11 @@ dependencies = [ [[package]] name = "temporalio-sdk-core" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "async-trait", - "backoff", + "backon", "bon", "crossbeam-channel", "crossbeam-utils", @@ -2666,7 +2725,7 @@ dependencies = [ "futures", "futures-util", "gethostname", - "itertools", + "itertools 0.14.0", "lru", "mockall", "opentelemetry-otlp", @@ -2675,8 +2734,8 @@ dependencies = [ "pin-project", "prost", "prost-wkt-types", - "rand 0.10.1", - "reqwest 0.13.4", + "rand 0.10.2", + "reqwest", "serde", "serde_json", "siphasher", @@ -2705,29 +2764,29 @@ checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if", ] @@ -2744,9 +2803,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -2759,9 +2818,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", @@ -2776,13 +2835,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2797,9 +2856,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" dependencies = [ "futures-core", "pin-project-lite", @@ -2808,22 +2867,23 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ "bytes", "futures-core", "futures-sink", + "libc", "pin-project-lite", "tokio", ] [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c" dependencies = [ "indexmap", "serde_core", @@ -2854,9 +2914,9 @@ dependencies = [ [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tonic" @@ -2899,7 +2959,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2924,7 +2984,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn", + "syn 2.0.119", "tempfile", "tonic-build", ] @@ -3008,7 +3068,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3058,9 +3118,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typetag" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5a897b12c6c1151ad0b138b8db50252dc301f93bc3b027db05eec82aeed298c" +checksum = "c90e86058a30d42a1a928dfb4b49bb33c98c3a2b4909492e6b0881cd94798ec2" dependencies = [ "erased-serde", "inventory", @@ -3071,13 +3131,13 @@ dependencies = [ [[package]] name = "typetag-impl" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf808357c6ed7e13ba0f3277ec8d8f21b2d501274895104263985330c726c1c5" +checksum = "f153acc4e99a5f2a5aefa09fb078be54e26271b2813f6041200b224c098d8328" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -3104,6 +3164,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -3130,9 +3196,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.23.4" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ "getrandom 0.4.3", ] @@ -3225,7 +3291,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] @@ -3273,9 +3339,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" dependencies = [ "rustls-pki-types", ] @@ -3302,7 +3368,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3364,7 +3430,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3375,7 +3441,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3418,16 +3484,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -3445,31 +3502,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -3487,101 +3527,53 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" [[package]] name = "wit-bindgen" @@ -3624,28 +3616,28 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.52" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.52" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3665,7 +3657,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] @@ -3705,7 +3697,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3726,15 +3718,15 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977347db8caa080403f6b6b7c1cda9479a8e869316f7e13a59b19076a40f94e3" +checksum = "b142a20ec14a91d5bc708c1dc21b080c550113d8aa77afa29635673a65dd02c5" [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zopfli" diff --git a/temporalio/bridge/Cargo.toml b/temporalio/bridge/Cargo.toml index a1c6b5e3f..b9391a0bb 100644 --- a/temporalio/bridge/Cargo.toml +++ b/temporalio/bridge/Cargo.toml @@ -15,9 +15,36 @@ module-name = "temporalio.bridge.temporal_sdk_bridge" name = "temporal_sdk_bridge" crate-type = ["cdylib"] +[features] +# Default builds keep the historical `ring` rustls provider (unchanged behavior). +# A FIPS build — `maturin build --no-default-features --features fips` +# (TEMPORALIO_FIPS=1) — swaps the entire rustls stack onto aws-lc-rs in FIPS +# mode (aws-lc-fips-sys) and eliminates `ring`: the transitive core/client/common +# TLS *and* this crate's own tokio-rustls use in client.rs. Ported from +# temporalio/sdk-ruby PR #466. +# The extra `tokio-rustls/*` entry is sdk-python-specific: the bridge has its own +# direct tokio-rustls dep (client.rs custom verifier) that Ruby lacked, so its +# provider must track the build too — else `cargo tree -i ring` would still find ring. +default = ["tls-ring"] +tls-ring = [ + "temporalio-sdk-core/tls-ring", + "temporalio-client/tls-ring", + "tokio-rustls/ring", +] +tls-aws-lc = [ + "temporalio-sdk-core/tls-aws-lc", + "temporalio-client/tls-aws-lc", + "tokio-rustls/aws_lc_rs", +] +fips = ["tls-aws-lc", "dep:aws-lc-rs"] + [dependencies] anyhow = "1.0" async-trait = "0.1" +# Only compiled under `fips`. Not called directly — its presence flips the +# shared aws-lc-rs crate (rustls' aws_lc_rs provider) into FIPS mode, linking +# aws-lc-fips-sys instead of aws-lc-sys. Ported from sdk-ruby PR #466. +aws-lc-rs = { version = "1", features = ["fips"], optional = true } futures = "0.3" prost = "0.14" pyo3 = { version = "0.29", features = [ @@ -28,14 +55,29 @@ pyo3 = { version = "0.29", features = [ ] } pyo3-async-runtimes = { version = "0.29", features = ["tokio-runtime"] } pythonize = "0.29" -temporalio-client = { version = "0.5", path = "./sdk-core/crates/client" } -temporalio-common = { version = "0.5", path = "./sdk-core/crates/common", features = [ +# default-features disabled so the rustls provider isn't pinned to `tls-ring`; +# the bridge's tls-ring/tls-aws-lc features (above) select it. Re-add the +# crate's non-TLS default (`envconfig`). +temporalio-client = { version = "0.6", path = "./sdk-core/crates/client", default-features = false, features = [ + "envconfig", +] } +temporalio-common = { version = "0.6", path = "./sdk-core/crates/common", features = [ "envconfig", "otel" ]} -temporalio-sdk-core = { version = "0.5", path = "./sdk-core/crates/sdk-core", features = [ +# default-features disabled (drops the pinned `tls-ring`); re-add the non-TLS +# defaults the bridge relied on (`envconfig`, `prometheus`) plus `ephemeral-server`. +temporalio-sdk-core = { version = "0.6", path = "./sdk-core/crates/sdk-core", default-features = false, features = [ "ephemeral-server", + "envconfig", + "prometheus", ] } tokio = "1.26" +# Matches the sdk-core client crate's rustls stack; used to build the custom +# server certificate verifier for ClientTlsConfig.verification_server_name. +# Provider is NOT pinned here — the bridge's tls-ring/tls-aws-lc features select +# `ring` (default) or `aws_lc_rs` (fips), so this crate's direct rustls use in +# client.rs tracks the rest of the stack and the `cargo tree -i ring` FIPS gate passes. +tokio-rustls = { version = "0.26", default-features = false } tokio-stream = "0.1" tonic = "0.14" tracing = "0.1" diff --git a/temporalio/bridge/_visitor.py b/temporalio/bridge/_visitor.py index 4e258b9a1..ef1e21dbd 100644 --- a/temporalio/bridge/_visitor.py +++ b/temporalio/bridge/_visitor.py @@ -58,28 +58,26 @@ async def visit(self, fs: VisitorFunctions, root: Any) -> None: async def _visit_nexus_operation_input_payload( self, fs: VisitorFunctions, - endpoint: str, payload: Payload, + ) -> None: + await self._visit_temporal_api_common_v1_Payload(fs, payload) + + async def _visit_temporal_api_common_v1_Payload( + self, fs: VisitorFunctions, payload: Payload ) -> None: new_payload = await temporalio.nexus.system.maybe_visit_payload( - endpoint, payload, fs, self.skip_search_attributes, ) if new_payload is None: - await self._visit_temporal_api_common_v1_Payload(fs, payload) + await fs.visit_payload(payload) return if new_payload is not payload: payload.CopyFrom(new_payload) await fs.visit_system_nexus_envelope(payload) - async def _visit_temporal_api_common_v1_Payload( - self, fs: VisitorFunctions, o: Payload - ): - await fs.visit_payload(o) - async def _visit_temporal_api_common_v1_Payloads( self, fs: VisitorFunctions, o: Any ): @@ -474,7 +472,7 @@ async def _visit_coresdk_workflow_commands_ScheduleNexusOperation( self, fs: VisitorFunctions, o: Any ): if o.HasField("input"): - await self._visit_nexus_operation_input_payload(fs, o.endpoint, o.input) + await self._visit_nexus_operation_input_payload(fs, o.input) async def _visit_coresdk_workflow_commands_WorkflowCommand( self, fs: VisitorFunctions, o: Any @@ -549,3 +547,77 @@ async def _visit_coresdk_workflow_completion_WorkflowActivationCompletion( await self._visit_coresdk_workflow_completion_Success(fs, o.successful) elif o.HasField("failed"): await self._visit_coresdk_workflow_completion_Failure(fs, o.failed) + + async def _visit_temporal_api_nexus_v1_StartOperationResponse_Sync( + self, fs: VisitorFunctions, o: Any + ): + if o.HasField("payload"): + await self._visit_temporal_api_common_v1_Payload(fs, o.payload) + + async def _visit_temporal_api_nexus_v1_Failure(self, fs: VisitorFunctions, o: Any): + if o.HasField("cause"): + await self._visit_temporal_api_nexus_v1_Failure(fs, o.cause) + + async def _visit_temporal_api_nexus_v1_UnsuccessfulOperationError( + self, fs: VisitorFunctions, o: Any + ): + if o.HasField("failure"): + await self._visit_temporal_api_nexus_v1_Failure(fs, o.failure) + + async def _visit_temporal_api_nexus_v1_StartOperationResponse( + self, fs: VisitorFunctions, o: Any + ): + if o.HasField("sync_success"): + await self._visit_temporal_api_nexus_v1_StartOperationResponse_Sync( + fs, o.sync_success + ) + elif o.HasField("operation_error"): + await self._visit_temporal_api_nexus_v1_UnsuccessfulOperationError( + fs, o.operation_error + ) + elif o.HasField("failure"): + await self._visit_temporal_api_failure_v1_Failure(fs, o.failure) + + async def _visit_temporal_api_nexus_v1_Response(self, fs: VisitorFunctions, o: Any): + if o.HasField("start_operation"): + await self._visit_temporal_api_nexus_v1_StartOperationResponse( + fs, o.start_operation + ) + + async def _visit_temporal_api_nexus_v1_HandlerError( + self, fs: VisitorFunctions, o: Any + ): + if o.HasField("failure"): + await self._visit_temporal_api_nexus_v1_Failure(fs, o.failure) + + async def _visit_coresdk_nexus_NexusTaskCompletion( + self, fs: VisitorFunctions, o: Any + ): + if o.HasField("completed"): + await self._visit_temporal_api_nexus_v1_Response(fs, o.completed) + elif o.HasField("error"): + await self._visit_temporal_api_nexus_v1_HandlerError(fs, o.error) + elif o.HasField("failure"): + await self._visit_temporal_api_failure_v1_Failure(fs, o.failure) + + async def _visit_temporal_api_common_v1_Header(self, fs: VisitorFunctions, o: Any): + for v in o.fields.values(): + await self._visit_temporal_api_common_v1_Payload(fs, v) + + async def _visit_temporal_api_workflowservice_v1_SignalWithStartWorkflowExecutionRequest( + self, fs: VisitorFunctions, o: Any + ): + if o.HasField("input"): + await self._visit_temporal_api_common_v1_Payloads(fs, o.input) + if o.HasField("signal_input"): + await self._visit_temporal_api_common_v1_Payloads(fs, o.signal_input) + if o.HasField("memo"): + await self._visit_temporal_api_common_v1_Memo(fs, o.memo) + if o.HasField("search_attributes"): + await self._visit_temporal_api_common_v1_SearchAttributes( + fs, o.search_attributes + ) + if o.HasField("header"): + await self._visit_temporal_api_common_v1_Header(fs, o.header) + if o.HasField("user_metadata"): + await self._visit_temporal_api_sdk_v1_UserMetadata(fs, o.user_metadata) diff --git a/temporalio/bridge/_visitor_functions.py b/temporalio/bridge/_visitor_functions.py index 548a0ea94..da8c67ae2 100644 --- a/temporalio/bridge/_visitor_functions.py +++ b/temporalio/bridge/_visitor_functions.py @@ -1,7 +1,7 @@ from __future__ import annotations import asyncio -from typing import Protocol +from abc import ABC, abstractmethod from google.protobuf.internal.containers import RepeatedCompositeFieldContainer @@ -10,21 +10,31 @@ PayloadSequence = list[Payload] | RepeatedCompositeFieldContainer[Payload] -class VisitorFunctions(Protocol): +class VisitorFunctions(ABC): """Functions invoked by generated payload visitors.""" + @abstractmethod async def visit_payload(self, payload: Payload) -> None: """Visit a single payload.""" ... + @abstractmethod async def visit_payloads(self, payloads: PayloadSequence) -> None: """Visit a sequence of payloads together.""" ... - async def visit_system_nexus_envelope(self, payload: Payload) -> None: + async def visit_system_nexus_envelope(self, _payload: Payload) -> None: """Visit a recognized system Nexus envelope payload.""" return None + def checkpoint(self) -> int | None: + """Return a marker for visits scheduled after this point, if supported.""" + return None + + async def drain_since(self, _checkpoint: int) -> None: + """Wait for visits scheduled after ``checkpoint`` to finish.""" + return None + class BoundedVisitorFunctions(VisitorFunctions): """Wraps VisitorFunctions to cap concurrent payload visits via a semaphore. @@ -74,16 +84,33 @@ async def _run() -> None: self._tasks.append(asyncio.create_task(_run())) + def checkpoint(self) -> int: + """Return a marker for tasks scheduled after this point.""" + return len(self._tasks) + + async def drain_since(self, checkpoint: int) -> None: + """Wait for tasks scheduled after ``checkpoint`` to finish. + + This lets system-envelope traversal finish mutating its decoded value + before that value is serialized again, without waiting for unrelated + visits that were already in progress. + """ + await self._drain_tasks(self._tasks[checkpoint:]) + async def drain(self) -> None: """Wait for all in-flight background tasks to complete. On cancellation or error, cancels all remaining tasks and awaits them so their finally blocks run before this coroutine returns. """ - if not self._tasks: + await self._drain_tasks(self._tasks) + + async def _drain_tasks(self, tasks: list[asyncio.Task[None]]) -> None: + """Wait for the given tasks, cancelling all tasks if one fails.""" + if not tasks: return try: - await asyncio.gather(*self._tasks) + await asyncio.gather(*tasks) except BaseException: for task in self._tasks: task.cancel() diff --git a/temporalio/bridge/client.py b/temporalio/bridge/client.py index c2c5bef6e..213443f29 100644 --- a/temporalio/bridge/client.py +++ b/temporalio/bridge/client.py @@ -27,6 +27,7 @@ class ClientTlsConfig: domain: str | None client_cert: bytes | None client_private_key: bytes | None + verification_server_name: str | None @dataclass @@ -82,6 +83,8 @@ class ClientConfig: http_connect_proxy_config: ClientHttpConnectProxyConfig | None dns_load_balancing_config: ClientDnsLoadBalancingConfig | None grpc_compression: str + payloads_warn_size: int + memo_warn_size: int @dataclass diff --git a/temporalio/bridge/proto/common/__init__.py b/temporalio/bridge/proto/common/__init__.py index 5622fffb8..a8506090d 100644 --- a/temporalio/bridge/proto/common/__init__.py +++ b/temporalio/bridge/proto/common/__init__.py @@ -1,10 +1,12 @@ from .common_pb2 import ( + ExternalStorageMetrics, NamespacedWorkflowExecution, VersioningIntent, WorkerDeploymentVersion, ) __all__ = [ + "ExternalStorageMetrics", "NamespacedWorkflowExecution", "VersioningIntent", "WorkerDeploymentVersion", diff --git a/temporalio/bridge/proto/common/common_pb2.py b/temporalio/bridge/proto/common/common_pb2.py index c56456fce..481cf216d 100644 --- a/temporalio/bridge/proto/common/common_pb2.py +++ b/temporalio/bridge/proto/common/common_pb2.py @@ -18,7 +18,7 @@ from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n%temporal/sdk/core/common/common.proto\x12\x0e\x63oresdk.common\x1a\x1egoogle/protobuf/duration.proto"U\n\x1bNamespacedWorkflowExecution\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t"D\n\x17WorkerDeploymentVersion\x12\x17\n\x0f\x64\x65ployment_name\x18\x01 \x01(\t\x12\x10\n\x08\x62uild_id\x18\x02 \x01(\t*@\n\x10VersioningIntent\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x0e\n\nCOMPATIBLE\x10\x01\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x02\x42,\xea\x02)Temporalio::Internal::Bridge::Api::Commonb\x06proto3' + b'\n%temporal/sdk/core/common/common.proto\x12\x0e\x63oresdk.common\x1a\x1egoogle/protobuf/duration.proto"U\n\x1bNamespacedWorkflowExecution\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t"D\n\x17WorkerDeploymentVersion\x12\x17\n\x0f\x64\x65ployment_name\x18\x01 \x01(\t\x12\x10\n\x08\x62uild_id\x18\x02 \x01(\t"\x92\x01\n\x16\x45xternalStorageMetrics\x12\x15\n\rpayload_count\x18\x01 \x01(\x04\x12\x18\n\x10total_size_bytes\x18\x02 \x01(\x04\x12\x31\n\x0etotal_duration\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x14\n\x0c\x64river_names\x18\x04 \x03(\t*@\n\x10VersioningIntent\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x0e\n\nCOMPATIBLE\x10\x01\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x02\x42,\xea\x02)Temporalio::Internal::Bridge::Api::Commonb\x06proto3' ) _VERSIONINGINTENT = DESCRIPTOR.enum_types_by_name["VersioningIntent"] @@ -32,6 +32,7 @@ "NamespacedWorkflowExecution" ] _WORKERDEPLOYMENTVERSION = DESCRIPTOR.message_types_by_name["WorkerDeploymentVersion"] +_EXTERNALSTORAGEMETRICS = DESCRIPTOR.message_types_by_name["ExternalStorageMetrics"] NamespacedWorkflowExecution = _reflection.GeneratedProtocolMessageType( "NamespacedWorkflowExecution", (_message.Message,), @@ -54,15 +55,28 @@ ) _sym_db.RegisterMessage(WorkerDeploymentVersion) +ExternalStorageMetrics = _reflection.GeneratedProtocolMessageType( + "ExternalStorageMetrics", + (_message.Message,), + { + "DESCRIPTOR": _EXTERNALSTORAGEMETRICS, + "__module__": "temporal.sdk.core.common.common_pb2", + # @@protoc_insertion_point(class_scope:coresdk.common.ExternalStorageMetrics) + }, +) +_sym_db.RegisterMessage(ExternalStorageMetrics) + if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = ( b"\352\002)Temporalio::Internal::Bridge::Api::Common" ) - _VERSIONINGINTENT._serialized_start = 246 - _VERSIONINGINTENT._serialized_end = 310 + _VERSIONINGINTENT._serialized_start = 395 + _VERSIONINGINTENT._serialized_end = 459 _NAMESPACEDWORKFLOWEXECUTION._serialized_start = 89 _NAMESPACEDWORKFLOWEXECUTION._serialized_end = 174 _WORKERDEPLOYMENTVERSION._serialized_start = 176 _WORKERDEPLOYMENTVERSION._serialized_end = 244 + _EXTERNALSTORAGEMETRICS._serialized_start = 247 + _EXTERNALSTORAGEMETRICS._serialized_end = 393 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/bridge/proto/common/common_pb2.pyi b/temporalio/bridge/proto/common/common_pb2.pyi index 739a129e1..8862fa036 100644 --- a/temporalio/bridge/proto/common/common_pb2.pyi +++ b/temporalio/bridge/proto/common/common_pb2.pyi @@ -4,10 +4,13 @@ isort:skip_file """ import builtins +import collections.abc import sys import typing import google.protobuf.descriptor +import google.protobuf.duration_pb2 +import google.protobuf.internal.containers import google.protobuf.internal.enum_type_wrapper import google.protobuf.message @@ -121,3 +124,53 @@ class WorkerDeploymentVersion(google.protobuf.message.Message): ) -> None: ... global___WorkerDeploymentVersion = WorkerDeploymentVersion + +class ExternalStorageMetrics(google.protobuf.message.Message): + """Metrics for a set of external payload storage operations (all uploads and downloads) + performed while processing a task, so core can emit unified logging and metrics. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PAYLOAD_COUNT_FIELD_NUMBER: builtins.int + TOTAL_SIZE_BYTES_FIELD_NUMBER: builtins.int + TOTAL_DURATION_FIELD_NUMBER: builtins.int + DRIVER_NAMES_FIELD_NUMBER: builtins.int + payload_count: builtins.int + """Number of payloads stored or retrieved externally.""" + total_size_bytes: builtins.int + """Total size in bytes of the externally stored or retrieved payloads.""" + @property + def total_duration(self) -> google.protobuf.duration_pb2.Duration: + """Wall-clock time spent on the external storage operations.""" + @property + def driver_names( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """Names of the drivers that participated in the operations.""" + def __init__( + self, + *, + payload_count: builtins.int = ..., + total_size_bytes: builtins.int = ..., + total_duration: google.protobuf.duration_pb2.Duration | None = ..., + driver_names: collections.abc.Iterable[builtins.str] | None = ..., + ) -> None: ... + def HasField( + self, field_name: typing_extensions.Literal["total_duration", b"total_duration"] + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "driver_names", + b"driver_names", + "payload_count", + b"payload_count", + "total_duration", + b"total_duration", + "total_size_bytes", + b"total_size_bytes", + ], + ) -> None: ... + +global___ExternalStorageMetrics = ExternalStorageMetrics diff --git a/temporalio/bridge/proto/workflow_completion/workflow_completion_pb2.py b/temporalio/bridge/proto/workflow_completion/workflow_completion_pb2.py index ce26b220d..057b301e4 100644 --- a/temporalio/bridge/proto/workflow_completion/workflow_completion_pb2.py +++ b/temporalio/bridge/proto/workflow_completion/workflow_completion_pb2.py @@ -31,7 +31,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n?temporal/sdk/core/workflow_completion/workflow_completion.proto\x12\x1b\x63oresdk.workflow_completion\x1a%temporal/api/failure/v1/message.proto\x1a(temporal/api/enums/v1/failed_cause.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a%temporal/sdk/core/common/common.proto\x1a;temporal/sdk/core/workflow_commands/workflow_commands.proto"\xac\x01\n\x1cWorkflowActivationCompletion\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12:\n\nsuccessful\x18\x02 \x01(\x0b\x32$.coresdk.workflow_completion.SuccessH\x00\x12\x36\n\x06\x66\x61iled\x18\x03 \x01(\x0b\x32$.coresdk.workflow_completion.FailureH\x00\x42\x08\n\x06status"\xac\x01\n\x07Success\x12<\n\x08\x63ommands\x18\x01 \x03(\x0b\x32*.coresdk.workflow_commands.WorkflowCommand\x12\x1b\n\x13used_internal_flags\x18\x06 \x03(\r\x12\x46\n\x13versioning_behavior\x18\x07 \x01(\x0e\x32).temporal.api.enums.v1.VersioningBehavior"\x81\x01\n\x07\x46\x61ilure\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x43\n\x0b\x66orce_cause\x18\x02 \x01(\x0e\x32..temporal.api.enums.v1.WorkflowTaskFailedCauseB8\xea\x02\x35Temporalio::Internal::Bridge::Api::WorkflowCompletionb\x06proto3' + b'\n?temporal/sdk/core/workflow_completion/workflow_completion.proto\x12\x1b\x63oresdk.workflow_completion\x1a%temporal/api/failure/v1/message.proto\x1a(temporal/api/enums/v1/failed_cause.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a%temporal/sdk/core/common/common.proto\x1a;temporal/sdk/core/workflow_commands/workflow_commands.proto"\xbe\x02\n\x1cWorkflowActivationCompletion\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12:\n\nsuccessful\x18\x02 \x01(\x0b\x32$.coresdk.workflow_completion.SuccessH\x00\x12\x36\n\x06\x66\x61iled\x18\x03 \x01(\x0b\x32$.coresdk.workflow_completion.FailureH\x00\x12H\n\x18payload_download_metrics\x18\x04 \x01(\x0b\x32&.coresdk.common.ExternalStorageMetrics\x12\x46\n\x16payload_upload_metrics\x18\x05 \x01(\x0b\x32&.coresdk.common.ExternalStorageMetricsB\x08\n\x06status"\xac\x01\n\x07Success\x12<\n\x08\x63ommands\x18\x01 \x03(\x0b\x32*.coresdk.workflow_commands.WorkflowCommand\x12\x1b\n\x13used_internal_flags\x18\x06 \x03(\r\x12\x46\n\x13versioning_behavior\x18\x07 \x01(\x0e\x32).temporal.api.enums.v1.VersioningBehavior"\x81\x01\n\x07\x46\x61ilure\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12\x43\n\x0b\x66orce_cause\x18\x02 \x01(\x0e\x32..temporal.api.enums.v1.WorkflowTaskFailedCauseB8\xea\x02\x35Temporalio::Internal::Bridge::Api::WorkflowCompletionb\x06proto3' ) @@ -79,9 +79,9 @@ b"\352\0025Temporalio::Internal::Bridge::Api::WorkflowCompletion" ) _WORKFLOWACTIVATIONCOMPLETION._serialized_start = 316 - _WORKFLOWACTIVATIONCOMPLETION._serialized_end = 488 - _SUCCESS._serialized_start = 491 - _SUCCESS._serialized_end = 663 - _FAILURE._serialized_start = 666 - _FAILURE._serialized_end = 795 + _WORKFLOWACTIVATIONCOMPLETION._serialized_end = 634 + _SUCCESS._serialized_start = 637 + _SUCCESS._serialized_end = 809 + _FAILURE._serialized_start = 812 + _FAILURE._serialized_end = 941 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/bridge/proto/workflow_completion/workflow_completion_pb2.pyi b/temporalio/bridge/proto/workflow_completion/workflow_completion_pb2.pyi index 5b438f360..8e12736aa 100644 --- a/temporalio/bridge/proto/workflow_completion/workflow_completion_pb2.pyi +++ b/temporalio/bridge/proto/workflow_completion/workflow_completion_pb2.pyi @@ -14,6 +14,7 @@ import google.protobuf.message import temporalio.api.enums.v1.failed_cause_pb2 import temporalio.api.enums.v1.workflow_pb2 import temporalio.api.failure.v1.message_pb2 +import temporalio.bridge.proto.common.common_pb2 import temporalio.bridge.proto.workflow_commands.workflow_commands_pb2 if sys.version_info >= (3, 8): @@ -31,23 +32,52 @@ class WorkflowActivationCompletion(google.protobuf.message.Message): RUN_ID_FIELD_NUMBER: builtins.int SUCCESSFUL_FIELD_NUMBER: builtins.int FAILED_FIELD_NUMBER: builtins.int + PAYLOAD_DOWNLOAD_METRICS_FIELD_NUMBER: builtins.int + PAYLOAD_UPLOAD_METRICS_FIELD_NUMBER: builtins.int run_id: builtins.str """The run id from the workflow activation you are completing""" @property def successful(self) -> global___Success: ... @property def failed(self) -> global___Failure: ... + @property + def payload_download_metrics( + self, + ) -> temporalio.bridge.proto.common.common_pb2.ExternalStorageMetrics: + """Metrics for external payload storage downloads (retrievals) performed while processing + this activation. Only set when external storage retrieved payloads. + """ + @property + def payload_upload_metrics( + self, + ) -> temporalio.bridge.proto.common.common_pb2.ExternalStorageMetrics: + """Metrics for external payload storage uploads (stores) performed while processing this + activation. Only set when external storage stored payloads. + """ def __init__( self, *, run_id: builtins.str = ..., successful: global___Success | None = ..., failed: global___Failure | None = ..., + payload_download_metrics: temporalio.bridge.proto.common.common_pb2.ExternalStorageMetrics + | None = ..., + payload_upload_metrics: temporalio.bridge.proto.common.common_pb2.ExternalStorageMetrics + | None = ..., ) -> None: ... def HasField( self, field_name: typing_extensions.Literal[ - "failed", b"failed", "status", b"status", "successful", b"successful" + "failed", + b"failed", + "payload_download_metrics", + b"payload_download_metrics", + "payload_upload_metrics", + b"payload_upload_metrics", + "status", + b"status", + "successful", + b"successful", ], ) -> builtins.bool: ... def ClearField( @@ -55,6 +85,10 @@ class WorkflowActivationCompletion(google.protobuf.message.Message): field_name: typing_extensions.Literal[ "failed", b"failed", + "payload_download_metrics", + b"payload_download_metrics", + "payload_upload_metrics", + b"payload_upload_metrics", "run_id", b"run_id", "status", diff --git a/temporalio/bridge/runtime.py b/temporalio/bridge/runtime.py index fa7fb275d..e5987cb42 100644 --- a/temporalio/bridge/runtime.py +++ b/temporalio/bridge/runtime.py @@ -71,6 +71,7 @@ class OpenTelemetryConfig: metric_temporality_delta: bool durations_as_seconds: bool http: bool + histogram_bucket_overrides: Mapping[str, Sequence[float]] | None = None @dataclass(frozen=True) @@ -98,6 +99,7 @@ class RuntimeOptions: telemetry: TelemetryConfig worker_heartbeat_interval_millis: int | None = 60_000 # 60s + disable_environment_info: bool = False # WARNING: This must match Rust runtime::BufferedLogEntry diff --git a/temporalio/bridge/sdk-core b/temporalio/bridge/sdk-core index b9e20dad5..8cf682b7a 160000 --- a/temporalio/bridge/sdk-core +++ b/temporalio/bridge/sdk-core @@ -1 +1 @@ -Subproject commit b9e20dad51763ca6a7e4c8b2ae7f54e1623dea18 +Subproject commit 8cf682b7aec9aafbeb6e779872822f37a6f8c55c diff --git a/temporalio/bridge/services_generated.py b/temporalio/bridge/services_generated.py index 301c218cf..c9960fcb5 100644 --- a/temporalio/bridge/services_generated.py +++ b/temporalio/bridge/services_generated.py @@ -81,6 +81,24 @@ async def count_schedules( timeout=timeout, ) + async def count_workers( + self, + req: temporalio.api.workflowservice.v1.CountWorkersRequest, + retry: bool = False, + metadata: Mapping[str, str | bytes] = {}, + timeout: timedelta | None = None, + ) -> temporalio.api.workflowservice.v1.CountWorkersResponse: + """Invokes the WorkflowService.count_workers rpc method.""" + return await self._client._rpc_call( + rpc="count_workers", + req=req, + service=self._service, + resp_type=temporalio.api.workflowservice.v1.CountWorkersResponse, + retry=retry, + metadata=metadata, + timeout=timeout, + ) + async def count_workflow_executions( self, req: temporalio.api.workflowservice.v1.CountWorkflowExecutionsRequest, @@ -1161,6 +1179,24 @@ async def poll_nexus_task_queue( timeout=timeout, ) + async def poll_workflow_execution_time_skipping( + self, + req: temporalio.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingRequest, + retry: bool = False, + metadata: Mapping[str, str | bytes] = {}, + timeout: timedelta | None = None, + ) -> temporalio.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingResponse: + """Invokes the WorkflowService.poll_workflow_execution_time_skipping rpc method.""" + return await self._client._rpc_call( + rpc="poll_workflow_execution_time_skipping", + req=req, + service=self._service, + resp_type=temporalio.api.workflowservice.v1.PollWorkflowExecutionTimeSkippingResponse, + retry=retry, + metadata=metadata, + timeout=timeout, + ) + async def poll_workflow_execution_update( self, req: temporalio.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest, diff --git a/temporalio/bridge/src/client.rs b/temporalio/bridge/src/client.rs index 85dedef94..c154d6a58 100644 --- a/temporalio/bridge/src/client.rs +++ b/temporalio/bridge/src/client.rs @@ -2,6 +2,7 @@ use pyo3::exceptions::{PyException, PyRuntimeError, PyValueError}; use pyo3::prelude::*; use std::collections::HashMap; use std::str::FromStr; +use std::sync::Arc; use std::time::Duration; use temporalio_client::tonic::{ self, @@ -11,6 +12,14 @@ use temporalio_client::{ ClientKeepAliveOptions as CoreClientKeepAliveConfig, Connection, ConnectionOptions, DnsLoadBalancingOptions, GrpcCompression, HttpConnectProxyOptions, RetryOptions, }; +use tokio_rustls::rustls::client::danger::{ + HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, +}; +use tokio_rustls::rustls::client::WebPkiServerVerifier; +use tokio_rustls::rustls::crypto::CryptoProvider; +use tokio_rustls::rustls::pki_types::pem::PemObject; +use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime}; +use tokio_rustls::rustls::{self, DigitallySignedStruct, RootCertStore, SignatureScheme}; use tracing::warn; use url::Url; @@ -38,6 +47,8 @@ pub struct ClientConfig { http_connect_proxy_config: Option, dns_load_balancing_config: Option, grpc_compression: String, + payloads_warn_size: u64, + memo_warn_size: u64, } #[derive(FromPyObject)] @@ -46,6 +57,7 @@ struct ClientTlsConfig { domain: Option, client_cert: Option>, client_private_key: Option>, + verification_server_name: Option, } #[derive(FromPyObject)] @@ -268,6 +280,12 @@ impl ClientConfig { .maybe_http_connect_proxy(self.http_connect_proxy_config.map(Into::into)) .dns_load_balancing(dns_load_balancing) .grpc_compression(grpc_compression_from_str(&self.grpc_compression)?) + .payload_limits( + temporalio_client::PayloadLimitsOptions::builder() + .payloads_warn_size(self.payloads_warn_size) + .memo_warn_size(self.memo_warn_size) + .build(), + ) .headers(ascii_headers) .binary_headers(binary_headers) .maybe_api_key(self.api_key) @@ -295,56 +313,162 @@ impl TryFrom for temporalio_client::TlsOptions { type Error = PyErr; fn try_from(conf: ClientTlsConfig) -> PyResult { - Ok(temporalio_client::TlsOptions { - server_root_ca_cert: conf.server_root_ca_cert, - domain: conf.domain, - client_tls_options: match (conf.client_cert, conf.client_private_key) { - (None, None) => None, - (Some(client_cert), Some(client_private_key)) => { - Some(temporalio_client::ClientTlsOptions { - client_cert, - client_private_key, - }) - } - _ => { - return Err(PyValueError::new_err( - "Must have both client cert and private key or neither", - )) - } - }, - server_cert_verifier: None, - }) + let mut server_root_ca_cert = conf.server_root_ca_cert; + let server_cert_verifier = match conf.verification_server_name { + None => None, + Some(name) => { + // The CA bundle is consumed by the verifier's own root store; a + // custom verifier cannot be combined with roots on the connection. + let ca_cert = server_root_ca_cert.take().ok_or_else(|| { + PyValueError::new_err( + "Must have server root CA cert when verification server name is set", + ) + })?; + Some(fixed_server_name_verifier(&name, &ca_cert)?) + } + }; + let client_tls_options = match (conf.client_cert, conf.client_private_key) { + (None, None) => None, + (Some(client_cert), Some(client_private_key)) => Some( + temporalio_client::ClientTlsOptions::builder() + .client_cert(client_cert) + .client_private_key(client_private_key) + .build(), + ), + _ => { + return Err(PyValueError::new_err( + "Must have both client cert and private key or neither", + )) + } + }; + Ok(temporalio_client::TlsOptions::builder() + .maybe_server_root_ca_cert(server_root_ca_cert) + .maybe_domain(conf.domain) + .maybe_client_tls_options(client_tls_options) + .maybe_server_cert_verifier(server_cert_verifier) + .build()) + } +} + +/// Builds a standard WebPKI verifier over the given root CA bundle that +/// checks the certificate against `verification_server_name` rather than the +/// connection's server name, leaving SNI/`:authority` to follow the +/// connected host (or `domain` when set). +fn fixed_server_name_verifier( + verification_server_name: &str, + ca_cert_pem: &[u8], +) -> PyResult> { + let certs = CertificateDer::pem_slice_iter(ca_cert_pem) + .collect::, _>>() + .map_err(|err| { + PyValueError::new_err(format!("Invalid server root CA cert PEM: {err:?}")) + })?; + // Root loading and provider selection mirror tonic's default (no custom + // verifier) client path: unparsable certificates in the bundle are + // skipped, and the provider is the process default if one is installed, + // else the build's compiled-in provider. Under a `fips` build lib.rs + // installs aws-lc-rs (FIPS) as the process default, so `get_default()` + // returns it and this fallback is not taken; the fallback is still made + // `cfg`-conditional because the `ring` module is absent in a FIPS build + // (`tokio-rustls/ring` off) and would otherwise fail to compile. + let mut roots = RootCertStore::empty(); + roots.add_parsable_certificates(certs); + let provider = CryptoProvider::get_default().cloned().unwrap_or_else(|| { + #[cfg(feature = "fips")] + { + Arc::new(rustls::crypto::aws_lc_rs::default_provider()) + } + #[cfg(not(feature = "fips"))] + { + Arc::new(rustls::crypto::ring::default_provider()) + } + }); + let inner = WebPkiServerVerifier::builder_with_provider(roots.into(), provider) + .build() + .map_err(|err| { + PyValueError::new_err(format!("Failed building certificate verifier: {err}")) + })?; + let server_name = ServerName::try_from(verification_server_name.to_owned()) + .map_err(|err| PyValueError::new_err(format!("Invalid verification server name: {err}")))?; + Ok(Arc::new(FixedServerNameVerifier { inner, server_name })) +} + +/// Delegates to the standard WebPKI verifier, but verifies the certificate +/// against a fixed server name instead of the connection's server name. +#[derive(Debug)] +struct FixedServerNameVerifier { + inner: Arc, + server_name: ServerName<'static>, +} + +impl ServerCertVerifier for FixedServerNameVerifier { + fn verify_server_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + ocsp_response: &[u8], + now: UnixTime, + ) -> Result { + self.inner.verify_server_cert( + end_entity, + intermediates, + &self.server_name, + ocsp_response, + now, + ) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.inner.verify_tls12_signature(message, cert, dss) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.inner.verify_tls13_signature(message, cert, dss) + } + + fn supported_verify_schemes(&self) -> Vec { + self.inner.supported_verify_schemes() } } impl From for RetryOptions { fn from(conf: ClientRetryConfig) -> Self { - RetryOptions { - initial_interval: Duration::from_millis(conf.initial_interval_millis), - randomization_factor: conf.randomization_factor, - multiplier: conf.multiplier, - max_interval: Duration::from_millis(conf.max_interval_millis), - max_elapsed_time: conf.max_elapsed_time_millis.map(Duration::from_millis), - max_retries: conf.max_retries, - } + RetryOptions::builder() + .initial_interval(Duration::from_millis(conf.initial_interval_millis)) + .randomization_factor(conf.randomization_factor) + .multiplier(conf.multiplier) + .max_interval(Duration::from_millis(conf.max_interval_millis)) + .max_elapsed_time(conf.max_elapsed_time_millis.map(Duration::from_millis)) + .max_retries(conf.max_retries) + .build() } } impl From for CoreClientKeepAliveConfig { fn from(conf: ClientKeepAliveConfig) -> Self { - CoreClientKeepAliveConfig { - interval: Duration::from_millis(conf.interval_millis), - timeout: Duration::from_millis(conf.timeout_millis), - } + CoreClientKeepAliveConfig::builder() + .interval(Duration::from_millis(conf.interval_millis)) + .timeout(Duration::from_millis(conf.timeout_millis)) + .build() } } impl From for HttpConnectProxyOptions { fn from(conf: ClientHttpConnectProxyConfig) -> Self { - HttpConnectProxyOptions { - target_addr: conf.target_host, - basic_auth: conf.basic_auth, - } + HttpConnectProxyOptions::new(conf.target_host) + .maybe_basic_auth(conf.basic_auth) + .build() } } diff --git a/temporalio/bridge/src/client_rpc_generated.rs b/temporalio/bridge/src/client_rpc_generated.rs index 931b77a32..ea00d4fd5 100644 --- a/temporalio/bridge/src/client_rpc_generated.rs +++ b/temporalio/bridge/src/client_rpc_generated.rs @@ -47,6 +47,15 @@ impl ClientRef { count_schedules ) } + "count_workers" => { + rpc_call!( + connection, + call, + WorkflowService, + workflow_service, + count_workers + ) + } "count_workflow_executions" => { rpc_call!( connection, @@ -587,6 +596,15 @@ impl ClientRef { poll_nexus_task_queue ) } + "poll_workflow_execution_time_skipping" => { + rpc_call!( + connection, + call, + WorkflowService, + workflow_service, + poll_workflow_execution_time_skipping + ) + } "poll_workflow_execution_update" => { rpc_call!( connection, diff --git a/temporalio/bridge/src/envconfig.rs b/temporalio/bridge/src/envconfig.rs index 40ef9b638..7ce2f4664 100644 --- a/temporalio/bridge/src/envconfig.rs +++ b/temporalio/bridge/src/envconfig.rs @@ -91,10 +91,10 @@ fn load_client_config_inner( config_file_strict: bool, env_vars: Option>, ) -> PyResult> { - let options = LoadClientConfigOptions { - config_source, - config_file_strict, - }; + let options = LoadClientConfigOptions::builder() + .maybe_config_source(config_source) + .config_file_strict(config_file_strict) + .build(); let core_config = core_load_client_config(options, env_vars.as_ref()) .map_err(|e| ConfigError::new_err(format!("{e}")))?; @@ -110,13 +110,13 @@ fn load_client_connect_config_inner( config_file_strict: bool, env_vars: Option>, ) -> PyResult> { - let options = LoadClientConfigProfileOptions { - config_source, - config_file_profile: profile, - config_file_strict, - disable_file, - disable_env, - }; + let options = LoadClientConfigProfileOptions::builder() + .maybe_config_source(config_source) + .maybe_config_file_profile(profile) + .config_file_strict(config_file_strict) + .disable_file(disable_file) + .disable_env(disable_env) + .build(); let profile = core_load_client_config_profile(options, env_vars.as_ref()) .map_err(|e| ConfigError::new_err(format!("{e}")))?; diff --git a/temporalio/bridge/src/lib.rs b/temporalio/bridge/src/lib.rs index ee157fb18..a4fda26db 100644 --- a/temporalio/bridge/src/lib.rs +++ b/temporalio/bridge/src/lib.rs @@ -11,6 +11,19 @@ mod worker; #[pymodule] fn temporal_sdk_bridge(py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { + // Lets ops assert at runtime that the FIPS wheel is actually loaded, e.g. + // `temporalio.bridge.temporal_sdk_bridge.FIPS is True`. Mirrors sdk-ruby PR #466. + m.add("FIPS", cfg!(feature = "fips"))?; + + // Under a FIPS build, install aws-lc-rs (FIPS mode) as the process-wide + // rustls provider before any client is constructed. This makes + // CryptoProvider::get_default() resolve to aws-lc-rs everywhere (including + // the custom verifier in client.rs), so no `ring` provider is ever used. + #[cfg(feature = "fips")] + { + let _ = tokio_rustls::rustls::crypto::aws_lc_rs::default_provider().install_default(); + } + // Client stuff m.add("RPCError", py.get_type::())?; m.add_class::()?; diff --git a/temporalio/bridge/src/runtime.rs b/temporalio/bridge/src/runtime.rs index 26fd3482b..8a8e7b591 100644 --- a/temporalio/bridge/src/runtime.rs +++ b/temporalio/bridge/src/runtime.rs @@ -75,6 +75,7 @@ pub struct OpenTelemetryConfig { metric_temporality_delta: bool, durations_as_seconds: bool, http: bool, + histogram_bucket_overrides: Option>>, } #[derive(FromPyObject)] @@ -90,6 +91,7 @@ pub struct PrometheusConfig { pub struct RuntimeOptions { telemetry: TelemetryConfig, worker_heartbeat_interval_millis: Option, + disable_environment_info: bool, } const FORWARD_LOG_BUFFER_SIZE: usize = 2048; @@ -99,6 +101,7 @@ pub fn init_runtime(options: RuntimeOptions) -> PyResult { let RuntimeOptions { telemetry: TelemetryConfig { logging, metrics }, worker_heartbeat_interval_millis, + disable_environment_info, } = options; // Have to build/start telemetry config pieces @@ -133,6 +136,7 @@ pub fn init_runtime(options: RuntimeOptions) -> PyResult { .build(), ) .heartbeat_interval(worker_heartbeat_interval_millis.map(Duration::from_millis)) + .disable_environment_info(disable_environment_info) .build() .map_err(|err| PyValueError::new_err(format!("Invalid runtime options: {err}")))?; @@ -357,6 +361,11 @@ impl TryFrom for Arc { } else { None }) + .maybe_histogram_bucket_overrides(otel_conf.histogram_bucket_overrides.map( + |overrides| temporalio_common::telemetry::HistogramBucketOverrides { + overrides, + }, + )) .build(); Ok(Arc::new(build_otlp_metric_exporter(otel_options).map_err( |err| PyValueError::new_err(format!("Failed building OTel exporter: {err}")), diff --git a/temporalio/bridge/src/worker.rs b/temporalio/bridge/src/worker.rs index e530e89bb..321dc6560 100644 --- a/temporalio/bridge/src/worker.rs +++ b/temporalio/bridge/src/worker.rs @@ -57,12 +57,14 @@ pub struct WorkerConfig { default_heartbeat_throttle_interval_millis: u64, max_activities_per_second: Option, max_task_queue_activities_per_second: Option, + max_eager_activity_reservations_per_workflow_task: usize, graceful_shutdown_period_millis: u64, nondeterminism_as_workflow_fail: bool, nondeterminism_as_workflow_fail_for_types: HashSet, nexus_task_poller_behavior: PollerBehavior, plugins: Vec, storage_drivers: HashSet, + disable_payload_error_limit: bool, } #[derive(FromPyObject)] @@ -658,10 +660,14 @@ impl WorkerRef { enter_sync!(self.runtime); let heartbeat = ActivityHeartbeat::decode(proto.as_bytes()) .map_err(|err| PyValueError::new_err(format!("Invalid proto: {err}")))?; - self.worker - .as_ref() - .unwrap() - .record_activity_heartbeat(heartbeat); + let worker = self.worker.as_ref().unwrap().clone(); + // Detach from the GIL during the core call. Core may block on internal + // locks whose holders can call back into Python (e.g. a custom slot + // supplier's mark_slot_used runs while core's outstanding-activity + // lock is held); holding the GIL here would deadlock the worker. + proto + .py() + .detach(move || worker.record_activity_heartbeat(heartbeat)); Ok(()) } @@ -734,6 +740,9 @@ fn convert_worker_config( )) .maybe_max_worker_activities_per_second(conf.max_activities_per_second) .maybe_max_task_queue_activities_per_second(conf.max_task_queue_activities_per_second) + .max_eager_activity_reservations_per_workflow_task( + conf.max_eager_activity_reservations_per_workflow_task, + ) // Even though grace period is optional, if it is not set then the // auto-cancel-activity behavior of shutdown will not occur, so we // always set it even if 0. @@ -770,6 +779,7 @@ fn convert_worker_config( .map(|r#type| StorageDriverInfo { r#type }) .collect::>(), ) + .disable_payload_error_limit(conf.disable_payload_error_limit) .build() .map_err(|err| PyValueError::new_err(format!("Invalid worker config: {err}"))) } @@ -823,11 +833,13 @@ fn convert_tuner_holder( } Ok(temporalio_sdk_core::TunerHolderOptions::builder() - .maybe_resource_based_options(first.map(|first| { - temporalio_sdk_core::ResourceBasedSlotsOptions::builder() - .target_mem_usage(first.target_memory_usage) - .target_cpu_usage(first.target_cpu_usage) - .build() + .maybe_resource_based_config(first.map(|first| { + temporalio_sdk_core::ResourceBasedTunerConfig::Options( + temporalio_sdk_core::ResourceBasedSlotsOptions::builder() + .target_mem_usage(first.target_memory_usage) + .target_cpu_usage(first.target_cpu_usage) + .build(), + ) })) .workflow_slot_options(convert_slot_supplier( holder.workflow_slot_supplier, @@ -887,23 +899,25 @@ fn convert_versioning_strategy( }, WorkerVersioningStrategy::DeploymentBased(options) => { temporalio_sdk_core::WorkerVersioningStrategy::WorkerDeploymentBased( - temporalio_common::worker::WorkerDeploymentOptions { - version: temporalio_common::worker::WorkerDeploymentVersion { + temporalio_common::worker::WorkerDeploymentOptions::new( + temporalio_common::worker::WorkerDeploymentVersion { deployment_name: options.version.deployment_name, build_id: options.version.build_id, }, - use_worker_versioning: options.use_worker_versioning, - default_versioning_behavior: if options.use_worker_versioning { - Some( - options - .default_versioning_behavior - .try_into() - .unwrap_or_default(), + ) + .use_worker_versioning(options.use_worker_versioning) + .maybe_default_versioning_behavior(if options.use_worker_versioning { + Some( + temporalio_common::protos::temporal::api::enums::v1::VersioningBehavior::try_from( + options.default_versioning_behavior, ) - } else { - None - }, - }, + .unwrap_or_default() + .into(), + ) + } else { + None + }) + .build(), ) } WorkerVersioningStrategy::LegacyBuildIdBased(lb) => { diff --git a/temporalio/bridge/worker.py b/temporalio/bridge/worker.py index e1e23dd89..4b7f55d09 100644 --- a/temporalio/bridge/worker.py +++ b/temporalio/bridge/worker.py @@ -52,12 +52,14 @@ class WorkerConfig: default_heartbeat_throttle_interval_millis: int max_activities_per_second: float | None max_task_queue_activities_per_second: float | None + max_eager_activity_reservations_per_workflow_task: int graceful_shutdown_period_millis: int nondeterminism_as_workflow_fail: bool nondeterminism_as_workflow_fail_for_types: set[str] nexus_task_poller_behavior: PollerBehavior plugins: Sequence[str] storage_drivers: set[str] + disable_payload_error_limit: bool @dataclass @@ -284,10 +286,8 @@ class _Visitor(VisitorFunctions): def __init__( self, f: Callable[[Sequence[Payload]], Awaitable[list[Payload]]], - visit_system_nexus_envelope: Callable[[Payload], Awaitable[None]] | None = None, ): self._f = f - self._visit_system_nexus_envelope = visit_system_nexus_envelope async def visit_payload(self, payload: Payload) -> None: new_payload = (await self._f([payload]))[0] @@ -303,10 +303,6 @@ async def visit_payloads(self, payloads: PayloadSequence) -> None: del payloads[:] payloads.extend(new_payloads) - async def visit_system_nexus_envelope(self, payload: Payload) -> None: - if self._visit_system_nexus_envelope is not None: - await self._visit_system_nexus_envelope(payload) - async def decode_activation( activation: temporalio.bridge.proto.workflow_activation.WorkflowActivation, @@ -348,28 +344,14 @@ async def encode_completion( Returns: Metrics from any external storage store operations that occurred. """ - - async def _validate_system_nexus_envelope(payload: Payload) -> None: - data_converter._validate_payload_limits([payload]) - await CommandAwarePayloadVisitor( skip_search_attributes=True, skip_headers=not encode_headers, ).visit( - _Visitor( - data_converter._encode_payload_sequence, - visit_system_nexus_envelope=_validate_system_nexus_envelope, - ), + _Visitor(data_converter._encode_payload_sequence), completion, ) - async def _store_and_validate( - payloads: Sequence[Payload], - ) -> list[Payload]: - stored = await data_converter._external_store_payload_sequence(payloads) - data_converter._validate_payload_limits(stored) - return stored - metrics = temporalio.converter._extstore.StorageOperationMetrics() with metrics.track(): await CommandAwarePayloadVisitor( @@ -377,10 +359,7 @@ async def _store_and_validate( skip_headers=not encode_headers, concurrency_limit=storage_concurrency_limit, ).visit( - _Visitor( - _store_and_validate, - visit_system_nexus_envelope=_validate_system_nexus_envelope, - ), + _Visitor(data_converter._external_store_payload_sequence), completion, ) diff --git a/temporalio/client/__init__.py b/temporalio/client/__init__.py index 030f9a542..cdb34b860 100644 --- a/temporalio/client/__init__.py +++ b/temporalio/client/__init__.py @@ -19,6 +19,7 @@ GrpcCompression, HttpConnectProxyConfig, KeepAliveConfig, + PayloadLimitsConfig, RetryConfig, RPCError, RPCStatusCode, @@ -359,6 +360,7 @@ "GrpcCompression", "HttpConnectProxyConfig", "KeepAliveConfig", + "PayloadLimitsConfig", "RetryConfig", "RPCError", "RPCStatusCode", diff --git a/temporalio/client/_activity.py b/temporalio/client/_activity.py index 99c9ede31..138d09dc7 100644 --- a/temporalio/client/_activity.py +++ b/temporalio/client/_activity.py @@ -309,6 +309,9 @@ class ActivityExecutionDescription(ActivityExecution): long_poll_token: bytes | None """Token for follow-on long-poll requests. None if the activity is complete.""" + raw_callbacks: Sequence[temporalio.api.activity.v1.CallbackInfo] + """Underlying protobuf callbacks""" + @classmethod async def _from_execution_info( cls, @@ -316,6 +319,7 @@ async def _from_execution_info( long_poll_token: bytes | None, namespace: str, data_converter: temporalio.converter.DataConverter, + callbacks: Sequence[temporalio.api.activity.v1.CallbackInfo], ) -> Self: """Create from raw proto activity execution info.""" # Decode heartbeat details if present @@ -409,6 +413,7 @@ async def _from_execution_info( typed_search_attributes=temporalio.converter.decode_typed_search_attributes( info.search_attributes ), + raw_callbacks=callbacks, ) diff --git a/temporalio/client/_client.py b/temporalio/client/_client.py index 5efca9702..5acdfe476 100644 --- a/temporalio/client/_client.py +++ b/temporalio/client/_client.py @@ -35,6 +35,7 @@ GrpcCompression, HttpConnectProxyConfig, KeepAliveConfig, + PayloadLimitsConfig, RetryConfig, ServiceClient, TLSConfig, @@ -154,6 +155,7 @@ async def connect( http_connect_proxy_config: HttpConnectProxyConfig | None = None, dns_load_balancing_config: DnsLoadBalancingConfig | None = None, grpc_compression: GrpcCompression = GrpcCompression.GZIP, + payload_limits: PayloadLimitsConfig = PayloadLimitsConfig(), header_codec_behavior: HeaderCodecBehavior = HeaderCodecBehavior.NO_CODEC, ) -> Self: """Connect to a Temporal server. @@ -217,6 +219,8 @@ async def connect( grpc_compression: Transport-level gRPC compression for the client connection. Default is gzip. Set to :py:attr:`GrpcCompression.NONE` to disable compression. + payload_limits: Warning thresholds for outbound payload/memo sizes. Over-threshold + fields are logged but still sent. Set a threshold to 0 to disable it. header_codec_behavior: Encoding behavior for headers sent by the client. """ connect_config = temporalio.service.ConnectConfig( @@ -232,6 +236,7 @@ async def connect( http_connect_proxy_config=http_connect_proxy_config, dns_load_balancing_config=dns_load_balancing_config, grpc_compression=grpc_compression, + payload_limits=payload_limits, ) def make_lambda( @@ -3049,6 +3054,7 @@ class ClientConnectConfig(TypedDict, total=False): http_connect_proxy_config: HttpConnectProxyConfig | None dns_load_balancing_config: DnsLoadBalancingConfig | None grpc_compression: GrpcCompression + payload_limits: PayloadLimitsConfig header_codec_behavior: HeaderCodecBehavior diff --git a/temporalio/client/_impl.py b/temporalio/client/_impl.py index 8e33ff910..9595351f2 100644 --- a/temporalio/client/_impl.py +++ b/temporalio/client/_impl.py @@ -246,7 +246,7 @@ async def _build_start_workflow_execution_request( # inside a Nexus operation handler must forward the inbound Nexus task links # explicitly so the started callee's WorkflowExecutionStarted event links back to # the caller. - if not temporalio.nexus._operation_context._in_nexus_backing_workflow_start_context(): + if not temporalio.nexus._operation_context._in_nexus_backing_start_context(): req.links.extend(nexus_ctx._get_request_links()) return req @@ -277,7 +277,7 @@ async def _build_signal_with_start_workflow_execution_request( # If this signal-with-start is issued from inside a Nexus operation handler (but not the # nexus-backing workflow), forward the inbound Nexus task links so both the callee's # WorkflowExecutionStarted and WorkflowExecutionSignaled events link back to the caller. - if not temporalio.nexus._operation_context._in_nexus_backing_workflow_start_context(): + if not temporalio.nexus._operation_context._in_nexus_backing_start_context(): nexus_ctx = ( temporalio.nexus._operation_context._try_start_operation_context() ) @@ -587,6 +587,13 @@ async def start_activity(self, input: StartActivityInput) -> ActivityHandle[Any] input.id, input.activity_type, run_id=details.run_id ) raise + + # Apply StartActivity response elements to the current Nexus context. + # No-ops if called outside a Nexus context. + temporalio.nexus._operation_context._apply_start_activity_response_to_nexus_context( + input.id, resp + ) + return ActivityHandle( self._client, input.id, @@ -670,6 +677,12 @@ async def _build_start_activity_execution_request( # Set priority req.priority.CopyFrom(input.priority._to_proto()) + # Add request_id, links, and completion callbacks from the Nexus context + # if not in a Nexus context, this is a no-op + temporalio.nexus._operation_context._apply_nexus_context_to_start_activity_request( + req + ) + return req async def cancel_activity(self, input: CancelActivityInput) -> None: @@ -733,6 +746,7 @@ async def describe_activity( is_local=False, ) ), + callbacks=resp.callbacks, ) def list_activities( @@ -787,6 +801,11 @@ async def start_workflow_update( ): break + # Add response link if its a Nexus operation + nexus_ctx = temporalio.nexus._operation_context._try_start_operation_context() + if nexus_ctx is not None and resp.HasField("link"): + nexus_ctx._add_response_link(resp.link) + # Build the handle. If the user's wait stage is COMPLETED, make sure we # poll for result. handle: WorkflowUpdateHandle[Any] = WorkflowUpdateHandle( @@ -852,6 +871,23 @@ async def _build_update_workflow_execution_request( ) ), ) + # Only set Nexus fields for StartWorkflowUpdateInput, skip for UpdateWithStartUpdateWorkflowInput + if isinstance(input, StartWorkflowUpdateInput): + if input.request_id: + req.request.request_id = input.request_id + if input.links: + req.request.links.extend(input.links) + if input.callbacks: + req.request.completion_callbacks.extend( + temporalio.api.common.v1.Callback( + nexus=temporalio.api.common.v1.Callback.Nexus( + url=callback.url, + header=callback.headers, + ), + links=input.links or [], + ) + for callback in input.callbacks + ) if input.args: req.request.input.args.payloads.extend( await data_converter.encode(input.args) diff --git a/temporalio/client/_interceptor.py b/temporalio/client/_interceptor.py index 5333d487f..a6daedd45 100644 --- a/temporalio/client/_interceptor.py +++ b/temporalio/client/_interceptor.py @@ -322,6 +322,10 @@ class StartWorkflowUpdateInput: ret_type: type | None rpc_metadata: Mapping[str, str | bytes] rpc_timeout: timedelta | None + # The following options are for Workflow Updates exposed as Nexus Operations. Experimental and unstable + callbacks: Sequence[Callback] | None = None + links: Sequence[temporalio.api.common.v1.Link] | None = None + request_id: str | None = None @dataclass diff --git a/temporalio/client/_workflow.py b/temporalio/client/_workflow.py index 8579e8433..6b3559c31 100644 --- a/temporalio/client/_workflow.py +++ b/temporalio/client/_workflow.py @@ -59,6 +59,7 @@ ReturnType, SelfType, ) +from ._callback import Callback from ._exceptions import ( WorkflowContinuedAsNewError, WorkflowFailureError, @@ -955,6 +956,10 @@ async def _start_update( result_type: type | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, + # The following options are for Workflow Updates exposed as Nexus Operations. Experimental and unstable + callbacks: Sequence[Callback] | None = None, + links: Sequence[temporalio.api.common.v1.Link] | None = None, + request_id: str | None = None, ) -> WorkflowUpdateHandle[Any]: if wait_for_stage == WorkflowUpdateStage.ADMITTED: raise ValueError("ADMITTED wait stage not supported") @@ -967,7 +972,7 @@ async def _start_update( StartWorkflowUpdateInput( id=self._id, run_id=self._run_id, - first_execution_run_id=self.first_execution_run_id, + first_execution_run_id=self._first_execution_run_id, update_id=id, update=update_name, args=temporalio.common._arg_or_args(arg, args), @@ -976,6 +981,9 @@ async def _start_update( rpc_metadata=rpc_metadata, rpc_timeout=rpc_timeout, wait_for_stage=wait_for_stage, + callbacks=callbacks, + links=links, + request_id=request_id, ) ) diff --git a/temporalio/contrib/aws/s3driver/README.md b/temporalio/contrib/aws/s3driver/README.md index 73b9b3299..7494e4b96 100644 --- a/temporalio/contrib/aws/s3driver/README.md +++ b/temporalio/contrib/aws/s3driver/README.md @@ -58,9 +58,23 @@ driver = S3StorageDriver(client=MyS3Client(), bucket="my-temporal-payloads") ### Key structure -Payloads are stored under content-addressable keys derived from a SHA-256 hash of the serialized payload bytes, segmented by namespace and workflow/activity identifiers when serialization context is available, e.g.: +Payloads are stored under content-addressable keys derived from a SHA-256 hash of the serialized payload bytes, segmented by namespace and workflow/activity identifiers when serialization context is available. - v0/ns/my-namespace/wfi/my-workflow-id/d/sha256/ +Workflow key: + + v0/ns/{namespace}/wt/{workflow-type}/wi/{workflow-id}/ri/{run-id}/d/{hash-algorithm}/{hex-digest} + +Activity key: + + v0/ns/{namespace}/at/{activity-type}/ai/{activity-id}/ri/{run-id}/d/{hash-algorithm}/{hex-digest} + +Fallback key (used when no namespace, workflow, or activity information is available): + + v0/d/{hash-algorithm}/{hex-digest} + +- Missing values (including a missing run ID) are encoded as the literal `null`. +- `hex-digest` is the lower-case SHA-256 hex digest (64 characters). +- Dynamic path segments are percent-encoded: any byte outside S3's [safe character set](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html) (`0-9`, `a-z`, `A-Z`, and `! - _ . * ' ( )`) is escaped as `%XX` over its UTF-8 bytes. ### Notes diff --git a/temporalio/contrib/aws/s3driver/_driver.py b/temporalio/contrib/aws/s3driver/_driver.py index 445bfda8a..4bcf9de25 100644 --- a/temporalio/contrib/aws/s3driver/_driver.py +++ b/temporalio/contrib/aws/s3driver/_driver.py @@ -8,7 +8,7 @@ import asyncio import hashlib -import urllib.parse +import string from collections.abc import Callable, Coroutine, Sequence from typing import Any, TypeVar @@ -25,6 +25,26 @@ _T = TypeVar("_T") +# S3's safe character set for object key names. See +# https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html +_S3_SAFE_CHARS = frozenset(string.ascii_letters + string.digits + "!-_.*'()") + + +def _percent_encode(val: str | None) -> str | None: + """Percent-encode a path segment per the S3 key spec. + + Encodes every byte outside :data:`_S3_SAFE_CHARS` as ``%XX`` with + upper-case hex digits, operating on the UTF-8 bytes so non-ASCII + characters are escaped byte by byte. Returns ``None`` for empty or + missing values so callers can substitute ``"null"``. + """ + if not val: + return None + return "".join( + chr(byte) if chr(byte) in _S3_SAFE_CHARS else f"%{byte:02X}" + for byte in val.encode("utf-8") + ) + def _format_client_context(client: S3StorageDriverClient) -> str: """Format the client's ``describe()`` output as ", k=v, k=v" for error @@ -127,24 +147,20 @@ async def store( (e.g. proto binary). The returned list is the same length as ``payloads``. """ - - def _quote(val: str | None) -> str | None: - return urllib.parse.quote(val, safe="") if val else None - # Build context segments from the target identity. context_segments = "" target = context.target - namespace = _quote(target.namespace) if target is not None else None + namespace = _percent_encode(target.namespace) if target is not None else None namespace_segment = f"/ns/{namespace}" if namespace else "" if isinstance(target, StorageDriverWorkflowInfo): - wf_type = _quote(target.type) or "null" - wf_id = _quote(target.id) or "null" - wf_run_id = _quote(target.run_id) or "null" + wf_type = _percent_encode(target.type) or "null" + wf_id = _percent_encode(target.id) or "null" + wf_run_id = _percent_encode(target.run_id) or "null" context_segments = f"/wt/{wf_type}/wi/{wf_id}/ri/{wf_run_id}" elif isinstance(target, StorageDriverActivityInfo): - act_type = _quote(target.type) or "null" - act_id = _quote(target.id) or "null" - act_run_id = _quote(target.run_id) or "null" + act_type = _percent_encode(target.type) or "null" + act_id = _percent_encode(target.id) or "null" + act_run_id = _percent_encode(target.run_id) or "null" context_segments = f"/at/{act_type}/ai/{act_id}/ri/{act_run_id}" async def _upload(payload: Payload) -> StorageDriverClaim: diff --git a/temporalio/contrib/deepagents/README.md b/temporalio/contrib/deepagents/README.md new file mode 100644 index 000000000..da9092978 --- /dev/null +++ b/temporalio/contrib/deepagents/README.md @@ -0,0 +1,339 @@ +# DeepAgentsPlugin — Temporal plugin for LangChain Deep Agents + +Make a [Deep Agent](https://github.com/langchain-ai/deepagents) durable by adding +one plugin. Build your agent with `create_temporal_deep_agent(...)` (or vanilla +`create_deep_agent(...)`) inside a `@workflow.defn`, add +`plugins=[DeepAgentsPlugin(...)]` to your Client (or Worker), and each LLM call +and each I/O tool call becomes a Temporal Activity — while the agent's control +loop runs, and deterministically replays, inside the Workflow. + +The code you already wrote against `deepagents` does not change: sub-agents, +planning/todo state, the filesystem middleware, human-in-the-loop interrupts, and +`agent.ainvoke(...)` all keep working. You get crash-durability, resumable +human-in-the-loop, and bounded history on top. + +> This package is experimental and may change in future versions. + +## Install + +```bash +uv add "temporalio[deepagents]" +``` + +(or `pip install "temporalio[deepagents]"`). Requires Python ≥ 3.11 (the same +floor `deepagents` sets). + +## Hello world + +```python +import asyncio +from datetime import timedelta + +from deepagents import create_deep_agent # no import guard needed; see below +from temporalio import workflow +from temporalio.client import Client +from temporalio.contrib.deepagents import ( + DeepAgentsPlugin, + create_temporal_deep_agent, +) +from temporalio.worker import Worker + + +@workflow.defn +class ResearchAgent: + @workflow.run + async def run(self, question: str) -> str: + # create_temporal_deep_agent wraps deepagents' create_deep_agent and + # scopes this agent's model-call activity options explicitly. + agent = create_temporal_deep_agent( + model="anthropic:claude-sonnet-4-5", + system_prompt="You are a careful research assistant.", + activity_options={"start_to_close_timeout": timedelta(minutes=5)}, + ) + result = await agent.ainvoke( + {"messages": [{"role": "user", "content": question}]} + ) + return result["messages"][-1].content + + +async def main() -> None: + # API keys live on the worker via the model provider, never in workflow + # inputs or history. The default provider is LangChain's init_chat_model. + plugin = DeepAgentsPlugin() + # Add the plugin on ONE side. The SDK propagates a Client plugin to any + # Worker built from that Client, so the Worker below inherits it. + client = await Client.connect("localhost:7233", plugins=[plugin]) + worker = Worker( + client, + task_queue="deepagents-task-queue", + workflows=[ResearchAgent], + ) + await worker.run() + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +Two things worth noticing: + +- **No `workflow.unsafe.imports_passed_through()` guard.** The plugin + configures the workflow sandbox to pass the `deepagents` / LangChain import + tree through, so workflow files import them like any other module. +- **Vanilla `create_deep_agent(...)` also works.** The plugin substitutes the + durable model automatically whenever `model=` is a name string; use + `create_temporal_deep_agent` when you want to scope `activity_options` to one + agent instead of configuring plugin-wide defaults. + +## What this plugin gives you + +- **Drop-in durability.** `create_deep_agent(...).ainvoke(...)` runs unchanged + inside a Workflow. The loop replays deterministically; every nondeterministic + step (LLM, I/O tool, real filesystem/shell op) is an Activity. +- **One LLM call per Activity.** The Workflow ships only the model *name*; the + worker's `model_provider` builds the real client. Temporal owns retries and + timeouts (LLM-SDK retries are disabled). +- **Sub-agents inherit durability.** Because sub-agents inherit the parent's + `model` object and tools, substituting them once propagates to the whole agent + tree — no per-sub-agent wiring. +- Tools, real-I/O backends, human-in-the-loop, streaming, and continue-as-new + each get a section below. + +## Configuring activity options + +Per agent (recommended): `create_temporal_deep_agent(..., activity_options=...)` +as in Hello world above. Plugin-wide defaults use two keyed maps, because model +calls and tool calls have different timeout profiles: + +```python +from datetime import timedelta + +from temporalio.contrib.deepagents import DeepAgentsPlugin + +plugin = DeepAgentsPlugin( + # A single config, or a map keyed by MODEL name (thinking-mode models get + # longer timeouts than fast ones). + model_activity_options={"start_to_close_timeout": timedelta(minutes=5)}, + # A single config, or a map keyed by TOOL name. + tool_activity_options={"start_to_close_timeout": timedelta(seconds=30)}, +) +``` + +## Tools: the Workflow-vs-Activity choice, made explicit + +A tool that only mutates agent state can run in-workflow; a tool that does real +I/O must not. Both directions are one call: + +```python +from datetime import timedelta + +from langchain_core.tools import tool +from temporalio import activity +from temporalio.contrib.deepagents import activity_as_tool, tool_as_activity + + +@activity.defn +async def get_weather(city: str) -> str: + """Return the current weather for a city.""" + return f"It is sunny and 22C in {city}." + + +@tool +def web_search(query: str) -> str: + """Search the web for a query.""" + return f"Top result for {query!r}: ..." + + +# An existing Temporal activity, exposed to the agent as a tool: +weather_tool = activity_as_tool( + get_weather, start_to_close_timeout=timedelta(seconds=30) +) +# A LangChain tool whose body does I/O, moved into an activity: +search_tool = tool_as_activity( + web_search, start_to_close_timeout=timedelta(seconds=30) +) +``` + +Pass both to `create_temporal_deep_agent(..., tools=[weather_tool, +search_tool])`. An unwrapped, non-builtin tool runs in-workflow and the plugin +warns at construction, so the choice is never silent. Deep Agents' pure +built-ins (`write_todos`, state-backed file tools) stay in-workflow by design. + +## Durable file and shell backends + +Wrap a real-I/O backend (`FilesystemBackend` / `LocalShellBackend` / +`StoreBackend`) in `TemporalBackend` and the agent's *built-in* file and shell +tools execute as durable `deepagents.backend_op` Activities instead of touching +disk from workflow code: + +```python +from datetime import timedelta + +from deepagents.backends import FilesystemBackend +from temporalio import workflow +from temporalio.contrib.deepagents import TemporalBackend, create_temporal_deep_agent + + +@workflow.defn +class FilesystemAgent: + @workflow.run + async def run(self, root_dir: str) -> str: + backend = TemporalBackend( + FilesystemBackend(root_dir=root_dir, virtual_mode=True), + activity_options={"start_to_close_timeout": timedelta(seconds=30)}, + ) + agent = create_temporal_deep_agent( + model="anthropic:claude-sonnet-4-5", + backend=backend, + ) + result = await agent.ainvoke( + {"messages": [{"role": "user", "content": "Take notes as you work."}]} + ) + return result["messages"][-1].content +``` + +State-only backends (the default) need no wrapping — they are pure workflow +state, replayed deterministically. + +## Human-in-the-loop + +With `interrupt_on=...`, the agent pauses before a guarded tool and +`ainvoke(...)` returns the pending approval under the SDK-native +`__interrupt__` key — directly in your workflow. Expose it via a Query and +resume with an Update; no shim exception, the native LangGraph resume protocol +is used as-is: + +```python +from langgraph.checkpoint.memory import InMemorySaver +from langgraph.types import Command +from temporalio import workflow +from temporalio.contrib.deepagents import create_temporal_deep_agent + + +@workflow.defn +class ApprovalAgent: + def __init__(self) -> None: + self._pending: str | None = None + self._decision: str | None = None + + @workflow.run + async def run(self, request: str) -> str: + agent = create_temporal_deep_agent( + model="anthropic:claude-sonnet-4-5", + interrupt_on={"book_trip": True}, + checkpointer=InMemorySaver(), + ) + config = {"configurable": {"thread_id": workflow.info().workflow_id}} + result = await agent.ainvoke( + {"messages": [{"role": "user", "content": request}]}, config=config + ) + if result.get("__interrupt__"): + self._pending = str(result["__interrupt__"][0].value) + await workflow.wait_condition(lambda: self._decision is not None) + result = await agent.ainvoke( + Command(resume={"decisions": [{"type": self._decision}]}), + config=config, + ) + return result["messages"][-1].content + + @workflow.query + def pending_approval(self) -> str | None: + return self._pending + + @workflow.update + async def resume(self, decision: str) -> None: + self._decision = decision +``` + +A client polls `pending_approval`, shows it to a person, and calls the +`resume` update with `"approve"` / `"reject"`. + +## Streaming + +Set `streaming_topic=` on the plugin and model dispatch switches to a streaming +Activity that publishes chunk batches to a +`temporalio.contrib.workflow_streams` topic for live subscribers — while the +aggregated final message still returns to the workflow, so the durable result +is identical to the non-streaming path: + +```python +from temporalio.contrib.deepagents import DeepAgentsPlugin + +plugin = DeepAgentsPlugin(streaming_topic="agent-stream") +``` + +Subscribers read the topic with `WorkflowStreamClient`; each item is an +`AIMessageChunk` in `langchain_core.load.dumpd` form. + +## Continue-as-new: what carries and what does not + +Long conversations bloat workflow history. `run_deep_agent(agent, input, +state_snapshot=...)` snapshots state and continues into a fresh run when the +turn ends with pending todos and the server recommends continuing +(`workflow.info().is_continue_as_new_suggested()`, the default and recommended +mode — it accounts for history length *and* size): + +```python +from deepagents import create_deep_agent +from temporalio import workflow +from temporalio.contrib.deepagents import run_deep_agent + + +@workflow.defn +class LongResearchAgent: + @workflow.run + async def run(self, input: dict, state_snapshot: dict | None = None) -> dict: + agent = create_deep_agent(model="anthropic:claude-sonnet-4-5") + return await run_deep_agent( + agent, + input, + state_snapshot=state_snapshot, + ) +``` + +Pass `continue_as_new_after=N` instead to trigger on a fixed history-event +count. + +- **Carries forward:** the accumulated messages and the model/tool result cache + (so an LLM/tool call completed before the continue-as-new is *not* re-run + after it). Your `@workflow.run` must accept `state_snapshot=None` as shown. +- **Does not carry forward:** anything held only in an in-memory checkpointer's + own structures beyond the messages/todos snapshot. The default in-workflow + `InMemorySaver` is rehydrated for free by deterministic replay; a durable + checkpointer that does its own I/O is not replay-safe from inside a workflow, + and the plugin warns if you pass one — prefer the snapshot + continue-as-new + path above. + +## Runtime behavior + +While a worker built with this plugin is running, the plugin wraps +`deepagents.create_deep_agent` so a bare `model="provider:name"` string is +auto-routed through an Activity. The wrapper only rewrites arguments when called +*inside a workflow*, so importing `deepagents` on a plain client or activity +worker is unaffected, and the original function is restored when the worker +stops. If you would rather be explicit, use `create_temporal_deep_agent` or +pass `TemporalModel("provider:name")` yourself. + +## Composing with other plugins + +This plugin carries no tracing context of its own. For observability, compose it +with `temporalio.contrib.langsmith` or `temporalio.contrib.opentelemetry` — +registration order does not matter: + +```python +from temporalio.client import Client +from temporalio.contrib.deepagents import DeepAgentsPlugin + + +async def connect(): + return await Client.connect( + "localhost:7233", + plugins=[ + # LangSmithPlugin(), # or OpenTelemetryPlugin(), in either order + DeepAgentsPlugin(), + ], + ) +``` + +For agents built directly as LangGraph graphs (rather than a compiled Deep +Agent), see `temporalio.contrib.langgraph`. diff --git a/temporalio/contrib/deepagents/__init__.py b/temporalio/contrib/deepagents/__init__.py new file mode 100644 index 000000000..26febecb3 --- /dev/null +++ b/temporalio/contrib/deepagents/__init__.py @@ -0,0 +1,68 @@ +"""Temporal plugin for LangChain Deep Agents. + +Make an existing Deep Agent durable by adding one plugin: build your agent with +``create_deep_agent(...)`` inside a ``@workflow.defn`` and add +``plugins=[DeepAgentsPlugin(...)]`` to your Client or Worker. Each LLM call and +each I/O tool call becomes a Temporal activity, while the agent's control loop +runs — and deterministically replays — inside the workflow. + +.. warning:: + This package is experimental and may change in future versions. + +The public names are imported lazily so ``import temporalio.contrib.deepagents`` +succeeds before LangChain is installed; touching a name that needs LangChain +imports it on first access. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +__all__ = [ + "DeepAgentsPlugin", + "TemporalModel", + "TemporalBackend", + "activity_as_tool", + "tool_as_activity", + "run_deep_agent", + "create_temporal_deep_agent", + "DeepAgentsWorkflowError", +] + +if TYPE_CHECKING: + from temporalio.contrib.deepagents._model import TemporalModel + from temporalio.contrib.deepagents._plugin import DeepAgentsPlugin + from temporalio.contrib.deepagents._tools import ( + TemporalBackend, + activity_as_tool, + tool_as_activity, + ) + from temporalio.contrib.deepagents.workflow import ( + DeepAgentsWorkflowError, + create_temporal_deep_agent, + run_deep_agent, + ) + + +def __getattr__(name: str) -> object: + if name == "DeepAgentsPlugin": + from temporalio.contrib.deepagents._plugin import DeepAgentsPlugin + + return DeepAgentsPlugin + if name == "TemporalModel": + from temporalio.contrib.deepagents._model import TemporalModel + + return TemporalModel + if name in ("TemporalBackend", "activity_as_tool", "tool_as_activity"): + from temporalio.contrib.deepagents import _tools + + return getattr(_tools, name) + if name in ( + "DeepAgentsWorkflowError", + "run_deep_agent", + "create_temporal_deep_agent", + ): + from temporalio.contrib.deepagents import workflow + + return getattr(workflow, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/temporalio/contrib/deepagents/_activity.py b/temporalio/contrib/deepagents/_activity.py new file mode 100644 index 000000000..3c1501a42 --- /dev/null +++ b/temporalio/contrib/deepagents/_activity.py @@ -0,0 +1,363 @@ +"""The activities that carry every nondeterministic Deep Agents operation. + +The Deep Agents control loop runs *inside* the workflow; the operations that +must not run there — talking to an LLM, executing a tool that does real I/O, or +touching a real filesystem / shell backend — are moved out to these activities. + +Each activity is a method on :class:`DeepAgentActivities` so the worker-only +dependencies (the ``model_provider`` that builds real chat models from a name, +the streaming batch interval) can be captured on the instance rather than +smuggled through activity inputs. API keys therefore live on the worker, never +in a workflow input or in history. + +Every method: + +* takes a single serializable dataclass in and returns a single dataclass out + (LangChain objects travel as their ``dumpd`` JSON form via + :mod:`temporalio.contrib.deepagents._serde`); +* translates the LLM SDK's HTTP error into Temporal's retry contract so a 429 + honors the upstream ``retry-after`` instead of hammering it; +* heartbeats on a background task so a slow (thinking-mode / long-context) call + is not mistaken for a stuck worker. +""" + +from __future__ import annotations + +import asyncio +import dataclasses +import importlib +from datetime import timedelta +from functools import wraps +from typing import Any, Callable + +from temporalio import activity +from temporalio.contrib.deepagents import _serde +from temporalio.exceptions import ApplicationError + +# Activity type names. The workflow dispatches by these strings, so the +# in-workflow model / tool stubs never import the activity class itself. +INVOKE_MODEL = "deepagents.invoke_model" +INVOKE_MODEL_STREAMING = "deepagents.invoke_model_streaming" +INVOKE_TOOL = "deepagents.invoke_tool" +BACKEND_OP = "deepagents.backend_op" + + +# --------------------------------------------------------------------------- +# Boundary payloads +# --------------------------------------------------------------------------- + + +@dataclasses.dataclass +class ModelActivityInput: + """A single LLM request. + + ``model_name`` is resolved to a real model by the worker's + ``model_provider``; the workflow never ships credentials. + """ + + model_name: str + messages: list[Any] + """Messages in ``langchain_core.load.dumpd`` form.""" + tool_schemas: list[dict[str, Any]] = dataclasses.field(default_factory=list) + """OpenAI-format tool advertisements (name + description + argument schema).""" + bind_kwargs: dict[str, Any] = dataclasses.field(default_factory=dict) + config: dict[str, Any] = dataclasses.field(default_factory=dict) + """A stripped ``RunnableConfig`` (see :func:`_serde.strip_runnable_config`).""" + streaming_topic: str | None = None + + +@dataclasses.dataclass +class ModelActivityOutput: + """The model's reply. + + ``message`` is an ``AIMessage`` in ``dumpd`` form, carrying tool calls, + usage, and response metadata. + """ + + message: Any + + +@dataclasses.dataclass +class ToolActivityInput: + """One tool execution routed to an activity.""" + + tool_name: str + tool_call_id: str + args: dict[str, Any] + config: dict[str, Any] = dataclasses.field(default_factory=dict) + + +@dataclasses.dataclass +class ToolActivityOutput: + """A tool result as a ``ToolMessage`` in ``dumpd`` form.""" + + message: Any + + +@dataclasses.dataclass +class BackendOpInput: + """A single filesystem / shell / store operation for a wrapped backend.""" + + backend_ref: str + """Key identifying which registered backend to act on.""" + op: str + """Backend method name, e.g. ``ls`` / ``read_file`` / ``write_file`` / ``execute``.""" + args: list[Any] = dataclasses.field(default_factory=list) + kwargs: dict[str, Any] = dataclasses.field(default_factory=dict) + + +@dataclasses.dataclass +class BackendOpOutput: + """The backend operation's return value. + + Deepagents protocol dataclasses (``WriteResult`` / ``ReadResult`` / …) + ride in the tagged form produced by ``_serde.dump_backend_result`` so the + in-workflow stub can rebuild the real type; plain JSON values pass + through unchanged. + """ + + result: Any + + +# --------------------------------------------------------------------------- +# Heartbeating + error translation +# --------------------------------------------------------------------------- + + +def _auto_heartbeater(fn: Callable) -> Callable: + """Heartbeat at half the configured ``heartbeat_timeout`` while ``fn`` runs. + + Long LLM calls (thinking mode, long context, streaming accumulation) can run + well past a scheduler's patience; without a heartbeat Temporal would cancel + them and surface a ``HeartbeatTimeoutError`` instead of the real problem. + """ + + @wraps(fn) + async def wrapped(*args: Any, **kwargs: Any) -> Any: + heartbeat_timeout = activity.info().heartbeat_timeout + beat_task: asyncio.Task | None = None + if heartbeat_timeout: + interval = heartbeat_timeout.total_seconds() / 2 + + async def beat() -> None: + while True: + activity.heartbeat() + await asyncio.sleep(interval) + + beat_task = asyncio.create_task(beat()) + try: + return await fn(*args, **kwargs) + finally: + if beat_task is not None: + beat_task.cancel() + # Let the cancellation land before returning so no pending task + # outlives the activity (a bare ``cancel()`` leaves the task to + # be destroyed while pending if the loop shuts down first). + # ``asyncio.wait`` never re-raises the task's CancelledError. + await asyncio.wait([beat_task]) + + return wrapped + + +def _translate_api_error(exc: Exception) -> ApplicationError | None: + """Map an LLM SDK HTTP error onto Temporal's retry contract. + + Works by duck typing so neither ``openai`` nor ``anthropic`` needs to be + imported here: both expose ``status_code`` and ``response.headers``. Returns + ``None`` when ``exc`` is not a recognizable HTTP status error, so the caller + can fall through to its generic handling. + """ + status = getattr(exc, "status_code", None) + if status is None: + return None + headers: dict[str, Any] = {} + response = getattr(exc, "response", None) + if response is not None: + headers = dict(getattr(response, "headers", {}) or {}) + # Case-insensitive header access. + lower = {str(k).lower(): v for k, v in headers.items()} + + retryable = status in (408, 409, 429) or 500 <= status < 600 + should_retry = lower.get("x-should-retry") + if should_retry == "false": + retryable = False + elif should_retry == "true": + retryable = True + + delay_ms = lower.get("retry-after-ms") + retry_after = lower.get("retry-after") + next_delay: timedelta | None = None + try: + if delay_ms is not None: + next_delay = timedelta(milliseconds=int(delay_ms)) + elif retry_after is not None: + next_delay = timedelta(seconds=int(retry_after)) + except (TypeError, ValueError): + next_delay = None + + return ApplicationError( + str(exc), + type=type(exc).__name__, + non_retryable=not retryable, + next_retry_delay=next_delay, + ) + + +# --------------------------------------------------------------------------- +# The activities +# --------------------------------------------------------------------------- + + +def _default_model_provider(model_name: str) -> Any: + """Build a chat model from a name string with LLM-SDK retries disabled. + + Temporal owns retries; the model client must not also retry, or a single + logical attempt fans out into nested retry storms that Temporal can neither + see nor bound. + """ + # importlib: `langchain` (unlike langchain-core) is absent on Python 3.10 + # environments where the deepagents extra cannot install; a static import + # here fails type-checking there. + init_chat_model = importlib.import_module("langchain.chat_models").init_chat_model + + return init_chat_model(model_name, max_retries=0) + + +class DeepAgentActivities: + """Holds the worker-side dependencies and exposes the four activities. + + An instance is created by ``DeepAgentsPlugin`` + and its bound methods are registered on the worker. + """ + + def __init__( + self, + *, + model_provider: Callable[[str], Any] | None = None, + streaming_batch_interval: timedelta = timedelta(milliseconds=100), + ) -> None: + """Store the worker-side model provider + streaming configuration.""" + self._model_provider = model_provider or _default_model_provider + self._streaming_batch_interval = streaming_batch_interval + + def _build_bound_model(self, input: ModelActivityInput) -> Any: + model = self._model_provider(input.model_name) + if input.bind_kwargs: + model = model.bind(**input.bind_kwargs) + if input.tool_schemas: + model = model.bind_tools(input.tool_schemas) + return model + + @activity.defn(name=INVOKE_MODEL) + @_auto_heartbeater + async def invoke_model(self, input: ModelActivityInput) -> ModelActivityOutput: + """Run exactly one LLM call and return the resulting ``AIMessage``.""" + messages = _serde.load_messages(input.messages) + config = _serde.rebuild_runnable_config(input.config) + model = self._build_bound_model(input) + try: + message = await model.ainvoke(messages, config=config) + except Exception as exc: + translated = _translate_api_error(exc) + if translated is not None: + activity.logger.warning( + "Model call failed with an HTTP status error", exc_info=True + ) + raise translated from exc + raise + return ModelActivityOutput(message=_serde.dump_object(message)) + + @activity.defn(name=INVOKE_MODEL_STREAMING) + @_auto_heartbeater + async def invoke_model_streaming( + self, input: ModelActivityInput + ) -> ModelActivityOutput: + """Stream one LLM call, publishing chunk batches to ``streaming_topic``. + + Token-level deltas are coalesced at ``streaming_batch_interval`` and + pushed to external subscribers via the shared workflow-streams topic; the + aggregated final ``AIMessage`` is returned to the workflow so the + durable result is identical to the non-streaming path. + """ + from temporalio.contrib.workflow_streams import WorkflowStreamClient + + messages = _serde.load_messages(input.messages) + config = _serde.rebuild_runnable_config(input.config) + model = self._build_bound_model(input) + + final: Any = None + try: + async with WorkflowStreamClient.from_within_activity( + batch_interval=self._streaming_batch_interval + ) as client: + topic = ( + client.topic(input.streaming_topic) + if input.streaming_topic + else None + ) + async for chunk in model.astream(messages, config=config): + if topic is not None: + topic.publish(_serde.dump_object(chunk)) + final = chunk if final is None else final + chunk + except Exception as exc: + translated = _translate_api_error(exc) + if translated is not None: + activity.logger.warning( + "Streaming model call failed with an HTTP status error", + exc_info=True, + ) + raise translated from exc + raise + return ModelActivityOutput(message=_serde.dump_object(final)) + + @activity.defn(name=INVOKE_TOOL) + @_auto_heartbeater + async def invoke_tool(self, input: ToolActivityInput) -> ToolActivityOutput: + """Execute one registered tool and return its ``ToolMessage``.""" + from temporalio.contrib.deepagents._tools import get_registered_tool + + tool = get_registered_tool(input.tool_name) + if tool is None: + raise ApplicationError( + f"Tool {input.tool_name!r} is not registered on this worker. " + f"Wrap it with tool_as_activity(...) or activity_as_tool(...).", + type="DeepAgentsUnknownTool", + non_retryable=True, + ) + config = _serde.rebuild_runnable_config(input.config) + tool_call = { + "name": input.tool_name, + "args": input.args, + "id": input.tool_call_id, + "type": "tool_call", + } + message = await tool.ainvoke(tool_call, config=config) + return ToolActivityOutput(message=_serde.dump_object(message)) + + @activity.defn(name=BACKEND_OP) + @_auto_heartbeater + async def backend_op(self, input: BackendOpInput) -> BackendOpOutput: + """Run one operation against a registered (real-I/O) backend.""" + from temporalio.contrib.deepagents._tools import registered_backends + + backend = registered_backends().get(input.backend_ref) + if backend is None: + raise ApplicationError( + f"Backend {input.backend_ref!r} is not registered on this worker.", + type="DeepAgentsUnknownBackend", + non_retryable=True, + ) + method = getattr(backend, input.op, None) + if method is None: + raise ApplicationError( + f"Backend {input.backend_ref!r} has no operation {input.op!r}.", + type="DeepAgentsUnknownBackendOp", + non_retryable=True, + ) + result = method(*input.args, **input.kwargs) + if asyncio.iscoroutine(result): + result = await result + # Protocol results are plain dataclasses whose attributes the + # middleware reads in-workflow — tag them so the stub can rebuild + # the real type instead of receiving a decayed dict. + return BackendOpOutput(result=_serde.dump_backend_result(result)) diff --git a/temporalio/contrib/deepagents/_model.py b/temporalio/contrib/deepagents/_model.py new file mode 100644 index 000000000..2d97f992d --- /dev/null +++ b/temporalio/contrib/deepagents/_model.py @@ -0,0 +1,372 @@ +"""The model seam: a chat model whose every call becomes a Temporal activity. + +:class:`TemporalModel` is a real ``BaseChatModel``. Placed anywhere Deep Agents +expects a model, it routes each generation through the ``deepagents.invoke_model`` +activity instead of calling the provider inline. Because Deep Agents' sub-agents +inherit the parent's ``model`` instance by default, substituting this one object +makes every model call in the whole agent tree durable — no middleware injection +that a sub-agent could silently miss. + +Two ways to get one: + +* explicit — the user writes ``TemporalModel("anthropic:claude-...")`` and hands + it to ``create_deep_agent(model=...)``; this is the public type users assert + against; +* implicit — while running inside a workflow, the plugin patches + ``create_deep_agent`` so a bare ``model="anthropic:..."`` string is wrapped + automatically (see :func:`install_model_patch`). + +Worker-wide dispatch defaults (activity options, streaming topic) live in +:class:`temporalio.contrib.deepagents._serde.Settings`, a module global set by +the plugin. They live in ``_serde`` (not here) so the plugin can configure them +without importing this module — which would drag LangChain into plugin +construction. This module is under ``temporalio``, which the workflow sandbox +passes through, so the object the workflow reads is the same one the plugin set. +""" + +from __future__ import annotations + +import importlib +from collections.abc import AsyncIterator, Iterator, Mapping, Sequence +from datetime import timedelta +from typing import Any + +from temporalio import workflow +from temporalio.contrib.deepagents import _activity, _serde + +with workflow.unsafe.imports_passed_through(): + from langchain_core.callbacks import ( + AsyncCallbackManagerForLLMRun, + CallbackManagerForLLMRun, + ) + from langchain_core.language_models import BaseChatModel + from langchain_core.messages import AIMessageChunk, BaseMessage + from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult + + +def _as_message_chunk(message: Any) -> Any: + """Re-shape a finished ``AIMessage`` as the ``AIMessageChunk`` a stream yields.""" + return AIMessageChunk( + content=getattr(message, "content", ""), + additional_kwargs=getattr(message, "additional_kwargs", {}) or {}, + response_metadata=getattr(message, "response_metadata", {}) or {}, + id=getattr(message, "id", None), + tool_calls=getattr(message, "tool_calls", []) or [], + usage_metadata=getattr(message, "usage_metadata", None), + ) + + +# --------------------------------------------------------------------------- +# Activity-option resolution (worker-wide defaults live in _serde.Settings) +# --------------------------------------------------------------------------- + + +_DEFAULT_MODEL_TIMEOUT = timedelta(minutes=5) + + +def _resolve_activity_options( + model_name: str, instance_options: Mapping[str, Any] | None +) -> dict[str, Any]: + """Merge worker defaults with a per-instance override into execute_activity kwargs.""" + opts: dict[str, Any] = {} + default = _serde.get_settings().model_activity_options + if isinstance(default, Mapping) and not _looks_like_activity_config(default): + # Mapping[model_name, ActivityConfig]: pick this model's entry. + chosen = default.get(model_name) + if isinstance(chosen, Mapping): + opts.update(chosen) + elif isinstance(default, Mapping): + opts.update(default) + if instance_options: + opts.update(instance_options) + opts.setdefault("start_to_close_timeout", _DEFAULT_MODEL_TIMEOUT) + return opts + + +def _looks_like_activity_config(m: Mapping[str, Any]) -> bool: + """A single ``ActivityConfig`` has known option keys; a per-model map does not.""" + known = { + "task_queue", + "schedule_to_close_timeout", + "schedule_to_start_timeout", + "start_to_close_timeout", + "heartbeat_timeout", + "retry_policy", + "cancellation_type", + "activity_id", + "versioning_intent", + "summary", + "priority", + } + return bool(m) and all(k in known for k in m) + + +# --------------------------------------------------------------------------- +# The model +# --------------------------------------------------------------------------- + + +class TemporalModel(BaseChatModel): + """A ``BaseChatModel`` that runs each generation as a Temporal activity. + + Args: + model: The provider model name resolved worker-side by the plugin's + ``model_provider`` (e.g. ``"anthropic:claude-sonnet-4-5"``). Only the + name crosses the workflow boundary; credentials stay on the worker. + activity_options: Optional per-model ``execute_activity`` overrides + (timeouts, retry policy). Falls back to the plugin's + ``model_activity_options``. + """ + + model: str + activity_options: dict[str, Any] | None = None + + # ``protected_namespaces=()`` silences pydantic's warning about the ``model`` + # field colliding with its ``model_`` namespace. + model_config = {"arbitrary_types_allowed": True, "protected_namespaces": ()} + + @property + def _llm_type(self) -> str: + return "temporal-deepagents" + + def bind_tools( + self, + tools: Sequence[Any], + *, + tool_choice: Any | None = None, + **kwargs: Any, + ) -> Any: + """Bind tools to the model the way LangChain's ``create_agent`` expects. + + ``BaseChatModel.bind_tools`` is abstract (raises ``NotImplementedError``), + but the agent factory calls ``model.bind_tools(tools, ...)`` on every model + node — so a durable model must implement it or the whole loop dies. The + tools are converted to their JSON schema *now* (at bind time) so the bound + object is serialization-safe, and carried as the ``tools`` kwarg that + :meth:`_build_input` already reads and forwards to the activity, where the + real provider model is what actually binds them. + """ + schemas = [ + t if isinstance(t, dict) else _serde.tool_to_schema(t) for t in tools + ] + if tool_choice is not None: + kwargs["tool_choice"] = tool_choice + return self.bind(tools=schemas, **kwargs) + + def _summary(self) -> str: + return f"invoke_model[{self.model}]" + + def _build_input( + self, + messages: Sequence[BaseMessage], + streaming_topic: str | None, + **kwargs: Any, + ) -> _activity.ModelActivityInput: + tools = kwargs.get("tools") or [] + tool_schemas = [ + t if isinstance(t, dict) else _serde.tool_to_schema(t) for t in tools + ] + bind_kwargs = { + k: v + for k, v in kwargs.items() + if k not in ("tools", "config", "run_manager", "stop", "callbacks") + and _serde._is_jsonish(v) + } + if kwargs.get("stop"): + bind_kwargs["stop"] = kwargs["stop"] + return _activity.ModelActivityInput( + model_name=self.model, + messages=_serde.dump_messages(messages), + tool_schemas=tool_schemas, + bind_kwargs=bind_kwargs, + config=_serde.strip_runnable_config(kwargs.get("config") or {}), + streaming_topic=streaming_topic, + ) + + def _generate( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: CallbackManagerForLLMRun | None = None, + **kwargs: Any, + ) -> ChatResult: + raise NotImplementedError( + "TemporalModel is async-only inside a Temporal workflow. Drive the " + "agent with `await agent.ainvoke(...)` / `.astream(...)`, which uses " + "the async model path." + ) + + async def _agenerate( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: AsyncCallbackManagerForLLMRun | None = None, + **kwargs: Any, + ) -> ChatResult: + from temporalio.contrib.deepagents.workflow import call_model + + activity_input = self._build_input(messages, None, stop=stop, **kwargs) + opts = _resolve_activity_options(self.model, self.activity_options) + output = await call_model( + _activity.INVOKE_MODEL, + activity_input, + summary=self._summary(), + **opts, + ) + message = _serde.load_object(output.message) + return ChatResult(generations=[ChatGeneration(message=message)]) + + async def _astream( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: AsyncCallbackManagerForLLMRun | None = None, + **kwargs: Any, + ) -> AsyncIterator[ChatGenerationChunk]: + from temporalio.contrib.deepagents.workflow import call_model + + topic = _serde.get_settings().streaming_topic + opts = _resolve_activity_options(self.model, self.activity_options) + if topic: + activity_input = self._build_input(messages, topic, stop=stop, **kwargs) + output = await call_model( + _activity.INVOKE_MODEL_STREAMING, + activity_input, + summary=f"invoke_model_streaming[{self.model}]", + **opts, + ) + else: + # No topic configured: fall back to a single response-level chunk. + activity_input = self._build_input(messages, None, stop=stop, **kwargs) + output = await call_model( + _activity.INVOKE_MODEL, + activity_input, + summary=self._summary(), + **opts, + ) + message = _serde.load_object(output.message) + yield ChatGenerationChunk(message=_as_message_chunk(message)) + + def _stream( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: CallbackManagerForLLMRun | None = None, + **kwargs: Any, + ) -> Iterator[ChatGenerationChunk]: + raise NotImplementedError( + "TemporalModel streaming is async-only; use `agent.astream(...)`." + ) + + +# --------------------------------------------------------------------------- +# create_deep_agent model patch (implicit wrapping) +# --------------------------------------------------------------------------- +# +# The durability seam is ``deepagents._models.resolve_model``, which +# ``create_deep_agent`` calls to turn a ``model=`` string (or instance) into a +# ``BaseChatModel`` — for both the top-level agent and every ``SubAgent`` +# (``graph.py`` lines 592 and 634). We patch it on the ``deepagents.graph`` +# module, where ``create_deep_agent``'s body resolves the ``resolve_model`` name +# at call time. +# +# Patching *this* seam (not ``deepagents.create_deep_agent``) is what makes the +# rewrite survive the user's import style. A user who writes the idiomatic +# ``from deepagents import create_deep_agent`` binds the *original* function +# object into their module; rebinding the ``deepagents.create_deep_agent`` +# attribute would never be seen by that already-bound reference, so string +# models would reach the real provider inside the workflow (a hang / non- +# determinism). ``create_deep_agent``'s body, by contrast, always looks up +# ``resolve_model`` in the ``deepagents.graph`` globals afresh on each call, so +# rebinding it there is observed no matter how the caller imported the factory. +# It also preserves ``_model_spec`` (the original string), which the factory +# reads *before* calling ``resolve_model`` for harness-profile lookup. +# +# ``create_deep_agent`` is still wrapped separately, best-effort, purely to fire +# the construction-time warnings that need the ``tools`` / ``checkpointer`` +# kwargs (those warnings are advisory and carry no durability weight). + +_original_create_deep_agent: Any = None +_original_resolve_model: Any = None + + +def _wrap_model_arg(model: Any) -> Any: + """Resolve a ``create_deep_agent`` model argument to a durable model. + + A name string becomes a :class:`TemporalModel`. A :class:`TemporalModel` + passes through. Any other live ``BaseChatModel`` instance is rejected at the + workflow boundary: it has no name the activity could rebuild from, so it would + run its provider call *inside the workflow* — nondeterministic and unsafe. + """ + from temporalio.contrib.deepagents.workflow import DeepAgentsWorkflowError + + if isinstance(model, str): + return TemporalModel(model=model) + if isinstance(model, TemporalModel): + return model + if isinstance(model, BaseChatModel): + raise DeepAgentsWorkflowError( + f"create_deep_agent received a live {type(model).__name__} model " + f"instance, which would run inside the workflow (nondeterministic). " + f'Pass model="provider:name" (auto-routed through an activity) or ' + f"wrap it as TemporalModel(...) instead." + ) + return model + + +def install_model_patch() -> None: + """Route Deep Agents' model resolution through :class:`TemporalModel`. + + Patches ``deepagents.graph.resolve_model`` (the seam ``create_deep_agent`` + uses for the main agent *and* every sub-agent) so a bare ``model="..."`` + string becomes a durable :class:`TemporalModel`, and additionally wraps + ``deepagents.create_deep_agent`` to fire the advisory tool / checkpointer + warnings. Both only act when called inside a workflow, so importing + deepagents on a plain client / activity worker is unaffected. Idempotent. + """ + global _original_create_deep_agent, _original_resolve_model + # importlib: `deepagents` is absent on Python 3.10 environments (its floor + # is 3.11), so static imports here fail type-checking there. + deepagents = importlib.import_module("deepagents") + _graph = importlib.import_module("deepagents.graph") + + if _original_resolve_model is None: + _original_resolve_model = _graph.resolve_model + + def patched_resolve_model(model: Any) -> Any: + if workflow.in_workflow(): + return _wrap_model_arg(model) + return _original_resolve_model(model) + + setattr(_graph, "resolve_model", patched_resolve_model) + + if _original_create_deep_agent is None: + _original_create_deep_agent = deepagents.create_deep_agent + + def patched(*args: Any, **kwargs: Any) -> Any: + if workflow.in_workflow(): + from temporalio.contrib.deepagents._tools import warn_unwrapped_tools + from temporalio.contrib.deepagents.workflow import ( + warn_durable_checkpointer, + ) + + warn_unwrapped_tools(kwargs.get("tools")) + warn_durable_checkpointer(kwargs.get("checkpointer")) + return _original_create_deep_agent(*args, **kwargs) + + setattr(deepagents, "create_deep_agent", patched) + + +def uninstall_model_patch() -> None: + """Restore the original ``resolve_model`` / ``create_deep_agent``.""" + global _original_create_deep_agent, _original_resolve_model + if _original_resolve_model is not None: + _graph = importlib.import_module("deepagents.graph") + + setattr(_graph, "resolve_model", _original_resolve_model) + _original_resolve_model = None + if _original_create_deep_agent is not None: + deepagents = importlib.import_module("deepagents") + + setattr(deepagents, "create_deep_agent", _original_create_deep_agent) + _original_create_deep_agent = None diff --git a/temporalio/contrib/deepagents/_plugin.py b/temporalio/contrib/deepagents/_plugin.py new file mode 100644 index 000000000..3b95fcdaf --- /dev/null +++ b/temporalio/contrib/deepagents/_plugin.py @@ -0,0 +1,296 @@ +"""The plugin object users add to ``plugins=[...]``. + +:class:`DeepAgentsPlugin` wires everything together so that existing +``deepagents`` code — ``create_deep_agent(...).ainvoke(...)`` — runs durably +inside a ``@workflow.defn`` with no other changes: + +* registers the four activities that carry the nondeterministic work; +* installs the LangChain-aware data converter (composing, never clobbering, a + user converter); +* passes the LangChain / LangGraph / deepagents import tree through the workflow + sandbox; +* wraps the worker run in a ``run_context`` that patches Deep Agents' model + resolution seam (``deepagents.graph.resolve_model``) to auto-route bare + ``model=`` strings through activities — regardless of how the user imported + ``create_deep_agent``; +* registers :class:`DeepAgentsWorkflowError` as a workflow-failure type; +* pushes the model / tool dispatch defaults down to the seams that read them. + +The plugin auto-propagates from a ``Client`` to any ``Worker`` built from it, so +add it on exactly one side. ``configure_worker`` additionally de-duplicates +activities by name, so a user who mistakenly adds it on both sides gets a clean +no-op instead of a "More than one activity named ..." crash. +""" + +from __future__ import annotations + +import sys +import warnings +from collections.abc import AsyncIterator, Sequence +from contextlib import asynccontextmanager +from dataclasses import replace +from datetime import timedelta +from typing import Any, Callable + +from temporalio import activity as activity_mod +from temporalio.contrib.deepagents import _serde, _tools +from temporalio.contrib.deepagents._activity import DeepAgentActivities +from temporalio.contrib.deepagents.workflow import DeepAgentsWorkflowError +from temporalio.plugin import SimplePlugin +from temporalio.worker import WorkflowRunner +from temporalio.worker.workflow_sandbox import SandboxedWorkflowRunner + +# Runtime floor for the Deep Agents control loop. Kept in a constant so +# static checkers do not narrow `sys.version_info` comparisons into +# unreachable-code findings on new interpreters. +_MIN_PYTHON = (3, 11) + + +class DeepAgentsPlugin(SimplePlugin): + """Temporal plugin that makes LangChain Deep Agents durable. + + Args: + model_provider: Builds a real chat model from a name string, worker-side. + This is where API keys live; only the model name ever crosses the + workflow boundary. Defaults to LangChain's ``init_chat_model`` with + LLM-SDK retries disabled (Temporal owns retries). + model_activity_options: ``ActivityConfig`` (or ``Mapping[model_name, + ActivityConfig]``) for the model activities — timeouts, retry policy. + tool_activity_options: ``ActivityConfig`` (or ``Mapping[tool_name, + ActivityConfig]``) default for tools wrapped with ``tool_as_activity``. + streaming_topic: When set, model calls stream through the streaming + activity and publish chunk batches to this workflow-streams topic. + streaming_batch_interval: How long the streaming activity coalesces + chunks before publishing a batch. + passthrough_modules: Extra sandbox-passthrough modules, merged with the + plugin's LangChain/deepagents defaults. + data_converter: Override the default LangChain-aware converter. ``None`` + installs the default; the SDK default is upgraded in place; any other + converter raises (fold ``DeepAgentsPayloadConverter`` into your own). + """ + + def __init__( + self, + *, + model_provider: Callable[[str], Any] | None = None, + model_activity_options: Any = None, + tool_activity_options: Any = None, + streaming_topic: str | None = None, + streaming_batch_interval: timedelta = timedelta(milliseconds=100), + passthrough_modules: Sequence[str] | None = None, + data_converter: Any = None, + ) -> None: + """Configure the plugin; see the class docstring for parameters.""" + if sys.version_info < _MIN_PYTHON: + warnings.warn( + "DeepAgentsPlugin requires Python >= 3.11 (deepagents pins " + ">=3.11); the Deep Agents control loop relies on contextvars " + "propagation through asyncio that older versions lack.", + stacklevel=2, + ) + + self._passthrough_modules = passthrough_modules + # Held on the instance so the wiring is statically traceable: each is + # passed straight into the call that consumes it (below), not stashed as + # dead config. + self._tool_activity_options = tool_activity_options + self._data_converter = data_converter + + # Push dispatch defaults down to the model and tool seams that read them. + # These live in ``_serde`` / ``_tools`` (langchain-free modules) so + # constructing the plugin never imports LangChain. ``tool_activity_options`` + # is stored under ``_tools._tool_defaults`` and read back on the tool + # dispatch path by ``_tools._resolve_tool_options(...)`` — which + # ``tool_as_activity`` calls to compute each tool activity's timeout/retry + # when the caller does not override ``activity_options``. + _serde.set_settings( + model_activity_options=model_activity_options, + streaming_topic=streaming_topic, + ) + # wired-via-composition: consumed on the tool dispatch path in _tools.py + # (_resolve_tool_options), renamed to ``options`` at the callsite. + _tools.set_tool_defaults(self._tool_activity_options) + + # The activities that carry the nondeterministic work. model_provider and + # the batch interval are captured here so API keys never enter an input. + self._activities = DeepAgentActivities( + model_provider=model_provider, + streaming_batch_interval=streaming_batch_interval, + ) + activities = [ + self._activities.invoke_model, + self._activities.invoke_model_streaming, + self._activities.invoke_tool, + self._activities.backend_op, + ] + + super().__init__( + "langchain.DeepAgentsPlugin", + activities=activities, + # wired-via-composition: ``data_converter`` flows into SimplePlugin's + # own ``data_converter`` kwarg (renamed to ``user_converter`` inside + # build_data_converter), which installs it on the client/worker. + data_converter=_serde.build_data_converter(self._data_converter), + workflow_runner=self._make_workflow_runner(), + workflow_failure_exception_types=[DeepAgentsWorkflowError], + run_context=self._run_context, + ) + + # -- sandbox passthrough ------------------------------------------------- + + def _make_workflow_runner( + self, + ) -> Callable[[WorkflowRunner | None], WorkflowRunner]: + modules = _serde.resolve_passthrough_modules(self._passthrough_modules) + + def workflow_runner(runner: WorkflowRunner | None) -> WorkflowRunner: + if runner is None: + raise ValueError("No WorkflowRunner provided to DeepAgentsPlugin.") + if isinstance(runner, SandboxedWorkflowRunner): + return replace( + runner, + restrictions=runner.restrictions.with_passthrough_modules(*modules), + ) + return runner + + return workflow_runner + + # -- run context --------------------------------------------------------- + + @asynccontextmanager + async def _run_context(self) -> AsyncIterator[None]: + """Patch Deep Agents' model resolution seam for the worker's lifetime. + + The patch (on ``deepagents.graph.resolve_model``, the seam + ``create_deep_agent`` uses for the agent and every sub-agent) only + rewrites ``model=`` strings when running inside a workflow, so it is inert + on plain clients / activity workers. Determinism itself + rides on the same mechanism ``contrib.langgraph`` uses — the loop is + in-workflow and every nondeterministic call is an activity — so no + speculative time/uuid shims are installed here. + + One shim *is* required: LangChain's ``create_agent`` factory wraps every + model node with LangSmith's ``@traceable``, whose async path hops onto a + thread via ``asyncio.run_in_executor`` — which the deterministic workflow + event loop does not implement (it raises ``NotImplementedError``). Tracing + is an observability concern that must not run in-workflow, so we install + LangSmith's own Temporal escape hatch (``set_runtime_overrides``) to run + that setup inline when ``in_workflow()``, and defer to the default thread + hop everywhere else (activities / clients, where tracing is fine). + """ + # Imported lazily (not at module top) so plugin construction stays + # langchain-free; the patch is only needed once the worker is running. + # The import itself is inside the guard: on a worker without LangChain + # installed (e.g. one that only runs the plugin's determinism / failure + # paths) importing ``_model`` raises ``ModuleNotFoundError``, and the + # worker must still start — it simply runs without the auto-wrap patch. + patched = False + try: + from temporalio.contrib.deepagents import _model, _tools + + _model.install_model_patch() + _tools.install_backend_async_patch() + patched = True + except ImportError as exc: # LangChain / deepagents absent on this worker + # Deliberately ImportError only: a *missing* optional dependency + # must not stop the worker, but a genuine patch-installation bug + # (e.g. upstream renaming the seam raises AttributeError) must + # surface at startup, not degrade into silent in-workflow calls. + warnings.warn( + f"DeepAgentsPlugin could not patch create_deep_agent ({exc}); " + "use explicit TemporalModel(...) instances to route model calls " + "through activities.", + stacklevel=2, + ) + # Installed for the life of the process, never uninstalled — see + # _install_langsmith_temporal_override for why. + _install_langsmith_temporal_override() + try: + yield + finally: + if patched: + # Import is cached: patched=True implies the import above succeeded. + from temporalio.contrib.deepagents import _model, _tools + + _model.uninstall_model_patch() + _tools.uninstall_backend_async_patch() + + # -- worker config ------------------------------------------------------- + + def configure_worker(self, config: Any) -> Any: + """Deduplicate activity registrations after the base configuration.""" + config = super().configure_worker(config) + activities = config.get("activities") + if activities: + config["activities"] = _dedupe_activities(activities) + return config + + +async def _temporal_aio_to_thread( + default_aio_to_thread: Callable[..., Any], + ctx: Any, + func: Callable[..., Any], + /, + *args: Any, + **kwargs: Any, +) -> Any: + """Run LangSmith's ``aio_to_thread`` seam safely inside a workflow. + + Outside a workflow (activities, clients) we defer to LangSmith's default + thread hop. Inside a workflow the deterministic event loop cannot spawn a + thread, so we run the setup inline in the requested context. ``func`` here is + LangSmith's own run-tree bookkeeping — cheap and side-effect-free with respect + to workflow determinism. + """ + from temporalio import workflow + + if not workflow.in_workflow(): + return await default_aio_to_thread(ctx, func, *args, **kwargs) + with workflow.unsafe.sandbox_unrestricted(): + return ctx.run(func, *args, **kwargs) + + +def _install_langsmith_temporal_override() -> None: + """Route LangSmith's thread hop inline while in a workflow. + + ``set_runtime_overrides`` mutates a module global in the sandbox-passthrough + ``langsmith`` package, so the in-workflow tracer sees it too. No-op (and + harmless) when LangSmith is not installed or too old to expose + ``set_runtime_overrides``. + + The override is deliberately installed for the life of the process and + never uninstalled: LangSmith exposes a single process-wide override slot + (each ``set_runtime_overrides`` call replaces it wholesale), and + ``temporalio.contrib.langsmith`` installs a behaviorally identical override + exactly once, without reinstalling. Resetting the slot on this worker's + shutdown would therefore permanently strip a composed LangSmith plugin's + workflow-safety override. Leaving it installed is safe: the override defers + to LangSmith's default thread hop whenever ``workflow.in_workflow()`` is + false, so it is inert outside workflows. + """ + try: + import langsmith + + langsmith.set_runtime_overrides(aio_to_thread=_temporal_aio_to_thread) + except Exception: + pass + + +def _dedupe_activities(activities: Sequence[Any]) -> list[Any]: + """Drop duplicate activity registrations by defn name, keeping the first. + + Guards the "plugin added on both Client and Worker" trap, where the plugin's + activities would otherwise be appended twice and the worker would reject the + duplicate names. + """ + seen: set[str] = set() + out: list[Any] = [] + for act in activities: + defn = activity_mod._Definition.from_callable(act) + name = defn.name if defn is not None else getattr(act, "__name__", repr(act)) + key = name or repr(act) + if key in seen: + continue + seen.add(key) + out.append(act) + return out diff --git a/temporalio/contrib/deepagents/_serde.py b/temporalio/contrib/deepagents/_serde.py new file mode 100644 index 000000000..13ed0d1c6 --- /dev/null +++ b/temporalio/contrib/deepagents/_serde.py @@ -0,0 +1,410 @@ +"""Serialization helpers, a result cache, and worker-runtime configuration. + +The Deep Agents control loop runs *inside* the Temporal workflow, so the values +that actually cross the workflow⇄activity boundary are a small set of LangChain +types: chat messages, tool-call descriptors, and the ``RunnableConfig`` metadata +attached to each model / tool call. LangChain messages are polymorphic +``Serializable`` models (an ``AIMessage`` must not be rehydrated as a +``ToolMessage``) and ``RunnableConfig`` carries live callback / checkpointer +references, so neither survives a naive round-trip. This module owns: + +* message (de)serialization via ``langchain_core.load.dumpd`` / ``load`` — the + round-trip that preserves message subtype and tool-call structure; +* the strip → ship → rebuild dance for ``RunnableConfig``; +* tool → JSON-schema advertisement (full name + description + argument schema, + never ``{name, description}`` alone — without the argument schema the model + picks the right tool but hallucinates its arguments); +* the Pydantic data converter (``exclude_unset=True``) the plugin installs on + the client and replayer, so message payloads stay small and round-trip + cleanly; +* a workflow-scoped result cache so model / tool results computed before a + ``continue_as_new`` are reused rather than recomputed after it; +* the sandbox passthrough module list covering LangChain's transitive + eager-import tree. + +LangChain imports are deferred into the functions that need them, so importing +this module — and constructing the plugin — does not require LangChain to be +installed on the machine assembling the worker. +""" + +from __future__ import annotations + +import contextvars +import dataclasses +import hashlib +import json +from typing import TYPE_CHECKING, Any, cast + +if TYPE_CHECKING: + from langchain_core.runnables import RunnableConfig + +from temporalio.contrib.pydantic import PydanticPayloadConverter, ToJsonOptions +from temporalio.converter import DataConverter + +# --------------------------------------------------------------------------- +# Worker-wide dispatch settings +# --------------------------------------------------------------------------- +# +# These are fixed for the worker's lifetime (not per-workflow state), so a module +# global is correct. This module lives under ``temporalio``, which the workflow +# sandbox passes through, so the object the in-workflow model stub reads is the +# same one the plugin configured. They live here (not in ``_model``) so that +# ``DeepAgentsPlugin`` can push them down without importing ``_model`` — which +# would drag in LangChain at plugin-construction time. + + +@dataclasses.dataclass +class Settings: + """Dispatch defaults shared by every ``TemporalModel`` on the worker.""" + + model_activity_options: Any = None + """``ActivityConfig`` or ``Mapping[model_name, ActivityConfig]``.""" + streaming_topic: str | None = None + + +_settings = Settings() + + +def set_settings( + *, + model_activity_options: Any = None, + streaming_topic: str | None = None, +) -> None: + """Install the worker-wide model dispatch defaults (called by the plugin).""" + _settings.model_activity_options = model_activity_options + _settings.streaming_topic = streaming_topic + + +def get_settings() -> Settings: + """Return the active dispatch settings.""" + return _settings + + +# --------------------------------------------------------------------------- +# Data converter +# --------------------------------------------------------------------------- + + +class DeepAgentsPayloadConverter(PydanticPayloadConverter): + """Pydantic payload converter pinned to ``exclude_unset=True``. + + LangChain request/response types (and ``DeepAgentState``) are deeply nested + with many ``Optional[...] = None`` fields. Shipping every unset default + inflates payloads several-fold and some peers reject the explicit nulls on + round-trip, so we exclude unset fields by convention. + """ + + def __init__(self) -> None: + """Construct the converter with ``exclude_unset`` serialization.""" + super().__init__(ToJsonOptions(exclude_unset=True)) + + +data_converter = DataConverter(payload_converter_class=DeepAgentsPayloadConverter) +"""The plugin's default data converter (LangChain messages are shipped as their +``dumpd`` JSON form, so the Pydantic converter only ever sees plain containers).""" + + +def build_data_converter( + user_converter: DataConverter | None, +) -> DataConverter: + """Compose the plugin's converter with whatever the caller already set. + + * ``None`` — install the plugin default. + * the SDK default converter — swap in the LangChain-aware Pydantic + converter via :func:`dataclasses.replace`. + * a custom converter — refuse rather than silently clobber it; the caller + must fold :class:`DeepAgentsPayloadConverter` into their own converter. + """ + if user_converter is None: + return data_converter + if user_converter is DataConverter.default: + return dataclasses.replace( + user_converter, payload_converter_class=DeepAgentsPayloadConverter + ) + raise ValueError( + "DeepAgentsPlugin cannot compose with a custom data_converter " + "automatically. Set payload_converter_class=DeepAgentsPayloadConverter " + "on your own DataConverter (so LangChain messages serialize with " + "exclude_unset=True), or omit data_converter to use the plugin default." + ) + + +# --------------------------------------------------------------------------- +# LangChain object (de)serialization +# --------------------------------------------------------------------------- + + +def dump_object(obj: Any) -> Any: + """Serialize a single LangChain ``Serializable`` (message, tool call, …).""" + from langchain_core.load import dumpd + + return dumpd(obj) + + +def load_object(data: Any) -> Any: + """Rehydrate a value produced by :func:`dump_object`, preserving subtype.""" + from langchain_core.load import load + + return load(data) + + +# --------------------------------------------------------------------------- +# Backend protocol result (de)serialization +# --------------------------------------------------------------------------- + +_BACKEND_DATACLASS_KEY = "__deepagents_dataclass__" + + +def dump_backend_result(value: Any) -> Any: + """Encode a backend op's return value for the activity boundary. + + Backend protocol results (``WriteResult`` / ``ReadResult`` / ``GrepResult`` + and their nested ``FileInfo`` / ``GrepMatch`` items, …) are plain + dataclasses — not LangChain ``Serializable`` objects — and the filesystem + middleware reads their ATTRIBUTES in-workflow, so a plain JSON round-trip + (which decays them to dicts) breaks the seam at the first real backend op. + Tag deepagents dataclasses with their import path so + :func:`load_backend_result` rebuilds the real type; anything else (str, + dict, a custom backend's own types) passes through with today's + plain-JSON behavior. + """ + import dataclasses + + if dataclasses.is_dataclass(value) and not isinstance(value, type): + cls = type(value) + dumped_fields = { + f.name: dump_backend_result(getattr(value, f.name)) + for f in dataclasses.fields(value) + } + if cls.__module__.split(".", 1)[0] == "deepagents": + return { + _BACKEND_DATACLASS_KEY: f"{cls.__module__}:{cls.__qualname__}", + "fields": dumped_fields, + } + return dumped_fields + if isinstance(value, (list, tuple)): + return [dump_backend_result(v) for v in value] + if isinstance(value, dict): + return {k: dump_backend_result(v) for k, v in value.items()} + return value + + +def load_backend_result(value: Any) -> Any: + """Rebuild a value produced by :func:`dump_backend_result`. + + Only ``deepagents.*`` dataclasses are reconstructed (the tag is written + exclusively for them); anything else would mean a forged payload, so + refuse rather than import arbitrary types. Reconstruction suppresses + ``DeprecationWarning``: this is transport, not user code — the backend + already constructed the object once on the activity side, and required + deprecated fields (e.g. ``WriteResult.files_update``) would otherwise + warn on every op. Field values equal to a declared default are omitted + from the constructor call. + """ + import dataclasses + import importlib + import warnings + + if isinstance(value, dict) and _BACKEND_DATACLASS_KEY in value: + path = value[_BACKEND_DATACLASS_KEY] + module_name, _, qualname = path.partition(":") + if module_name.split(".", 1)[0] != "deepagents": + raise ValueError(f"refusing to rebuild non-deepagents type {path!r}") + obj: Any = importlib.import_module(module_name) + for part in qualname.split("."): + obj = getattr(obj, part) + if not (isinstance(obj, type) and dataclasses.is_dataclass(obj)): + raise ValueError(f"{path!r} is not a dataclass") + loaded = {k: load_backend_result(v) for k, v in value["fields"].items()} + kwargs = {} + for f in dataclasses.fields(obj): + if f.name not in loaded: + continue + if f.default is not dataclasses.MISSING and loaded[f.name] == f.default: + continue + kwargs[f.name] = loaded[f.name] + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + return obj(**kwargs) + if isinstance(value, list): + return [load_backend_result(v) for v in value] + if isinstance(value, dict): + return {k: load_backend_result(v) for k, v in value.items()} + return value + + +def dump_messages(messages: Any) -> list[Any]: + """Serialize a sequence of LangChain messages to their ``dumpd`` form.""" + from langchain_core.load import dumpd + + return [dumpd(m) for m in messages] + + +def load_messages(dumped: list[Any]) -> list[Any]: + """Rehydrate messages serialized by :func:`dump_messages`.""" + from langchain_core.load import load + + return [load(d) for d in dumped] + + +def tool_to_schema(tool: Any) -> dict[str, Any]: + """Advertise a tool to the model as a full OpenAI tool schema. + + Carries name + description + argument JSON schema so the model can build + valid arguments, not just select the tool by name. + """ + from langchain_core.utils.function_calling import convert_to_openai_tool + + return convert_to_openai_tool(tool) + + +# --------------------------------------------------------------------------- +# RunnableConfig strip / rebuild +# --------------------------------------------------------------------------- + + +def _is_jsonish(value: Any) -> bool: + if value is None or isinstance(value, (str, int, float, bool)): + return True + if isinstance(value, (list, tuple)): + return all(_is_jsonish(v) for v in value) + if isinstance(value, dict): + return all(isinstance(k, str) and _is_jsonish(v) for k, v in value.items()) + return False + + +def strip_runnable_config(config: Any) -> dict[str, Any]: + """Reduce a live ``RunnableConfig`` to its JSON-safe subset for shipping. + + Keeps ``tags``, ``run_name``, ``run_id``, ``recursion_limit``, JSON-safe + ``metadata`` and the JSON-safe (non-dunder) ``configurable`` keys. Drops + callbacks, checkpointer / store / cache handles and every other live + reference — those are reconstructed activity-side. + """ + if not config: + return {} + out: dict[str, Any] = {} + if config.get("tags"): + out["tags"] = list(config["tags"]) + for key in ("run_id", "run_name"): + if config.get(key) is not None: + out[key] = str(config[key]) + if config.get("recursion_limit") is not None: + out["recursion_limit"] = config["recursion_limit"] + metadata = config.get("metadata") + if isinstance(metadata, dict): + out["metadata"] = {k: v for k, v in metadata.items() if _is_jsonish(v)} + configurable = config.get("configurable") + if isinstance(configurable, dict): + kept = { + k: v + for k, v in configurable.items() + if not k.startswith("__") and _is_jsonish(v) + } + if kept: + out["configurable"] = kept + return out + + +def rebuild_runnable_config(data: dict[str, Any]) -> "RunnableConfig": + """Reconstruct a minimal ``RunnableConfig`` from :func:`strip_runnable_config`.""" + config: dict[str, Any] = {"metadata": dict(data.get("metadata", {}))} + if data.get("tags"): + config["tags"] = list(data["tags"]) + for key in ("run_id", "run_name"): + if data.get(key) is not None: + config[key] = data[key] + if data.get("recursion_limit") is not None: + config["recursion_limit"] = data["recursion_limit"] + if data.get("configurable"): + config["configurable"] = dict(data["configurable"]) + # Double cast: a TypedDict and dict[str, Any] "insufficiently overlap" + # for basedpyright's reportInvalidCast; object is the sanctioned bridge. + return cast("RunnableConfig", cast(object, config)) + + +# --------------------------------------------------------------------------- +# Result cache (continue-as-new dedup) +# --------------------------------------------------------------------------- + +# Per-workflow state: set at the top of the workflow run, read by the model and +# tool dispatch paths, snapshotted for continue-as-new. A ContextVar (not a +# module-global keyed by run id) so update / signal handler tasks spawned by the +# workflow inherit the same cache automatically. +_result_cache: contextvars.ContextVar[dict[str, Any] | None] = contextvars.ContextVar( + "_deepagents_result_cache", default=None +) + + +def set_result_cache(cache: dict[str, Any] | None) -> None: + """Seed the workflow-scoped result cache (e.g. carried across CAN).""" + _result_cache.set(dict(cache) if cache else {}) + + +def result_cache_snapshot() -> dict[str, Any] | None: + """Return a serializable copy of the cache, or ``None`` when empty.""" + cache = _result_cache.get() + return dict(cache) if cache else None + + +def cache_key(kind: str, call_id: str, args: Any) -> str: + """Stable key over ``(kind, call_id, args)`` for cache lookups.""" + blob = json.dumps([kind, call_id, args], default=str, sort_keys=True) + return hashlib.sha256(blob.encode("utf-8")).hexdigest() + + +def cache_lookup(key: str) -> tuple[bool, Any]: + """Return ``(hit, value)`` for ``key`` in the active cache.""" + cache = _result_cache.get() + if cache is not None and key in cache: + return True, cache[key] + return False, None + + +def cache_put(key: str, value: Any) -> None: + """Record ``value`` under ``key`` when a cache is active for this run.""" + cache = _result_cache.get() + if cache is not None: + cache[key] = value + + +# --------------------------------------------------------------------------- +# Sandbox passthrough +# --------------------------------------------------------------------------- + +# The workflow sandbox re-imports modules per run; LangChain / LangGraph / +# deepagents build large class hierarchies with eager import side effects, and +# LangSmith pulls in numpy. Passing them through means "import once in the host +# and share", which is both faster and required for identity checks +# (isinstance across the sandbox boundary) to hold. +_DEFAULT_PASSTHROUGH: tuple[str, ...] = ( + "langchain", + "langchain_core", + "langchain_anthropic", + "langgraph", + "deepagents", + "langsmith", + "numpy", + "pydantic", + "pydantic_core", + "anthropic", + "tiktoken", + "jsonpatch", + "jsonpointer", + "tenacity", + "orjson", + "httpx", + "httpcore", +) + + +def default_passthrough_modules() -> tuple[str, ...]: + """The LangChain / deepagents transitive import tree passed through the sandbox.""" + return _DEFAULT_PASSTHROUGH + + +def resolve_passthrough_modules(user: Any) -> tuple[str, ...]: + """Merge caller-supplied passthrough modules with the plugin defaults.""" + merged = [*_DEFAULT_PASSTHROUGH, *(user or ())] + # dict.fromkeys preserves order while de-duplicating. + return tuple(dict.fromkeys(merged)) diff --git a/temporalio/contrib/deepagents/_tools.py b/temporalio/contrib/deepagents/_tools.py new file mode 100644 index 000000000..1235224c6 --- /dev/null +++ b/temporalio/contrib/deepagents/_tools.py @@ -0,0 +1,517 @@ +"""The tool + backend seams: the explicit per-unit Workflow-vs-Activity choice. + +Deep Agents holds its tools and filesystem/shell backends in-workflow. A tool or +backend op that only reads and writes ``DeepAgentState`` is pure and belongs in +the workflow (deterministic, replay-safe). One that does real I/O — a web +search, a shell command, a disk write — must not run there. This module gives +the user three explicit ways to move that work to an activity: + +* :func:`activity_as_tool` — expose an existing ``@activity.defn`` as a Deep + Agents tool (Temporal adopters already have activities; don't make them + re-declare); +* :func:`tool_as_activity` — wrap a LangChain ``BaseTool`` / callable so its + execution runs as an activity; +* :class:`TemporalBackend` — wrap a real-I/O backend so each file/exec op runs + as an activity. + +The choice is always explicit: an unwrapped non-builtin tool runs in-workflow, +and the plugin warns at construction so that is a conscious decision, never a +silent one. + +Registries here live in a ``temporalio``-namespaced (sandbox-passthrough) module, +so the object the worker's activity sees is the same one the module-level +``tool_as_activity(...)`` / ``TemporalBackend(...)`` call populated. They hold +worker-wide wiring, not per-workflow state. +""" + +from __future__ import annotations + +import importlib +import threading +import uuid as _uuid +import warnings +import weakref +from collections.abc import Mapping +from datetime import timedelta +from functools import wraps +from typing import TYPE_CHECKING, Any, Callable + +from temporalio import activity as activity_mod +from temporalio import workflow +from temporalio.contrib.deepagents import _activity, _serde + +# LangChain is a runtime dependency of the *tool seam*, but importing this module +# must not require it (the plugin imports it just to read tool defaults). So the +# ``langchain_core.tools`` symbols are imported lazily inside the functions that +# actually build tools; ``from __future__ import annotations`` keeps the type +# hints below as strings so they never touch LangChain at import time. +if TYPE_CHECKING: + from langchain_core.tools import BaseTool + + +# --------------------------------------------------------------------------- +# Tool registry (worker-side execution targets) +# --------------------------------------------------------------------------- + +_TOOL_REGISTRY: dict[str, "BaseTool"] = {} +_BACKEND_REGISTRY: dict[str, Any] = {} +# Serializes registration against the GC-time unregister in +# _unregister_backend, which may run on another thread. +_BACKEND_REGISTRY_LOCK = threading.Lock() + +# Worker-wide default activity options for tools wrapped with tool_as_activity, +# set by the plugin. Not per-workflow state: fixed for the worker's lifetime, and +# this module is sandbox-passthrough so the workflow sees the configured value. +_tool_defaults: dict[str, Any] = {} + + +def set_tool_defaults(options: Any) -> None: + """Install the plugin's default ``tool_activity_options`` (called by the plugin). + + Accepts a single ``ActivityConfig`` or a ``Mapping[tool_name, ActivityConfig]``. + """ + _tool_defaults.clear() + if options: + _tool_defaults["__value__"] = options + + +def _resolve_tool_options( + tool_name: str, instance_options: Mapping[str, Any] | None +) -> dict[str, Any]: + """Merge plugin tool defaults (possibly per-tool) with a per-call override.""" + opts: dict[str, Any] = {} + default = _tool_defaults.get("__value__") + if isinstance(default, Mapping): + per_tool = default.get(tool_name) + if isinstance(per_tool, Mapping): + opts.update(per_tool) + elif not any(isinstance(v, Mapping) for v in default.values()): + opts.update(default) + if instance_options: + opts.update(instance_options) + return opts + + +# Names of tools that route through Temporal (activity_as_tool / tool_as_activity), +# used to warn about unwrapped non-builtin tools at workflow build time. +_ROUTED_TOOL_NAMES: set[str] = set() + +# Deep Agents' built-in tools run in-workflow (pure state mutations); they are +# not expected to be wrapped, so they never trigger the unwrapped-tool warning. +# These are the LLM-facing TOOL names (``read_file``, ``write_file``, …) — +# NOT the backend protocol method names in ``_BACKEND_OPS`` (``read``, +# ``aread``, …); the two namespaces intentionally differ. +_BUILTIN_TOOL_NAMES = frozenset( + { + "write_todos", + "ls", + "read_file", + "write_file", + "edit_file", + "glob", + "grep", + "execute", + "task", + } +) + + +def register_tool(tool: BaseTool) -> None: + """Record ``tool`` so :meth:`DeepAgentActivities.invoke_tool` can run it.""" + _TOOL_REGISTRY[tool.name] = tool + + +def warn_unwrapped_tools(tools: Any) -> None: + """Warn once per unwrapped, non-builtin tool passed to ``create_deep_agent``. + + Running a tool in-workflow is only safe if it is pure/deterministic. The + Workflow-vs-Activity choice must be conscious, so any user tool that was not + routed through :func:`tool_as_activity` / :func:`activity_as_tool` gets a + construction-time warning rather than silently executing in the workflow. + """ + for tool in tools or (): + name = getattr(tool, "name", getattr(tool, "__name__", None)) + if name is None or name in _BUILTIN_TOOL_NAMES or name in _ROUTED_TOOL_NAMES: + continue + warnings.warn( + f"Tool {name!r} is passed to create_deep_agent unwrapped and will run " + f"inside the workflow. That is only safe if it is pure/deterministic. " + f"If it does I/O, wrap it with tool_as_activity(...) or expose an " + f"existing activity with activity_as_tool(...).", + stacklevel=3, + ) + + +def get_registered_tool(name: str) -> BaseTool | None: + """Look up a tool registered by :func:`register_tool`.""" + return _TOOL_REGISTRY.get(name) + + +def register_backend(ref: str, backend: Any) -> None: + """Record a backend so :meth:`DeepAgentActivities.backend_op` can reach it.""" + with _BACKEND_REGISTRY_LOCK: + _BACKEND_REGISTRY[ref] = backend + + +def _unregister_backend(ref: str, inner: Any) -> None: + """Drop ``ref`` from the registry if it still maps to ``inner``. + + GC hook for :class:`TemporalBackend` (via ``weakref.finalize``): a wrapper + is typically constructed per workflow run, so without cleanup a long-lived + worker accumulates one registry entry per run. The identity guard is + load-bearing: refs are deterministic per run, so after a cache eviction a + replay re-registers the *same* ref with a fresh inner backend — the evicted + wrapper's finalizer must not remove that live registration. + """ + with _BACKEND_REGISTRY_LOCK: + if _BACKEND_REGISTRY.get(ref) is inner: + del _BACKEND_REGISTRY[ref] + + +def registered_backends() -> dict[str, Any]: + """Return the live backend registry (read by the plugin at worker build).""" + return _BACKEND_REGISTRY + + +# --------------------------------------------------------------------------- +# activity_as_tool +# --------------------------------------------------------------------------- + + +def activity_as_tool( + activity: Callable, + *, + start_to_close_timeout: timedelta, + name: str | None = None, + description: str | None = None, + retry_policy: Any = None, + summary: str | None = None, +) -> BaseTool: + """Expose an existing Temporal activity as a Deep Agents tool. + + The returned tool advertises the activity's argument schema to the model and, + when called in-workflow, dispatches to the activity via + ``workflow.execute_activity`` — Temporal owns its retries and timeout. + + Args: + activity: A function decorated with ``@activity.defn``. + start_to_close_timeout: Required per-call timeout for the activity. + name: Override the tool name advertised to the model (defaults to + the activity definition name). + description: Override the tool description advertised to the model + (defaults to the activity docstring). + retry_policy: Optional Temporal retry policy for the activity. + summary: Optional ``summary=`` recorded on each activity invocation. + """ + with workflow.unsafe.imports_passed_through(): + from langchain_core.tools import StructuredTool + + defn = activity_mod._Definition.from_callable(activity) + if defn is None: + raise ValueError( + "activity_as_tool requires a function decorated with @activity.defn; " + f"{getattr(activity, '__name__', activity)!r} is not an activity." + ) + tool_name = name or defn.name + if tool_name is None: + raise ValueError( + "activity_as_tool requires a named activity (dynamic activities " + "have no definition name); pass name= explicitly." + ) + tool_desc = description or (activity.__doc__ or f"Temporal activity {tool_name}.") + act_summary = summary or f"tool:{tool_name}" + + # Temporal activities take a single positional argument. StructuredTool infers + # the model-facing schema from ``_run``'s signature, so we mirror the activity's + # own parameter names onto ``_run`` (via ``@wraps``) and then collapse the + # keyword call the model produces back into that single positional payload. + import inspect + + params = [ + p for p in inspect.signature(activity).parameters if p not in ("self", "cls") + ] + + @wraps(activity) + async def _run(*args: Any, **kwargs: Any) -> Any: + if args and not kwargs: + payload: Any = args[0] if len(args) == 1 else list(args) + elif len(params) == 1 and len(kwargs) == 1: + # Single-argument activity: pass the value directly, not {"arg": value}. + payload = next(iter(kwargs.values())) + else: + payload = kwargs + return await workflow.execute_activity( + activity, + payload, + start_to_close_timeout=start_to_close_timeout, + retry_policy=retry_policy, + summary=act_summary, + ) + + _ROUTED_TOOL_NAMES.add(tool_name) + return StructuredTool.from_function( + coroutine=_run, + name=tool_name, + description=tool_desc, + ) + + +# --------------------------------------------------------------------------- +# tool_as_activity +# --------------------------------------------------------------------------- + + +def tool_as_activity( + tool: BaseTool | Callable, + *, + start_to_close_timeout: timedelta, + activity_options: Mapping[str, Any] | None = None, +) -> BaseTool: + """Wrap a LangChain tool / callable so its execution runs as an activity. + + The underlying tool is registered on the worker; the returned tool keeps the + same name and argument schema (so the model's calls are unchanged) but, when + invoked in-workflow, dispatches ``deepagents.invoke_tool`` instead of running + the tool body inline. + """ + with workflow.unsafe.imports_passed_through(): + from langchain_core.tools import BaseTool, StructuredTool + + base_tool: BaseTool + if isinstance(tool, BaseTool): + base_tool = tool + else: + base_tool = StructuredTool.from_function( + tool if not _is_coroutine(tool) else None, + coroutine=tool if _is_coroutine(tool) else None, + ) + register_tool(base_tool) + + tool_name = base_tool.name + opts = _resolve_tool_options(tool_name, activity_options) + opts.setdefault("start_to_close_timeout", start_to_close_timeout) + + async def _run(**kwargs: Any) -> Any: + from temporalio.contrib.deepagents.workflow import call_tool + + tool_call_id = kwargs.pop("__tool_call_id__", None) or workflow.uuid4().hex + activity_input = _activity.ToolActivityInput( + tool_name=tool_name, + tool_call_id=tool_call_id, + args=kwargs, + ) + output = await call_tool( + activity_input, + summary=f"tool:{tool_name}", + **opts, + ) + message = _serde.load_object(output.message) + # Return CONTENT, not the pre-built ToolMessage: the activity cannot + # know the model's real tool_call_id, so a ToolMessage assembled there + # carries a generated id that a real provider rejects as an unpaired + # tool_result. Given plain content, the tool node stamps the model's + # own id — exactly as it does for unwrapped tools. + with workflow.unsafe.imports_passed_through(): + from langchain_core.messages import ToolMessage + + if isinstance(message, ToolMessage): + return message.content + return message + + _ROUTED_TOOL_NAMES.add(tool_name) + return StructuredTool( + name=tool_name, + description=base_tool.description, + args_schema=base_tool.args_schema, # type: ignore[arg-type] + coroutine=_run, + ) + + +def _is_coroutine(fn: Any) -> bool: + import inspect + + return inspect.iscoroutinefunction(fn) + + +# --------------------------------------------------------------------------- +# TemporalBackend +# --------------------------------------------------------------------------- + +# Async protocol methods and their sync twins. deepagents' ``BackendProtocol`` +# implements each async DEFAULT as ``asyncio.to_thread(sync_twin, ...)``; the +# deterministic workflow event loop has no thread executor, so any built-in +# tool call against an unwrapped in-workflow backend (e.g. the default +# ``StateBackend``) raises ``NotImplementedError``. A real model hits this on +# its first spontaneous ``grep``/``read_file`` call; scripted-model tests that +# never call built-ins sail past it. +_ASYNC_TO_SYNC_OPS: dict[str, str] = { + "als": "ls", + "als_info": "ls_info", + "aread": "read", + "awrite": "write", + "aedit": "edit", + "aglob": "glob", + "aglob_info": "glob_info", + "agrep": "grep", + "agrep_raw": "grep_raw", + "adownload_files": "download_files", + "aupload_files": "upload_files", + "aexecute": "execute", +} + +_original_backend_async_defaults: dict[str, Any] = {} + + +def install_backend_async_patch() -> None: + """Make ``BackendProtocol``'s async defaults workflow-safe. + + Inside a workflow, run the sync twin inline: for state-only backends that + is deterministic and semantically identical to the upstream default, + which merely moves the same sync call onto a worker thread. Outside a + workflow (activities, clients) the upstream default — thread hop plus + timeout guard — is used unchanged. Subclasses that override an async + method natively are unaffected; only the protocol defaults are replaced. + Idempotent. + """ + # importlib: `deepagents` is absent on Python 3.10 environments (its floor + # is 3.11), so a static import here fails type-checking there. + BackendProtocol = importlib.import_module( + "deepagents.backends.protocol" + ).BackendProtocol + + if _original_backend_async_defaults: + return + for async_name, sync_name in _ASYNC_TO_SYNC_OPS.items(): + original = BackendProtocol.__dict__.get(async_name) + if original is None: + continue + + def _make(sync_name: str, original: Any) -> Any: + async def patched(self: Any, *args: Any, **kwargs: Any) -> Any: + if workflow.in_workflow(): + return getattr(self, sync_name)(*args, **kwargs) + return await original(self, *args, **kwargs) + + return patched + + _original_backend_async_defaults[async_name] = original + setattr(BackendProtocol, async_name, _make(sync_name, original)) + + +def uninstall_backend_async_patch() -> None: + """Restore ``BackendProtocol``'s upstream async defaults.""" + if not _original_backend_async_defaults: + return + BackendProtocol = importlib.import_module( + "deepagents.backends.protocol" + ).BackendProtocol + + for async_name, original in _original_backend_async_defaults.items(): + setattr(BackendProtocol, async_name, original) + _original_backend_async_defaults.clear() + + +# Backend METHOD names (deepagents ``BackendProtocol`` + +# ``SandboxBackendProtocol``) whose calls must cross the activity boundary: +# every sync I/O method, its async ``a``-prefixed twin, and the sandbox/shell +# execute pair. The async twins are load-bearing — deepagents' filesystem +# middleware drives backends through ``als``/``aread``/``awrite``/… — so +# intercepting only sync names lets an agent's built-in file tools run I/O +# in-workflow. These are backend PROTOCOL method names, distinct from the +# LLM-facing tool names in ``_BUILTIN_TOOL_NAMES`` (``read_file`` etc.). +_BACKEND_OPS = ( + # Sync protocol surface. + "ls", + "ls_info", + "read", + "write", + "edit", + "glob", + "glob_info", + "grep", + "grep_raw", + "download_files", + "upload_files", + "execute", + # Async twins (what FilesystemMiddleware actually calls). + "als", + "als_info", + "aread", + "awrite", + "aedit", + "aglob", + "aglob_info", + "agrep", + "agrep_raw", + "adownload_files", + "aupload_files", + "aexecute", +) + + +class TemporalBackend: + """Route a real-I/O backend's operations through Temporal activities. + + Wrap a ``FilesystemBackend`` / ``LocalShellBackend`` / ``StoreBackend`` / + ``CompositeBackend`` so each file or shell operation becomes a durable + ``deepagents.backend_op`` activity instead of touching disk / shell from the + workflow. State-only backends (``StateBackend``) need no wrapping — they are + pure workflow state and run in-workflow. + + Unknown attribute access is forwarded to the inner backend so backend + metadata / configuration the agent reads (but that does no I/O) still works. + """ + + def __init__( + self, + inner: Any, + *, + activity_options: Mapping[str, Any] | None = None, + ) -> None: + """Wrap ``inner`` so its I/O ops dispatch as durable activities.""" + self._inner = inner + # A deterministic id: workflow.uuid4() is seeded per-run, so the ref is + # identical across replays (unlike id(inner)). Falls back to a plain uuid + # when a backend is wrapped outside a workflow (e.g. in a plain test). + if workflow.in_workflow(): + self._ref = f"backend:{workflow.uuid4().hex}" + else: + self._ref = f"backend:{_uuid.uuid4().hex}" + self._opts: dict[str, Any] = dict(activity_options or {}) + self._opts.setdefault("start_to_close_timeout", timedelta(minutes=1)) + register_backend(self._ref, inner) + # Balance the registration when this wrapper is garbage-collected + # (workflow completion / cache eviction) so per-run backends do not + # accumulate in the worker-global registry. The finalizer captures + # (ref, inner) — not ``self`` — so it cannot keep the wrapper alive, + # and it only removes its own registration (see _unregister_backend). + self._finalizer = weakref.finalize(self, _unregister_backend, self._ref, inner) + + async def _dispatch(self, op: str, *args: Any, **kwargs: Any) -> Any: + from temporalio.contrib.deepagents.workflow import call_backend_op + + activity_input = _activity.BackendOpInput( + backend_ref=self._ref, + op=op, + args=list(args), + kwargs=dict(kwargs), + ) + output = await call_backend_op( + activity_input, + summary=f"backend:{op}", + **self._opts, + ) + return _serde.load_backend_result(output.result) + + def __getattr__(self, name: str) -> Any: + """Bound-method access for a known I/O op returns an activity dispatcher. + + Everything else forwards to the inner backend unchanged. + """ + if name in _BACKEND_OPS: + + async def _op(*args: Any, **kwargs: Any) -> Any: + return await self._dispatch(name, *args, **kwargs) + + return _op + return getattr(self._inner, name) diff --git a/temporalio/contrib/deepagents/py.typed b/temporalio/contrib/deepagents/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/temporalio/contrib/deepagents/testing.py b/temporalio/contrib/deepagents/testing.py new file mode 100644 index 000000000..749b1ab96 --- /dev/null +++ b/temporalio/contrib/deepagents/testing.py @@ -0,0 +1,141 @@ +"""Test helpers for users adopting :class:`DeepAgentsPlugin`. + +Unit-testing a Deep Agent under Temporal should not require a live LLM endpoint +or a paid API key. This module ships: + +* :class:`FakeModel` — a real ``BaseChatModel`` returning scripted replies (plain + text or full ``AIMessage`` objects carrying ``tool_calls``), cycling when + exhausted; +* :func:`fake_model_factory` — a one-liner for the common text-only case; +* :func:`mock_model_provider` — a ``model_provider`` (name → model) you pass to + ``DeepAgentsPlugin(model_provider=...)`` so the model activity runs offline; +* :class:`MockTool` — a scripted ``BaseTool`` for exercising the tool seam. + +Importing this module has no process-wide side effects. +""" + +from __future__ import annotations + +from collections.abc import Callable, Sequence +from typing import Any + +from langchain_core.callbacks import ( + AsyncCallbackManagerForLLMRun, + CallbackManagerForLLMRun, +) +from langchain_core.language_models.chat_models import BaseChatModel +from langchain_core.messages import AIMessage, BaseMessage +from langchain_core.outputs import ChatGeneration, ChatResult +from langchain_core.tools import BaseTool +from pydantic import PrivateAttr + +__all__ = ["FakeModel", "fake_model_factory", "mock_model_provider", "MockTool"] + +Response = str | AIMessage + + +class FakeModel(BaseChatModel): + """A ``BaseChatModel`` returning scripted responses, for offline tests. + + Args: + responses: Replies returned one per call, cycling when exhausted. Each is + either a string (becomes an ``AIMessage``) or an ``AIMessage`` (so you + can script ``tool_calls`` to drive the agent's tool path). + """ + + responses: list[Any] + _cursor: int = PrivateAttr(default=0) + + def __init__(self, responses: Sequence[Response], **kwargs: Any) -> None: + """Validate and store the scripted responses.""" + resp = list(responses) + if not resp: + raise ValueError("FakeModel needs at least one scripted response.") + super().__init__(responses=resp, **kwargs) # type: ignore[call-arg] + + @property + def _llm_type(self) -> str: + return "temporalio-deepagents-fake-model" + + def bind_tools(self, tools: Sequence[Any], **kwargs: Any) -> "FakeModel": + """Ignore the tool set; the fake just replays its script. + + Returning ``self`` keeps ``model.bind_tools(...)`` chainable like a + real model. + """ + return self + + def _next(self) -> AIMessage: + item = self.responses[self._cursor % len(self.responses)] + self._cursor += 1 + return item if isinstance(item, AIMessage) else AIMessage(content=item) + + def _generate( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: CallbackManagerForLLMRun | None = None, + **kwargs: Any, + ) -> ChatResult: + return ChatResult(generations=[ChatGeneration(message=self._next())]) + + async def _agenerate( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: AsyncCallbackManagerForLLMRun | None = None, + **kwargs: Any, + ) -> ChatResult: + return ChatResult(generations=[ChatGeneration(message=self._next())]) + + +def fake_model_factory(responses: Sequence[Response]) -> FakeModel: + """One-liner scripted fake chat model. + + Example:: + + model = fake_model_factory(["The capital of France is Paris."]) + """ + return FakeModel(responses) + + +def mock_model_provider( + responses: Sequence[Response], +) -> Callable[[str], FakeModel]: + """A ``model_provider`` that hands out the scripted responses one call at a time. + + Each model activity invocation (main agent, a sub-agent, a follow-up turn + after a tool call) advances through ``responses`` and cycles when exhausted, + so a multi-turn agent can be scripted deterministically. The model activity + builds a fresh model per call, so the cursor lives on the provider closure + (shared for the worker's lifetime) rather than on any one model instance. + + Pass to the plugin so the model activity runs offline:: + + plugin = DeepAgentsPlugin(model_provider=mock_model_provider(["Paris."])) + """ + resp = list(responses) + if not resp: + raise ValueError("mock_model_provider needs at least one response.") + cursor = {"i": 0} + + def provider(_model_name: str) -> FakeModel: + reply = resp[cursor["i"] % len(resp)] + cursor["i"] += 1 + return FakeModel([reply]) + + return provider + + +class MockTool(BaseTool): + """A scripted ``BaseTool`` whose call returns a fixed value, for tests.""" + + name: str = "mock_tool" + description: str = "A mock tool that returns a scripted result." + result: Any = "ok" + + def _run(self, *args: Any, **kwargs: Any) -> Any: + return self.result + + async def _arun(self, *args: Any, **kwargs: Any) -> Any: + return self.result diff --git a/temporalio/contrib/deepagents/workflow.py b/temporalio/contrib/deepagents/workflow.py new file mode 100644 index 000000000..cb3e45b03 --- /dev/null +++ b/temporalio/contrib/deepagents/workflow.py @@ -0,0 +1,317 @@ +"""Workflow-side surface: the failure type, the dispatch helpers, and the runner. + +Everything here runs *inside* the workflow. The dispatch helpers +(:func:`call_model` / :func:`call_tool` / :func:`call_backend_op`) are the single +choke point through which the in-workflow model / tool / backend stubs reach +their activities; they also consult the continue-as-new result cache so work +done before a ``continue_as_new`` is reused rather than repeated after it. + +:func:`run_deep_agent` is the optional driver that adds continue-as-new +state-carry around a native ``agent.ainvoke(...)`` — plain ``agent.ainvoke(...)`` +still works without it. +""" + +from __future__ import annotations + +import importlib +import warnings +from collections.abc import Mapping +from typing import Any + +from temporalio import workflow +from temporalio.contrib.deepagents import _activity, _serde +from temporalio.exceptions import ApplicationError + +# Reserved key under which the CAN result cache rides inside a state snapshot. +_CACHE_KEY = "__temporal_cache__" + +# Checkpointer classes that keep their state in the workflow's own memory and are +# therefore rehydrated for free by deterministic replay. Anything else does its +# own I/O and is not replay-safe from inside the workflow. +_IN_WORKFLOW_SAVERS = frozenset({"InMemorySaver", "MemorySaver"}) + + +def warn_durable_checkpointer(checkpointer: Any) -> None: + """Warn when a user hands ``create_deep_agent`` a durable checkpointer. + + The Deep Agents loop runs inside the workflow, so a checkpointer that does + its own database / disk I/O would run that I/O from workflow code — not + replay-safe. We respect the user's choice (a warning, not a hard failure), + and point them at the durability path that *is* safe: the default in-workflow + ``InMemorySaver`` rehydrated by replay, plus + :func:`run_deep_agent` with ``continue_as_new_after`` for long conversations. + """ + if checkpointer is None: + return + if type(checkpointer).__name__ in _IN_WORKFLOW_SAVERS: + return + warnings.warn( + f"create_deep_agent received a durable checkpointer " + f"{type(checkpointer).__name__!r}. The agent loop runs inside the " + f"workflow, so this checkpointer's I/O would run from workflow code, " + f"which is not replay-safe. Prefer the default in-workflow InMemorySaver " + f"(rehydrated by replay) plus run_deep_agent(continue_as_new_after=...) " + f"for long-conversation durability.", + stacklevel=3, + ) + + +class DeepAgentsWorkflowError(ApplicationError): + """Raised for non-retryable Deep Agents failures surfaced in the workflow. + + This is the type registered in the plugin's + ``workflow_failure_exception_types``, so a model / tool failure that Temporal + has exhausted (or an invalid agent configuration) fails the workflow with a + stable ``ApplicationError.type`` — never a stringified peer exception. + """ + + TYPE = "deepagents.DeepAgentsWorkflowError" + + def __init__(self, message: str, *, non_retryable: bool = True) -> None: + """Construct the error with the plugin's stable failure ``type``.""" + super().__init__(message, type=self.TYPE, non_retryable=non_retryable) + + +# --------------------------------------------------------------------------- +# Dispatch helpers (the model / tool / backend choke point) +# --------------------------------------------------------------------------- + + +async def call_model( + activity_name: str, + activity_input: _activity.ModelActivityInput, + *, + summary: str, + **opts: Any, +) -> _activity.ModelActivityOutput: + """Dispatch one model call, reusing a cached result across continue-as-new.""" + key = _serde.cache_key( + "model", + activity_input.model_name, + [activity_input.messages, activity_input.tool_schemas], + ) + hit, cached = _serde.cache_lookup(key) + if hit: + return _activity.ModelActivityOutput(message=cached) + output = await workflow.execute_activity( + activity_name, + activity_input, + result_type=_activity.ModelActivityOutput, + summary=summary, + **opts, + ) + _serde.cache_put(key, output.message) + return output + + +async def call_tool( + activity_input: _activity.ToolActivityInput, + *, + summary: str, + **opts: Any, +) -> _activity.ToolActivityOutput: + """Dispatch one tool call, reusing a cached result across continue-as-new.""" + key = _serde.cache_key("tool", activity_input.tool_name, activity_input.args) + hit, cached = _serde.cache_lookup(key) + if hit: + return _activity.ToolActivityOutput(message=cached) + output = await workflow.execute_activity( + _activity.INVOKE_TOOL, + activity_input, + result_type=_activity.ToolActivityOutput, + summary=summary, + **opts, + ) + _serde.cache_put(key, output.message) + return output + + +async def call_backend_op( + activity_input: _activity.BackendOpInput, + *, + summary: str, + **opts: Any, +) -> _activity.BackendOpOutput: + """Dispatch one backend op, reusing a cached result across continue-as-new.""" + key = _serde.cache_key( + f"backend:{activity_input.backend_ref}", + activity_input.op, + [activity_input.args, activity_input.kwargs], + ) + hit, cached = _serde.cache_lookup(key) + if hit: + return _activity.BackendOpOutput(result=cached) + output = await workflow.execute_activity( + _activity.BACKEND_OP, + activity_input, + result_type=_activity.BackendOpOutput, + summary=summary, + **opts, + ) + _serde.cache_put(key, output.result) + return output + + +# --------------------------------------------------------------------------- +# run_deep_agent (continue-as-new state carry) +# --------------------------------------------------------------------------- + + +def _merge_snapshot(input: Any, snapshot: Mapping[str, Any]) -> Any: + """Prepend a snapshot's carried messages onto the next turn's input.""" + raw_prior: Any = snapshot.get("messages") or [] + prior = list(raw_prior) + if not prior: + return input + if isinstance(input, Mapping): + merged = dict(input) + raw_next: Any = input.get("messages") or [] + merged["messages"] = [*prior, *list(raw_next)] + return merged + return {"messages": [*prior, *_as_message_list(input)]} + + +def _as_message_list(input: Any) -> list[Any]: + if isinstance(input, (list, tuple)): + return list(input) + return [input] + + +async def run_deep_agent( + agent: Any, + input: Any, + *, + continue_as_new_after: int | None = None, + state_snapshot: Mapping[str, Any] | None = None, +) -> Any: + """Drive ``agent.ainvoke(input)`` with continue-as-new state carry. + + Once the completed turn leaves pending todos AND history has grown past the + limit, the turn's state (messages + the model/tool result cache) is + snapshotted and carried into a fresh run via ``workflow.continue_as_new``, + so long conversations do not accumulate unbounded history. + + By default (``continue_as_new_after=None``) the limit is the server's own + recommendation — ``workflow.info().is_continue_as_new_suggested()`` — which + accounts for both history length and size; this is the recommended mode. + Pass an explicit ``continue_as_new_after=N`` to trigger on a fixed history + event count instead. To run an agent with NO continue-as-new behavior, call + ``agent.ainvoke(...)`` directly rather than using this driver. + + The enclosing ``@workflow.run`` method must accept the continued call — i.e. + its signature is ``(input, state_snapshot=None)`` — because that is how the + carried state is threaded into the next run. + """ + # Resume path: rehydrate the result cache and fold carried messages in. + if state_snapshot is not None: + _serde.set_result_cache(dict(state_snapshot.get(_CACHE_KEY) or {})) + input = _merge_snapshot(input, state_snapshot) + else: + _serde.set_result_cache({}) + + try: + result = await agent.ainvoke(input) + except ApplicationError: + raise + except Exception as exc: + # If the framework wrapped a Temporal failure, surface the registered + # workflow-failure type rather than the framework's generic exception. + cause = exc.__cause__ + if cause is not None and workflow.is_failure_exception(cause): + raise DeepAgentsWorkflowError(f"Deep Agents run failed: {exc}") from cause + raise + + if continue_as_new_after is None: + # Default: follow the server's judgement. The suggestion accounts for + # history count AND size limits, which a fixed event threshold cannot. + should_continue = workflow.info().is_continue_as_new_suggested() + else: + should_continue = ( + workflow.info().get_current_history_length() >= continue_as_new_after + ) + if should_continue and _has_pending_work(result): + snapshot = { + "messages": _extract_messages(result), + _CACHE_KEY: _serde.result_cache_snapshot() or {}, + } + # ``continue_as_new`` threads positional args into the next run via + # ``args=``; the enclosing ``@workflow.run`` receives them as + # ``(input, state_snapshot)``. + workflow.continue_as_new(args=[input, snapshot]) + + return result + + +def _extract_messages(result: Any) -> list[Any]: + if isinstance(result, Mapping): + raw: Any = result.get("messages") or [] + return list(raw) + return [] + + +def _has_pending_work(result: Any) -> bool: + """True when the agent left unfinished todos worth carrying past a CAN. + + A finished single-shot run has no pending todos, so this returns False and the + driver returns the result instead of looping on continue-as-new forever. + """ + if isinstance(result, Mapping): + todos: Any = result.get("todos") or [] + return any( + isinstance(t, Mapping) and t.get("status") not in ("completed", "done") + for t in todos + ) + return False + + +def create_temporal_deep_agent( + *args: Any, + activity_options: Mapping[str, Any] | None = None, + **kwargs: Any, +) -> Any: + """Build a Deep Agent whose model calls run as durable activities. + + A thin wrapper over ``deepagents.create_deep_agent`` that makes the + Temporal wiring explicit: a ``model=`` name string is wrapped in + ``TemporalModel`` carrying this + agent's ``activity_options`` (``execute_activity`` overrides — timeouts, + retry policy — for its model calls). Every other argument — tools, + backend, sub-agents, ``interrupt_on`` — is forwarded unchanged. + + Unmodified ``create_deep_agent(...)`` also works inside a workflow (the + plugin substitutes the durable model automatically, using the plugin's + ``model_activity_options``); use this wrapper to scope activity options + to one agent instead of configuring them plugin-wide. + """ + with workflow.unsafe.imports_passed_through(): + # importlib keeps this resolution absolute: a static + # `import deepagents` from inside this same-named package directory + # is flagged (and on 3.10, mis-resolved) as implicitly relative. + deepagents_mod: Any = importlib.import_module("deepagents") + + from temporalio.contrib.deepagents._model import TemporalModel + + model = args[0] if args else kwargs.pop("model", None) + if isinstance(model, str): + model = TemporalModel( + model=model, + activity_options=( + dict(activity_options) if activity_options is not None else None + ), + ) + elif activity_options is not None: + if isinstance(model, TemporalModel): + model = TemporalModel( + model=model.model, activity_options=dict(activity_options) + ) + else: + raise ValueError( + "activity_options requires model= to be a model-name string " + "or a TemporalModel; got " + f"{type(model).__name__ if model is not None else 'no model'}." + ) + if args: + args = (model, *args[1:]) + else: + kwargs["model"] = model + return deepagents_mod.create_deep_agent(*args, **kwargs) diff --git a/temporalio/contrib/google_adk_agents/README.md b/temporalio/contrib/google_adk_agents/README.md index 40ebb9aee..d1ba2133a 100644 --- a/temporalio/contrib/google_adk_agents/README.md +++ b/temporalio/contrib/google_adk_agents/README.md @@ -166,12 +166,65 @@ worker = Worker( ) ``` +`TemporalMcpToolSet` also accepts an optional `factory_argument`. It is sent to the toolset activities and passed to the registered `toolset_factory` when the `McpToolset` is created. + +**Do not pass secrets, credentials, or API keys through `factory_argument`.** It is an activity argument, so it is recorded in workflow history and, without a payload codec, visible in the web UI. Resolve credentials worker-side inside the toolset factory instead. + +### Reading Session State in Activity Tools + +ADK's live `ToolContext` holds non-serializable objects, so it cannot be an +activity argument. To read the serializable subset from an activity-backed +tool, declare a parameter named `tool_context` annotated with +`ToolContextSnapshot`: + +```python +from datetime import timedelta + +from temporalio import activity +from temporalio.contrib.google_adk_agents.workflow import ( + ToolContextSnapshot, + activity_as_tool, +) + + +@activity.defn +async def get_weather(query: str, tool_context: ToolContextSnapshot) -> dict: + db_url = tool_context.state.get("url", "") + ... + + +weather_tool = activity_as_tool( + get_weather, start_to_close_timeout=timedelta(seconds=30) +) +``` + +Exactly like a native ADK function tool's `tool_context` parameter, it is +excluded from the LLM-facing tool schema; at invocation the wrapper snapshots +the live `ToolContext` (session state as a plain dict, plus the function-call +id) and passes it to the activity. Annotating any parameter with a live ADK +context type raises `ValueError` at wrap time, since ADK would inject the +non-serializable context into it regardless of its name. + +When running under Temporal, the entire session state crosses the activity +boundary: every value in it must be serializable by the configured data +converter and the total size must fit within payload limits, even for keys +the tool never reads. Local ADK runs pass the snapshot in memory and have no +such constraint. + +The snapshot is one-way and should be treated as read-only: mutations inside +the activity do not propagate back to the session, because the activity may +run on a different worker (and in local ADK runs, where the snapshot is +passed in memory, nested state values may alias the live session state, so +mutating them can corrupt the session). To modify session state, return the +needed information from the activity and apply it in workflow-side code (for +example an ADK callback or a plain tool function). + ### Local ADK Runs The same agent definitions can also be exercised outside Temporal with `adk run` or `adk web`. -- `TemporalModel` and `activity_tool(...)` work in local ADK runs without +- `TemporalModel` and `activity_as_tool(...)` work in local ADK runs without additional configuration. - If the agent uses `TemporalMcpToolSet`, define a shared toolset factory, register it with `TemporalMcpToolSetProvider(...)` for workflow runs, and diff --git a/temporalio/contrib/google_adk_agents/__init__.py b/temporalio/contrib/google_adk_agents/__init__.py index 3f236516b..d4c969fb3 100644 --- a/temporalio/contrib/google_adk_agents/__init__.py +++ b/temporalio/contrib/google_adk_agents/__init__.py @@ -6,6 +6,8 @@ from temporalio.contrib.google_adk_agents._mcp import ( TemporalMcpToolSet, TemporalMcpToolSetProvider, + TemporalStatefulMcpToolSet, + TemporalStatefulMcpToolSetProvider, ) from temporalio.contrib.google_adk_agents._model import TemporalModel from temporalio.contrib.google_adk_agents._plugin import ( @@ -16,5 +18,7 @@ "GoogleAdkPlugin", "TemporalMcpToolSet", "TemporalMcpToolSetProvider", + "TemporalStatefulMcpToolSet", + "TemporalStatefulMcpToolSetProvider", "TemporalModel", ] diff --git a/temporalio/contrib/google_adk_agents/_mcp.py b/temporalio/contrib/google_adk_agents/_mcp.py index 92bf994dd..b342c9c3c 100644 --- a/temporalio/contrib/google_adk_agents/_mcp.py +++ b/temporalio/contrib/google_adk_agents/_mcp.py @@ -1,6 +1,9 @@ +import asyncio +import functools from collections.abc import Sequence from dataclasses import dataclass from datetime import timedelta +from types import TracebackType from typing import Any, Callable from google.adk.agents.readonly_context import ReadonlyContext @@ -14,8 +17,17 @@ from google.genai.types import FunctionDeclaration from temporalio import activity, workflow -from temporalio.exceptions import ApplicationError -from temporalio.workflow import ActivityConfig +from temporalio.api.enums.v1.workflow_pb2 import ( + TIMEOUT_TYPE_HEARTBEAT, + TIMEOUT_TYPE_SCHEDULE_TO_START, +) +from temporalio.exceptions import ( + ActivityError, + ApplicationError, + is_cancelled_exception, +) +from temporalio.worker import PollerBehaviorSimpleMaximum, Worker +from temporalio.workflow import ActivityConfig, ActivityHandle @dataclass @@ -88,16 +100,28 @@ class TemporalMcpToolSetProvider: Manages the creation of toolset activities and handles tool execution within Temporal workflows. + + This provider is *stateless*: every ``list-tools``/``call-tool`` activity + invocation builds a fresh ``McpToolset`` via ``toolset_factory``, runs the + operation, and always closes the toolset in a ``finally`` block. This means + ``factory_argument`` is honored on every single call (no cross-workflow + connection sharing or silent mis-routing) and no MCP session or stdio + subprocess is leaked. State is not maintained across calls; if a persistent + connection is required, use :class:`TemporalStatefulMcpToolSetProvider`. """ def __init__( - self, name: str, toolset_factory: Callable[[Any | None], McpToolset] + self, + name: str, + toolset_factory: Callable[[Any | None], McpToolset], ) -> None: """Initializes the toolset provider. Args: name: Name prefix for the generated activities. toolset_factory: Factory function that creates McpToolset instances. + It should return a new toolset each time so that no state is + shared between workflow runs. """ super().__init__() self._name = name @@ -108,42 +132,52 @@ def _get_activities(self) -> Sequence[Callable]: async def get_tools( args: _GetToolsArguments, ) -> list[_ToolResult]: + # Build a fresh toolset per call, honoring this call's + # ``factory_argument``, and always close it so no MCP session + # (or stdio subprocess) leaks. See issue #1663. toolset = self._toolset_factory(args.factory_argument) - tools = await toolset.get_tools() - return [ - _ToolResult( - tool.name, - tool.description, - tool.is_long_running, - tool.custom_metadata, - tool._get_declaration(), - ) - for tool in tools - ] + try: + tools = await toolset.get_tools() + return [ + _ToolResult( + tool.name, + tool.description, + tool.is_long_running, + tool.custom_metadata, + tool._get_declaration(), + ) + for tool in tools + ] + finally: + await toolset.close() @activity.defn(name=self._name + "-call-tool") async def call_tool( args: _CallToolArguments, ) -> _CallToolResult: toolset = self._toolset_factory(args.factory_argument) - tools = await toolset.get_tools() - tool_match = [tool for tool in tools if tool.name == args.name] - if len(tool_match) == 0: - raise ApplicationError( - f"Unable to find matching mcp tool by name: {args.name}" + try: + tools = await toolset.get_tools() + + tool_match = [tool for tool in tools if tool.name == args.name] + if len(tool_match) == 0: + raise ApplicationError( + f"Unable to find matching mcp tool by name: {args.name}" + ) + if len(tool_match) > 1: + raise ApplicationError( + f"Found multiple MCP tools with the same name: {args.name}" + ) + tool = tool_match[0] + + # We cannot provide a full-fledged ToolContext so we need to provide only what is needed by the tool + result = await tool.run_async( + args=args.arguments, + tool_context=args.tool_context, # type:ignore ) - if len(tool_match) > 1: - raise ApplicationError( - f"Unable too many matching mcp tools by name: {args.name}" - ) - tool = tool_match[0] - - # We cannot provide a full-fledged ToolContext so we need to provide only what is needed by the tool - result = await tool.run_async( - args=args.arguments, - tool_context=args.tool_context, # type:ignore - ) - return _CallToolResult(result=result, tool_context=args.tool_context) + return _CallToolResult(result=result, tool_context=args.tool_context) + finally: + await toolset.close() return get_tools, call_tool @@ -221,10 +255,17 @@ def __init__( ): """Initializes the Temporal MCP toolset. + .. warning:: + Do not pass secrets, credentials, or API keys through ``factory_argument``. It + is an activity argument, so it is recorded in workflow history and, without a + payload codec, visible in the web UI. Resolve credentials worker-side inside the + toolset factory instead. + Args: name: Name of the toolset (used for activity naming). config: Optional activity configuration. - factory_argument: Optional argument passed to toolset factory. + factory_argument: Optional argument passed to ``toolset_factory``. + Must not contain secrets. not_in_workflow_toolset: Optional factory that returns the underlying ``McpToolset`` to use when this wrapper executes outside ``workflow.in_workflow()``, such as local ADK runs. @@ -281,3 +322,397 @@ async def get_tools( ) for tool_result in tool_results ] + + +def _handle_worker_failure(func: Callable) -> Callable: + """Surface dedicated-worker failures on the run-scoped task queue. + + A schedule-to-start timeout means the dedicated ``-server-session`` worker + never picked the activity up (it is gone); a heartbeat timeout means it + died mid-session. Either way Temporal cannot recreate the in-memory toolset + state, so we re-raise as an ``ApplicationError`` of type + ``"DedicatedWorkerFailure"`` for the caller to handle. + + Duplicated (rather than shared) from ``openai_agents._mcp`` on purpose: + these two contribs do not currently share internal code, and importing + across them would create an unwanted dependency. + """ + + @functools.wraps(func) + async def wrapper(*args: Any, **kwargs: Any): + try: + return await func(*args, **kwargs) + except ActivityError as e: + failure = e.failure + if failure: + cause = failure.cause + if cause: + if ( + cause.timeout_failure_info.timeout_type + == TIMEOUT_TYPE_SCHEDULE_TO_START + ): + raise ApplicationError( + "MCP Stateful Server Worker failed to schedule activity.", + type="DedicatedWorkerFailure", + ) from e + if ( + cause.timeout_failure_info.timeout_type + == TIMEOUT_TYPE_HEARTBEAT + ): + raise ApplicationError( + "MCP Stateful Server Worker failed to heartbeat.", + type="DedicatedWorkerFailure", + ) from e + raise e + + return wrapper + + +@dataclass +class _StatefulServerSessionArguments: + factory_argument: Any | None + + +@dataclass +class _StatefulCallToolArguments: + name: str + arguments: dict[str, Any] + tool_context: TemporalToolContext + + +class TemporalStatefulMcpToolSetProvider: + """Provider for a stateful, pooled MCP toolset backed by a dedicated worker. + + .. warning:: + This class is experimental and may change in future versions. + Use with caution in production environments. + + Unlike :class:`TemporalMcpToolSetProvider` (which builds and closes a fresh + ``McpToolset`` per activity call), this provider maintains a single + ``McpToolset`` for the lifetime of a workflow run. The workflow-side + :class:`TemporalStatefulMcpToolSet` starts a dedicated ``-server-session`` + activity on a task queue scoped to that specific run + (``name@run_id``); that activity builds the toolset once via + ``toolset_factory(factory_argument)``, holds it open, and runs a nested + :py:class:`temporalio.worker.Worker` serving the ``-list-tools``/``-call-tool`` + activities off the same run-scoped queue. The toolset is closed when the + workflow cancels the session activity on cleanup. + + Because state lives in a dedicated worker's memory, the caller must handle + the case where that worker fails, as Temporal cannot seamlessly recreate the + lost toolset. Failure surfaces as an ``ApplicationError`` with + ``type="DedicatedWorkerFailure"``. Prefer the stateless + :class:`TemporalMcpToolSetProvider` unless a persistent connection is + genuinely required. + """ + + def __init__( + self, + name: str, + toolset_factory: Callable[[Any | None], McpToolset], + ) -> None: + """Initializes the stateful toolset provider. + + Args: + name: Name prefix for the generated activities. It is suffixed with + ``-stateful`` so it never collides with a stateless provider of + the same base name registered on the same worker. + toolset_factory: Factory function that creates McpToolset instances. + It should return a new toolset each time so that no state is + shared between workflow runs. + """ + super().__init__() + self._name = name + "-stateful" + self._toolset_factory = toolset_factory + self._toolsets: dict[str, McpToolset] = {} + + @property + def name(self) -> str: + """The activity-name prefix (base name with the ``-stateful`` suffix).""" + return self._name + + def _get_activities(self) -> Sequence[Callable]: + def _server_id() -> str: + return self._name + "@" + (activity.info().workflow_run_id or "") + + @activity.defn(name=self._name + "-list-tools") + async def get_tools() -> list[_ToolResult]: + toolset = self._toolsets[_server_id()] + tools = await toolset.get_tools() + return [ + _ToolResult( + tool.name, + tool.description, + tool.is_long_running, + tool.custom_metadata, + tool._get_declaration(), + ) + for tool in tools + ] + + @activity.defn(name=self._name + "-call-tool") + async def call_tool(args: _StatefulCallToolArguments) -> _CallToolResult: + toolset = self._toolsets[_server_id()] + tools = await toolset.get_tools() + + tool_match = [tool for tool in tools if tool.name == args.name] + if len(tool_match) == 0: + raise ApplicationError( + f"Unable to find matching mcp tool by name: {args.name}" + ) + if len(tool_match) > 1: + raise ApplicationError( + f"Found multiple MCP tools with the same name: {args.name}" + ) + tool = tool_match[0] + + # We cannot provide a full-fledged ToolContext so we need to provide only what is needed by the tool + result = await tool.run_async( + args=args.arguments, + tool_context=args.tool_context, # type:ignore + ) + return _CallToolResult(result=result, tool_context=args.tool_context) + + async def heartbeat_every(delay: float, *details: Any) -> None: + """Heartbeat every ``delay`` seconds until cancelled.""" + while True: + await asyncio.sleep(delay) + activity.heartbeat(*details) + + @activity.defn(name=self._name + "-server-session") + async def connect( + args: _StatefulServerSessionArguments | None = None, + ) -> None: + server_id = self._name + "@" + (activity.info().workflow_run_id or "") + if server_id in self._toolsets: + raise ApplicationError( + "Cannot connect to an already running toolset. Use a distinct " + "name if running multiple stateful toolsets in one workflow." + ) + + # Created only once the duplicate-connect check has passed, and + # cancelled in the outermost ``finally`` below so it is torn down + # on every exit path -- including a ``toolset.close()`` failure, + # which would otherwise leave it heartbeating forever after this + # activity has already exited. + heartbeat_task = asyncio.create_task(heartbeat_every(30)) + try: + toolset = self._toolset_factory(args.factory_argument if args else None) + try: + self._toolsets[server_id] = toolset + try: + worker = Worker( + activity.client(), + task_queue=server_id, + activities=[get_tools, call_tool], + activity_task_poller_behavior=PollerBehaviorSimpleMaximum( + 1 + ), + ) + await worker.run() + finally: + await toolset.close() + finally: + del self._toolsets[server_id] + finally: + heartbeat_task.cancel() + try: + await heartbeat_task + except asyncio.CancelledError: + pass + + return (connect,) + + +class _TemporalStatefulTool(BaseTool): + def __init__( + self, + set_name: str, + config: ActivityConfig, + declaration: FunctionDeclaration | None, + *, + name: str, + description: str, + is_long_running: bool = False, + custom_metadata: dict[str, Any] | None = None, + ): + super().__init__( + name=name, + description=description, + is_long_running=is_long_running, + custom_metadata=custom_metadata, + ) + self._set_name = set_name + self._config = config + self._declaration = declaration + + def _get_declaration(self) -> types.FunctionDeclaration | None: + return self._declaration + + @_handle_worker_failure + async def run_async( + self, *, args: dict[str, Any], tool_context: ToolContext + ) -> Any: + result: _CallToolResult = await workflow.execute_activity( + self._set_name + "-call-tool", + _StatefulCallToolArguments( + self.name, + arguments=args, + tool_context=TemporalToolContext( + tool_confirmation=tool_context.tool_confirmation, + function_call_id=tool_context.function_call_id, + event_actions=tool_context._event_actions, + ), + ), + result_type=_CallToolResult, + **self._config, + ) + + # We need to propagate any event actions back to the main context + tool_context._event_actions = result.tool_context.event_actions + return result.result + + +class TemporalStatefulMcpToolSet(BaseToolset): + """Workflow-side handle for a stateful MCP toolset. + + .. warning:: + This class is experimental and may change in future versions. + Use with caution in production environments. + + Use it as an async context manager inside a workflow so the dedicated + ``-server-session`` worker is started on ``connect`` and torn down on + ``cleanup``:: + + async with TemporalStatefulMcpToolSet("my-tools") as toolset: + agent = Agent(..., tools=[toolset]) + await runner.run(...) + + The connection (and any MCP subprocess) lives for the duration of the + ``async with`` block and is reused across every tool call in that block, + then closed on exit. Callers must be prepared to handle + ``ApplicationError(type="DedicatedWorkerFailure")`` should the dedicated + worker die mid-run. + """ + + def __init__( + self, + name: str, + config: ActivityConfig | None = None, + server_session_config: ActivityConfig | None = None, + factory_argument: Any | None = None, + not_in_workflow_toolset: Callable[[Any | None], McpToolset] | None = None, + ): + """Initializes the stateful Temporal MCP toolset handle. + + Args: + name: Base name of the toolset. Must match the ``name`` passed to + the :class:`TemporalStatefulMcpToolSetProvider` (the + ``-stateful`` suffix is applied here automatically). + config: Optional activity configuration for the per-operation + (``-list-tools``/``-call-tool``) activities. A + ``schedule_to_start_timeout`` is used to detect a dead + dedicated worker. + server_session_config: Optional activity configuration for the + long-running ``-server-session`` activity. + factory_argument: Optional argument passed once to the toolset + factory when the session connects. + not_in_workflow_toolset: Optional factory that returns the + underlying ``McpToolset`` to use when this wrapper executes + outside ``workflow.in_workflow()``, such as local ADK runs. + """ + super().__init__() + self._name = name + "-stateful" + self._config = config or ActivityConfig( + start_to_close_timeout=timedelta(minutes=1), + schedule_to_start_timeout=timedelta(seconds=30), + ) + self._server_session_config = server_session_config or ActivityConfig( + start_to_close_timeout=timedelta(hours=1), + ) + self._factory_argument = factory_argument + self._not_in_workflow_toolset = not_in_workflow_toolset + self._connect_handle: ActivityHandle | None = None + + async def connect(self) -> None: + """Starts the dedicated ``-server-session`` activity for this run.""" + if not workflow.in_workflow(): + return + self._config["task_queue"] = self._name + "@" + workflow.info().run_id + self._connect_handle = workflow.start_activity( + self._name + "-server-session", + _StatefulServerSessionArguments(self._factory_argument), + **self._server_session_config, + ) + + async def cleanup(self) -> None: + """Cancels the dedicated session activity and awaits its teardown.""" + if self._connect_handle: + self._connect_handle.cancel() + try: + await self._connect_handle + except Exception as e: + if not is_cancelled_exception(e): + raise + finally: + self._connect_handle = None + + async def close(self) -> None: + """``BaseToolset`` teardown hook; delegates to :meth:`cleanup`.""" + await self.cleanup() + + async def __aenter__(self) -> "TemporalStatefulMcpToolSet": + """Connects the toolset and returns it for use as a context manager.""" + await self.connect() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + """Cleans up the toolset when exiting the context manager.""" + await self.cleanup() + + @_handle_worker_failure + async def get_tools( + self, readonly_context: ReadonlyContext | None = None + ) -> list[BaseTool]: + """Retrieves available tools from the stateful MCP toolset.""" + # If executed outside a workflow, like when doing local adk runs, use the mcp server directly + if not workflow.in_workflow(): + if self._not_in_workflow_toolset is None: + raise ValueError( + "Attempted to use TemporalStatefulMcpToolSet outside a " + "workflow, but no not_in_workflow_toolset was provided. " + "Either use McpToolSet directly or pass a factory that " + "returns the underlying McpToolset for non-workflow execution." + ) + return await self._not_in_workflow_toolset( + self._factory_argument + ).get_tools(readonly_context) + + if not self._connect_handle: + raise ApplicationError( + "Stateful MCP toolset not connected. Use it as an async context " + "manager (async with ...) or call connect() first." + ) + + tool_results: list[_ToolResult] = await workflow.execute_activity( + self._name + "-list-tools", + result_type=list[_ToolResult], + **self._config, + ) + return [ + _TemporalStatefulTool( + set_name=self._name, + config=self._config, + declaration=tool_result.function_declaration, + name=tool_result.name, + description=tool_result.description, + is_long_running=tool_result.is_long_running, + custom_metadata=tool_result.custom_metadata, + ) + for tool_result in tool_results + ] diff --git a/temporalio/contrib/google_adk_agents/_plugin.py b/temporalio/contrib/google_adk_agents/_plugin.py index 7344485c8..15b6613e3 100644 --- a/temporalio/contrib/google_adk_agents/_plugin.py +++ b/temporalio/contrib/google_adk_agents/_plugin.py @@ -8,7 +8,10 @@ from typing import Any from temporalio import workflow -from temporalio.contrib.google_adk_agents._mcp import TemporalMcpToolSetProvider +from temporalio.contrib.google_adk_agents._mcp import ( + TemporalMcpToolSetProvider, + TemporalStatefulMcpToolSetProvider, +) from temporalio.contrib.google_adk_agents._model import ( invoke_model, invoke_model_streaming, @@ -72,12 +75,18 @@ class GoogleAdkPlugin(SimplePlugin): def __init__( self, - toolset_providers: list[TemporalMcpToolSetProvider] | None = None, + toolset_providers: list[ + TemporalMcpToolSetProvider | TemporalStatefulMcpToolSetProvider + ] + | None = None, ): """Initializes the Temporal ADK Plugin. Args: - toolset_providers: Optional list of toolset providers for MCP integration. + toolset_providers: Optional list of stateless + (:class:`TemporalMcpToolSetProvider`) or stateful + (:class:`TemporalStatefulMcpToolSetProvider`) toolset providers + for MCP integration. """ @asynccontextmanager diff --git a/temporalio/contrib/google_adk_agents/workflow.py b/temporalio/contrib/google_adk_agents/workflow.py index 274dde807..23b254123 100644 --- a/temporalio/contrib/google_adk_agents/workflow.py +++ b/temporalio/contrib/google_adk_agents/workflow.py @@ -1,13 +1,193 @@ """Workflow utilities for Google ADK agents integration with Temporal.""" +import functools import inspect -from typing import Any, Callable +import types +import typing +from dataclasses import dataclass, field +from typing import Any, Callable, cast import temporalio.workflow from temporalio import workflow +_TOOL_CONTEXT_PARAM = "tool_context" -def activity_tool(activity_def: Callable, **kwargs: Any) -> Callable: + +@dataclass(frozen=True) +class ToolContextSnapshot: + """Serializable snapshot of the ADK ``ToolContext`` for activity-backed tools. + + .. warning:: + This class is experimental and may change in future versions. + Use with caution in production environments. + + ADK's ``ToolContext`` holds live, non-serializable objects, so it cannot + cross the activity boundary: activity inputs are sent to the server and + may run on a different worker than the workflow. This snapshot carries the + serializable subset instead. + + Declare a parameter named ``tool_context`` annotated with this type (or + ``ToolContextSnapshot | None``) on an activity wrapped by + :func:`activity_as_tool`: + + .. code-block:: python + + @activity.defn + async def get_weather(query: str, tool_context: ToolContextSnapshot) -> dict: + db_url = tool_context.state.get("url", "") + ... + + Exactly like a native ADK function tool's ``tool_context`` parameter, it + is excluded from the LLM-facing tool schema and filled in at invocation + time — here with a snapshot taken from the live ``ToolContext`` before the + activity is scheduled. + + When running under Temporal, the entire session state crosses the + activity boundary: every value in it must be serializable by the + configured data converter and the total size must fit within payload + limits, even for keys the tool never reads. A non-serializable value + fails the workflow task when the activity is scheduled. Local ADK runs + pass the snapshot in memory and have no such constraint. + + The snapshot is one-way and should be treated as read-only: mutating it + inside the activity does not propagate back to the session (and in local + runs nested values may alias the live session state, so mutating them can + corrupt the session). To modify session state, return the needed + information from the activity and apply it in workflow-side code (for + example an ADK callback or a plain tool function). + + Attributes: + state: The session state visible to this tool call, as a plain dict. + function_call_id: The id of the function call being handled, when + available. + """ + + state: dict[str, Any] = field(default_factory=dict) + function_call_id: str | None = None + + +def _annotation_members(annotation: Any) -> tuple[Any, ...]: + """Returns a union annotation's members, or the annotation itself.""" + origin = typing.get_origin(annotation) + if origin is typing.Union or origin is types.UnionType: # pyright: ignore[reportDeprecated] + return typing.get_args(annotation) + return (annotation,) + + +def _annotation_display(members: tuple[Any, ...]) -> str: + """Renders annotation members for error messages.""" + return " | ".join( + "None" if member is type(None) else getattr(member, "__name__", str(member)) + for member in members + ) + + +def _adk_context_error( + activity_def: Callable, name: str, annotation_name: str +) -> ValueError: + """Builds the error for a parameter annotated with a live ADK context.""" + return ValueError( + f"Activity '{activity_def.__name__}' declares '{name}:" + f" {annotation_name}', but ADK context objects are not serializable" + " and cannot be activity arguments. Declare a parameter named" + " 'tool_context' annotated with ToolContextSnapshot instead to receive" + " the serializable subset (session state and function-call id)." + ) + + +def _validated_tool_context_parameter( + activity_def: Callable, parameter: inspect.Parameter, members: tuple[Any, ...] +) -> inspect.Parameter: + """Returns the ``tool_context`` parameter after validating its annotation. + + The parameter must be annotated with :class:`ToolContextSnapshot` or + ``ToolContextSnapshot | None``. + """ + if parameter.annotation is inspect.Parameter.empty: + raise ValueError( + f"Activity '{activity_def.__name__}' has an unannotated" + " 'tool_context' parameter. Annotate it with ToolContextSnapshot" + " to receive the serializable subset of the ADK tool context" + " (session state and function-call id)." + ) + non_none = [member for member in members if member is not type(None)] + if non_none == [ToolContextSnapshot]: + return parameter + annotation_name = _annotation_display(members) + if any( + getattr(member, "__module__", "").startswith("google.adk") + for member in non_none + ): + raise _adk_context_error(activity_def, _TOOL_CONTEXT_PARAM, annotation_name) + raise ValueError( + f"Activity '{activity_def.__name__}' has a 'tool_context' parameter" + f" annotated with {annotation_name}. The name 'tool_context' is" + " reserved by ADK for context injection; annotate the parameter with" + " ToolContextSnapshot to receive the serializable subset of the tool" + " context." + ) + + +def _tool_context_parameter(activity_def: Callable) -> inspect.Parameter | None: + """Validates context-related parameters and returns the ``tool_context`` one. + + ADK injects the live context into the first parameter annotated with an + ADK context type (regardless of name), or failing that into one named + ``tool_context``, and excludes that parameter from the LLM-facing tool + schema. Live context objects cannot cross the activity boundary, so the + only supported declaration is a parameter named ``tool_context`` annotated + with :class:`ToolContextSnapshot` (or ``ToolContextSnapshot | None``); + anything else ADK would treat as a context parameter is rejected at wrap + time, as is a misplaced ToolContextSnapshot annotation that would leak + into the tool schema. + """ + try: + hints = typing.get_type_hints(activity_def) + except Exception: + hints = {} + adk_context: type[Any] | None + try: + from google.adk.tools.tool_context import ToolContext + + adk_context = ToolContext + except ImportError: + adk_context = None + tool_context_parameter: inspect.Parameter | None = None + for name, parameter in inspect.signature(activity_def).parameters.items(): + members = _annotation_members(hints.get(name, parameter.annotation)) + if name == _TOOL_CONTEXT_PARAM: + tool_context_parameter = _validated_tool_context_parameter( + activity_def, parameter, members + ) + elif adk_context is not None and any( + member is adk_context for member in members + ): + raise _adk_context_error(activity_def, name, _annotation_display(members)) + elif any(member is ToolContextSnapshot for member in members): + raise ValueError( + f"Activity '{activity_def.__name__}' annotates parameter" + f" '{name}' with ToolContextSnapshot, but ADK only injects the" + " tool context into a parameter named 'tool_context'; under" + " any other name it would appear in the LLM-facing tool" + " schema. Rename the parameter to 'tool_context'." + ) + return tool_context_parameter + + +def _snapshot_tool_context(tool_context: Any) -> ToolContextSnapshot: + """Builds the serializable snapshot from a live ADK ``ToolContext``.""" + state: dict[str, Any] = {} + state_object = getattr(tool_context, "state", None) + if state_object is not None: + to_dict = getattr(state_object, "to_dict", None) + state = dict(cast(Any, to_dict() if callable(to_dict) else state_object)) + return ToolContextSnapshot( + state=state, + function_call_id=getattr(tool_context, "function_call_id", None), + ) + + +def activity_as_tool(activity_def: Callable, **kwargs: Any) -> Callable: """Decorator/Wrapper to wrap a Temporal Activity as an ADK Tool. .. warning:: @@ -16,9 +196,25 @@ def activity_tool(activity_def: Callable, **kwargs: Any) -> Callable: This ensures the activity's signature is preserved for ADK's tool schema generation while marking it as a tool that executes via 'workflow.execute_activity'. + + If the activity declares a parameter named ``tool_context``, it must be + annotated with :class:`ToolContextSnapshot` (or ``ToolContextSnapshot | + None``). ADK excludes the parameter from the tool schema and injects the + live ``ToolContext`` into the wrapper, which passes the activity a + serializable snapshot of it (session state and function-call id) in that + parameter's position. Annotating any parameter with a live ADK context + type raises ``ValueError`` at wrap time, since ADK would inject the + non-serializable context into it regardless of its name. """ + tool_context_param = _tool_context_parameter(activity_def) + @functools.wraps(activity_def) async def wrapper(*args: Any, **kw: Any): + # ADK injects the live ToolContext by name; replace it with the + # serializable snapshot the activity actually declares. + if tool_context_param is not None and _TOOL_CONTEXT_PARAM in kw: + kw[_TOOL_CONTEXT_PARAM] = _snapshot_tool_context(kw[_TOOL_CONTEXT_PARAM]) + # Inspect signature to bind arguments sig = inspect.signature(activity_def) bound = sig.bind(*args, **kw) @@ -48,9 +244,8 @@ async def wrapper(*args: Any, **kw: Any): activity_def, args=activity_args, **options ) - # Copy metadata - wrapper.__name__ = activity_def.__name__ - wrapper.__doc__ = activity_def.__doc__ + # functools.wraps copies name/doc/module/annotations/qualname/dict. + # Signature must be set explicitly since the wrapper uses *args/**kw. setattr(wrapper, "__signature__", inspect.signature(activity_def)) return wrapper diff --git a/temporalio/contrib/google_genai/README.md b/temporalio/contrib/google_genai/README.md new file mode 100644 index 000000000..fca5311f5 --- /dev/null +++ b/temporalio/contrib/google_genai/README.md @@ -0,0 +1,285 @@ +# Google Gemini SDK Integration for Temporal + +> ⚠️ **Experimental.** This integration may change in future versions. Use with +> caution in production. + +## Overview + +This plugin lets you use the [Google Gemini SDK](https://googleapis.github.io/python-genai/) +(`google-genai`) inside Temporal workflows with durable execution. Every Gemini +API call becomes a **Temporal activity**, so model calls, tool calls, file +operations, interactions, and managed agents are retried, recorded in history, +and survive worker restarts. + +Key properties: + +- **Credentials never enter the workflow.** The real `genai.Client` lives only + on the worker, inside activities; no API keys or tokens appear in event + history. +- **The SDK's automatic function calling (AFC) loop runs in the workflow**, so + tool wrappers (`activity_as_tool`) work naturally — no manual agent loop. +- **Temporal owns retries.** Configure them via the activity `retry_policy`; the + SDK's own retry loop is rejected to avoid double-retry (see + [Retries & errors](#retries--errors)). + +## Install + +```bash +uv add temporalio google-genai +# For client-side MCP support, also: +uv add mcp +``` + +## Hello World + +```python +import os +from datetime import timedelta + +from google import genai +from google.genai import types + +from temporalio import activity, workflow +from temporalio.client import Client +from temporalio.contrib.google_genai import ( + GoogleGenAIPlugin, + TemporalAsyncClient, + activity_as_tool, +) +from temporalio.worker import Worker +from temporalio.workflow import ActivityConfig + + +# ---- a tool, as a normal Temporal activity (runs on the worker) ---- +@activity.defn +async def get_weather(city: str) -> str: + return f"It's sunny in {city}." + + +# ---- the workflow (runs in the Temporal sandbox) ---- +@workflow.defn +class WeatherAgent: + @workflow.run + async def run(self, prompt: str) -> str: + client = TemporalAsyncClient() + response = await client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + config=types.GenerateContentConfig( + tools=[ + activity_as_tool( + get_weather, + activity_config=ActivityConfig( + start_to_close_timeout=timedelta(seconds=30), + ), + ), + ], + ), + ) + return response.text or "" + + +# ---- worker setup (outside the sandbox: real client + credentials) ---- +async def main() -> None: + gemini = genai.Client(api_key=os.environ["GOOGLE_API_KEY"]) + plugin = GoogleGenAIPlugin(gemini) + + client = await Client.connect("localhost:7233", plugins=[plugin]) + async with Worker( + client, + task_queue="gemini", + workflows=[WeatherAgent], + activities=[get_weather], + ): + result = await client.execute_workflow( + WeatherAgent.run, + "What's the weather in Tokyo?", + id="weather-1", + task_queue="gemini", + ) + print(result) +``` + +Construct `TemporalAsyncClient` **inside** the workflow; construct the real +`genai.Client` and `GoogleGenAIPlugin` **on the worker**. + +## What this plugin gives you + +| Surface | Workflow API | Runs as | +| --- | --- | --- | +| Model calls | `client.models.generate_content` / `generate_content_stream` | activity (AFC loop in workflow) | +| Tools | `activity_as_tool(fn, ...)` | one activity per tool call | +| Files | `client.files.upload` / `download` | activity | +| File search | `client.file_search_stores.upload_to_file_search_store` | activity | +| Interactions | `client.interactions.create` / `get` / `cancel` / `delete` | whole-operation activity | +| Managed agents | `client.agents.create` / `get` / `list` / `delete` | whole-operation activity | +| MCP (client-side) | `TemporalMcpClientSession(name)` in `tools=[...]` | `list_tools` / `call_tool` activities | + +Streamed responses are batched: the activity drains the stream and the workflow +iterates the collected chunks/events. `client.webhooks` is not supported in +workflows and raises. + +## Tool calling + +`activity_as_tool` wraps any `@activity.defn` function as a Gemini tool. When the +model calls it, the AFC loop (running in the workflow) dispatches it as a +durable activity: + +```python +activity_as_tool( + get_weather, + activity_config=ActivityConfig(start_to_close_timeout=timedelta(seconds=30)), +) +``` + +A timeout is required — `activity_config` must set `start_to_close_timeout` or +`schedule_to_close_timeout` (Temporal needs one; there is no default for tools). + +## MCP support + +Client-side MCP (Gemini Developer API) is wired through the plugin: register the +server on the worker and reference it by name in the workflow. + +```python +from contextlib import asynccontextmanager +import sys + +from mcp import ClientSession, StdioServerParameters +from mcp.client.stdio import stdio_client + +from temporalio.contrib.google_genai import TemporalMcpClientSession + + +# ---- worker: a factory yielding a connected, initialized session ---- +@asynccontextmanager +async def weather_mcp(): + params = StdioServerParameters(command=sys.executable, args=["weather_server.py"]) + async with stdio_client(params) as (read, write): + async with ClientSession(read, write) as session: + await session.initialize() + yield session + + +plugin = GoogleGenAIPlugin( + genai.Client(api_key=os.environ["GOOGLE_API_KEY"]), + mcp_servers={"weather": weather_mcp}, + mcp_connection_idle_timeout=timedelta(minutes=5), +) + + +# ---- workflow: reference the server by name in the tools list ---- +@workflow.defn +class McpAgent: + @workflow.run + async def run(self, prompt: str) -> str: + client = TemporalAsyncClient() + session = TemporalMcpClientSession( + "weather", + activity_config=ActivityConfig(start_to_close_timeout=timedelta(seconds=30)), + ) + response = await client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + config=types.GenerateContentConfig(tools=[session]), + ) + return response.text or "" +``` + +The MCP connection lives on the worker (pooled, idle-evicted); the workflow only +carries the server name. Tool discovery and calls run as `{name}-list-tools` / +`{name}-call-tool` activities, so the full tool parameter schema reaches the +model. Set `cache_tools=True` to list a server's tools once per workflow instead +of per turn. + +## Streaming + +`generate_content_stream` works as usual — the workflow iterates chunks (batched +from the activity). To let an **external** consumer (a chat UI) observe chunks in +real time while the workflow runs durably, set `streaming_topic` on the client +and host a [`WorkflowStream`](../workflow_streams/) in the workflow. Each +streamed `GenerateContentResponse` is published to that topic as it arrives: + +```python +from temporalio.contrib.workflow_streams import WorkflowStream + + +@workflow.defn +class StreamingAgent: + @workflow.init + def __init__(self, prompt: str) -> None: + self.stream = WorkflowStream() # required when streaming_topic is set + + @workflow.run + async def run(self, prompt: str) -> str: + client = TemporalAsyncClient(streaming_topic="gemini") + text = [] + async for chunk in await client.models.generate_content_stream( + model="gemini-2.5-flash", contents=prompt, + ): + text.append(chunk.text or "") + return "".join(text) +``` + +Consume the stream from outside the workflow: + +```python +from temporalio.contrib.workflow_streams import WorkflowStreamClient + + +async def consume(client, workflow_id): + stream = WorkflowStreamClient.create(client, workflow_id) + async for item in stream.subscribe( + ["gemini"], result_type=types.GenerateContentResponse, + ): + print(item.data.text, end="", flush=True) +``` + +The workflow's own iteration is unchanged (it still receives batched chunks for +the SDK to parse); the topic is purely for external real-time observation. If +`streaming_topic` is set but the workflow hosts no `WorkflowStream`, the call +raises `GoogleGenAIError`. Tune flush cadence with +`TemporalAsyncClient(streaming_topic=..., streaming_batch_interval=...)` +(default 100ms). + +## Retries & errors + +Temporal owns retries. Configure them with the activity `retry_policy` via +`activity_config`. The plugin **rejects** the SDK's own retry config so retries +don't compound: + +- Constructing the plugin with a `genai.Client` that has + `http_options.retry_options` raises `ValueError`. +- Setting `http_options.retry_options` on a per-request call raises + `GoogleGenAIError`. + +API-call activities classify failures: transient statuses (408, 429, 5xx) stay +retryable (the activity's `retry_policy` applies); other statuses (e.g. 4xx) are +non-retryable so the workflow fails fast. + +## Vertex AI + +Pass `vertexai=True` to both the worker-side `genai.Client` and the +workflow-side `TemporalAsyncClient`. On the workflow side you must also set +`project` and `location` **explicitly**: + +```python +# worker +genai.Client(vertexai=True, project="my-project", location="us-central1") + +# workflow +TemporalAsyncClient(vertexai=True, project="my-project", location="us-central1") +``` + +Normally the SDK auto-discovers `project`/`location` from the environment +(credentials, ADC, metadata server). That discovery +would be non-deterministic and break replay. Setting them by hand +keeps it deterministic. + +## Composing with other plugins + +`GoogleGenAIPlugin` is a `temporalio.plugin.SimplePlugin`; pass it in the +`plugins=[...]` list alongside others (e.g. OpenTelemetry). It contributes a +Pydantic data converter, the Gemini activities, a sandbox-passthrough config for +`google.genai` (and `mcp`), and registers `GoogleGenAIError` as a workflow +failure type. When composing data converters, construct the plugins so their +converters are compatible. diff --git a/temporalio/contrib/google_genai/__init__.py b/temporalio/contrib/google_genai/__init__.py new file mode 100644 index 000000000..8cedcbc63 --- /dev/null +++ b/temporalio/contrib/google_genai/__init__.py @@ -0,0 +1,119 @@ +"""First-class Temporal integration for the Google Gemini SDK. + +.. warning:: + This module is experimental and may change in future versions. + Use with caution in production environments. + +This integration lets you use the Gemini SDK's async client with full +automatic function calling (AFC) support. Every API call becomes a +**durable Temporal activity**. Tools default to plain workflow methods +that run deterministically in-workflow; wrap any ``@activity.defn`` with +:func:`activity_as_tool` to run a tool as a durable activity instead. + +No credentials are fetched in the workflow, and no auth material appears in +Temporal's event history. + +- :class:`GoogleGenAIPlugin` — registers the Gemini SDK activities using a + caller-provided ``genai.Client`` on the worker side. +- :class:`TemporalAsyncClient` — construct from a workflow to get an + ``AsyncClient`` that routes API calls through activities. +- :func:`activity_as_tool` — convert any ``@activity.defn`` function into a + Gemini tool callable; Gemini's AFC invokes it as a Temporal activity. + +The Interactions API (``client.interactions``) and managed agents +(``client.agents``) are supported as whole-operation activities; streamed +interactions are batched (the activity drains the SSE stream and the +workflow iterates the collected events). ``client.webhooks`` is not +supported in workflows. The Interactions API has no automatic function +calling: declare tools as ``{"type": "function", ...}`` dicts (per the +Gemini docs) and drive the tool loop yourself, executing each call via +``workflow.execute_activity`` or an :func:`activity_as_tool` callable. + +MCP is supported across three paths. Client-side MCP (Gemini Developer API) +uses :class:`TemporalMcpClientSession`: register a server with +``GoogleGenAIPlugin(mcp_servers={name: factory})`` on the worker, then place +``TemporalMcpClientSession(name)`` in a ``generate_content`` ``tools`` list — +the SDK's AFC loop drives it, with ``list_tools`` / ``call_tool`` running as +activities against a pooled worker-side connection. Server-side MCP on Vertex +AI (``Tool(mcp_servers=[McpServer(...)])``) and the Interactions API's MCP step +types are executed by Google's backend and flow through unchanged as request / +response data — no extra wiring needed. Client-side MCP requires the ``mcp`` +package. + +Streaming: set ``TemporalAsyncClient(streaming_topic=...)`` and host a +:class:`temporalio.contrib.workflow_streams.WorkflowStream` in the workflow's +``@workflow.init``. Each ``generate_content_stream`` chunk is then published to +that topic as it arrives, so external consumers can observe model output in real +time while the workflow runs durably; the workflow's own iteration is unchanged. + +Quickstart:: + + # ---- worker setup (outside the Temporal Python Sandbox) ---- + client = genai.Client(api_key=os.environ["GOOGLE_API_KEY"]) + plugin = GoogleGenAIPlugin(client) + + @activity.defn + async def get_weather(state: str) -> str: ... + + # ---- workflow (inside the Temporal Python Sandbox) ---- + @workflow.defn + class AgentWorkflow: + @workflow.run + async def run(self, query: str) -> str: + client = TemporalAsyncClient() + response = await client.models.generate_content( + model="gemini-2.5-flash", + contents=query, + config=types.GenerateContentConfig( + tools=[ + activity_as_tool( + get_weather, + activity_config=ActivityConfig( + start_to_close_timeout=timedelta(seconds=30), + ), + ), + ], + ), + ) + return response.text +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from temporalio.contrib.google_genai._errors import GoogleGenAIError +from temporalio.contrib.google_genai._google_genai_plugin import GoogleGenAIPlugin +from temporalio.contrib.google_genai._temporal_async_client import ( + TemporalAsyncClient, +) +from temporalio.contrib.google_genai.workflow import ( + activity_as_tool, +) + +if TYPE_CHECKING: + from temporalio.contrib.google_genai._temporal_mcp import TemporalMcpClientSession + +__all__ = [ + "GoogleGenAIError", + "GoogleGenAIPlugin", + "TemporalAsyncClient", + "TemporalMcpClientSession", + "activity_as_tool", +] + + +def __getattr__(name: str) -> Any: + """Lazily expose ``TemporalMcpClientSession`` without importing ``mcp`` eagerly. + + ``mcp`` is an optional dependency, so importing this package must not require + it; the import (and any resulting ``ImportError``) is deferred until the + symbol is actually accessed. + """ + if name == "TemporalMcpClientSession": + from temporalio.contrib.google_genai._temporal_mcp import ( + TemporalMcpClientSession, + ) + + return TemporalMcpClientSession + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/temporalio/contrib/google_genai/_compat.py b/temporalio/contrib/google_genai/_compat.py new file mode 100644 index 000000000..6613e2b9c --- /dev/null +++ b/temporalio/contrib/google_genai/_compat.py @@ -0,0 +1,24 @@ +"""Version-compatibility shims for the ``google-genai`` SDK. + +Single home for workarounds that keep the plugin importable and +type-checkable across the supported ``google-genai`` range; remove entries +as upstream fixes land. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + # google-genai 2.12.0 exports two ``Interaction`` names: the interaction + # resource class and a ``TypeAliasType`` over the trigger-request + # variants. At runtime we get the resource class but type checkers + # resolve to the alias which causes failures (the alias has no ``id``, + # etc.). Force the type checker to see the class' defining module (in + # the same location in 2.10 and 2.12). + from google.genai._gaos.types.interactions.interaction import Interaction +else: + # At runtime, Interaction resolves properly for 2.10 and 2.12 + from google.genai.interactions import Interaction + +__all__ = ["Interaction"] diff --git a/temporalio/contrib/google_genai/_errors.py b/temporalio/contrib/google_genai/_errors.py new file mode 100644 index 000000000..3f6595e4b --- /dev/null +++ b/temporalio/contrib/google_genai/_errors.py @@ -0,0 +1,16 @@ +"""Error types for the Google Gemini SDK Temporal integration.""" + +from __future__ import annotations + +from temporalio.exceptions import ApplicationError + + +class GoogleGenAIError(ApplicationError): + """Error raised by the Google Gemini Temporal integration. + + Registered with the worker (and replayer) via + ``workflow_failure_exception_types`` so that, when raised in workflow code, + it terminally fails the workflow execution rather than failing the workflow + task and retrying it indefinitely. Use it for conditions that cannot be + recovered by retry — e.g. a tool that is not a valid Temporal activity. + """ diff --git a/temporalio/contrib/google_genai/_gemini_activity.py b/temporalio/contrib/google_genai/_gemini_activity.py new file mode 100644 index 000000000..496bbf1ab --- /dev/null +++ b/temporalio/contrib/google_genai/_gemini_activity.py @@ -0,0 +1,356 @@ +"""Temporal activity that executes Gemini SDK API calls with real credentials. + +The ``TemporalApiClient`` in the workflow dispatches calls here. This +activity holds a user-provided ``genai.Client`` and forwards structured +requests. Credentials are fetched/refreshed only within the activity — +they never appear in workflow event history. +""" + +from __future__ import annotations + +from collections.abc import Sequence +from contextlib import AsyncExitStack +from datetime import timedelta +from typing import Any, Callable + +import google.auth.credentials +from google.genai import Client as GeminiClient +from google.genai import errors as genai_errors +from google.genai import types +from google.genai.types import HttpOptions +from google.genai.types import HttpResponse as SdkHttpResponse + +from temporalio import activity +from temporalio.contrib.google_genai._compat import Interaction +from temporalio.contrib.google_genai._models import ( + _GeminiApiRequest, + _GeminiApiResponse, + _GeminiApiStreamedResponse, + _GeminiDownloadFileRequest, + _GeminiInteractionIdRequest, + _GeminiInteractionRequest, + _GeminiInteractionStreamedResponse, + _GeminiRegisterFilesRequest, + _GeminiUploadFileRequest, + _GeminiUploadToFileSearchStoreRequest, +) +from temporalio.contrib.workflow_streams import WorkflowStreamClient +from temporalio.exceptions import ApplicationError + + +def _resolve_http_options( + overrides: Any, +) -> HttpOptions | None: + """Reconstruct ``HttpOptions`` from serializable overrides, or None.""" + if overrides is None: + return None + return HttpOptions.model_validate(overrides.model_dump(exclude_none=True)) + + +# HTTP status codes the Gemini SDK itself treats as transient/retryable. +_RETRYABLE_HTTP_STATUS = frozenset({408, 429, 500, 502, 503, 504}) + + +def _classify_api_error(err: genai_errors.APIError) -> ApplicationError: + """Map a Gemini ``APIError`` to an ``ApplicationError`` Temporal can act on. + + Transient statuses (timeouts, rate limits, 5xx) stay retryable so the + activity's retry policy applies; everything else (e.g. 4xx client errors) + is marked non-retryable so the workflow fails fast instead of retrying a + request that cannot succeed. + """ + code = getattr(err, "code", None) + retryable = code in _RETRYABLE_HTTP_STATUS + return ApplicationError( + str(err), + type=type(err).__name__, + non_retryable=not retryable, + ) + + +async def _drain_interaction_stream( + stream: Any, +) -> _GeminiInteractionStreamedResponse: + """Collect every SSE event from an interaction stream, heartbeating per event. + + ``stream`` is the SDK's async streaming response; its concrete class is not a + stable public name, so it is typed structurally — only ``async with`` / + ``async for`` / ``event.model_dump(...)`` are used. + """ + events: list[dict[str, Any]] = [] + async with stream: + async for event in stream: + activity.heartbeat() + events.append( + event.model_dump(by_alias=True, exclude_none=True, mode="json") + ) + return _GeminiInteractionStreamedResponse(events=events) + + +class GeminiApiCaller: + """Wraps a ``genai.Client`` and exposes Temporal activities for SDK calls. + + The caller owns a reference to the user-provided ``genai.Client``. + All credential management, HTTP client configuration, etc. is the + responsibility of whoever constructs the client. + """ + + def __init__( + self, + client: GeminiClient, + credentials: google.auth.credentials.Credentials | None = None, + ) -> None: + """Initialize with a genai.Client and optional extra credentials.""" + self._client = client + self._credentials = credentials + + def activities(self) -> Sequence[Callable]: + """Return activities that route SDK calls through this client.""" + + @activity.defn + async def gemini_api_client_async_request( + req: _GeminiApiRequest, + ) -> _GeminiApiResponse: + """Execute a Gemini SDK API call with real credentials.""" + try: + response: SdkHttpResponse = ( + await self._client.aio._api_client.async_request( + http_method=req.http_method, + path=req.path, + request_dict=req.request_dict, + http_options=_resolve_http_options(req.http_options_overrides), + ) + ) + except genai_errors.APIError as err: + raise _classify_api_error(err) from err + return _GeminiApiResponse( + headers=response.headers or {}, + body=response.body or "", + ) + + @activity.defn + async def gemini_api_client_async_request_streamed( + req: _GeminiApiRequest, + ) -> _GeminiApiStreamedResponse: + """Execute a streamed Gemini SDK API call, collecting all chunks. + + When ``req.streaming_topic`` is set, each chunk is also published to + that workflow-stream topic (parsed as ``GenerateContentResponse``) + as it arrives, so external consumers see the model output in real + time. Chunks are still returned batched for the SDK to parse + in-workflow; publishing is best-effort and never breaks that path. + """ + chunks: list[_GeminiApiResponse] = [] + try: + async with AsyncExitStack() as stack: + topic = None + if req.streaming_topic: + publisher = WorkflowStreamClient.from_within_activity( + batch_interval=timedelta( + milliseconds=req.streaming_batch_interval_ms + ), + ) + await stack.enter_async_context(publisher) + topic = publisher.topic( + req.streaming_topic, type=types.GenerateContentResponse + ) + + stream = await self._client.aio._api_client.async_request_streamed( + http_method=req.http_method, + path=req.path, + request_dict=req.request_dict, + http_options=_resolve_http_options(req.http_options_overrides), + ) + async for chunk in stream: + body = chunk.body or "" + chunks.append( + _GeminiApiResponse(headers=chunk.headers or {}, body=body) + ) + if topic is not None and body: + try: + topic.publish( + types.GenerateContentResponse.model_validate_json( + body + ) + ) + except Exception: + # Best-effort: a malformed/transform-needing chunk + # must not break the batched return. + pass + except genai_errors.APIError as err: + raise _classify_api_error(err) from err + return _GeminiApiStreamedResponse(chunks=chunks) + + @activity.defn + async def gemini_files_upload( + req: _GeminiUploadFileRequest, + ) -> types.File: + """Upload a file using the real genai.Client on the worker.""" + if req.file_bytes is not None: + import io + + file_arg: Any = io.BytesIO(req.file_bytes) + else: + file_arg = req.file_path + + return await self._client.aio.files.upload(file=file_arg, config=req.config) + + @activity.defn + async def gemini_files_download( + req: _GeminiDownloadFileRequest, + ) -> bytes: + """Download a file using the real genai.Client on the worker.""" + return await self._client.aio.files.download( + file=req.file, config=req.config + ) + + @activity.defn + async def gemini_files_register( + req: _GeminiRegisterFilesRequest, + ) -> types.RegisterFilesResponse: + """Register GCS files using the real genai.Client on the worker. + + Uses ``credentials`` if provided at plugin init, + otherwise falls back to the client's own credentials. + Token refresh happens here on the worker side, so no auth + material enters the workflow event history. + """ + auth = self._credentials or self._client._api_client._credentials + if auth is None: + raise ValueError( + "No credentials available for register_files(). " + "Pass extra_credentials to GoogleGenAIPlugin or initialize " + "the genai.Client with credentials." + ) + return await self._client.aio.files.register_files( + auth=auth, + uris=req.uris, + config=req.config, + ) + + @activity.defn + async def gemini_file_search_stores_upload( + req: _GeminiUploadToFileSearchStoreRequest, + ) -> types.UploadToFileSearchStoreOperation: + """Upload a file to a file search store on the worker.""" + if req.file_bytes is not None: + import io + + file_arg: Any = io.BytesIO(req.file_bytes) + else: + file_arg = req.file_path + + return ( + await self._client.aio.file_search_stores.upload_to_file_search_store( + file_search_store_name=req.file_search_store_name, + file=file_arg, + config=req.config, + ) + ) + + @activity.defn + async def gemini_interactions_create( + req: _GeminiInteractionRequest, + ) -> dict[str, Any]: + """Create an interaction using the real genai.Client on the worker.""" + interaction = await self._client.aio.interactions.create(**req.params) + assert isinstance(interaction, Interaction) + return interaction.model_dump(by_alias=True, exclude_none=True, mode="json") + + @activity.defn + async def gemini_interactions_create_streamed( + req: _GeminiInteractionRequest, + ) -> _GeminiInteractionStreamedResponse: + """Create a streamed interaction, collecting all SSE events.""" + stream = await self._client.aio.interactions.create( + stream=True, **req.params + ) + assert not isinstance(stream, Interaction) + return await _drain_interaction_stream(stream) + + @activity.defn + async def gemini_interactions_get( + req: _GeminiInteractionIdRequest, + ) -> dict[str, Any]: + """Get an interaction using the real genai.Client on the worker.""" + interaction = await self._client.aio.interactions.get(req.id, **req.params) + return interaction.model_dump(by_alias=True, exclude_none=True, mode="json") + + @activity.defn + async def gemini_interactions_get_streamed( + req: _GeminiInteractionIdRequest, + ) -> _GeminiInteractionStreamedResponse: + """Get a streamed interaction, collecting all SSE events.""" + stream = await self._client.aio.interactions.get( + req.id, stream=True, **req.params + ) + assert not isinstance(stream, Interaction) + return await _drain_interaction_stream(stream) + + @activity.defn + async def gemini_interactions_delete( + req: _GeminiInteractionIdRequest, + ) -> Any: + """Delete an interaction using the real genai.Client on the worker.""" + return await self._client.aio.interactions.delete(req.id, **req.params) + + @activity.defn + async def gemini_interactions_cancel( + req: _GeminiInteractionIdRequest, + ) -> dict[str, Any]: + """Cancel an interaction using the real genai.Client on the worker.""" + interaction = await self._client.aio.interactions.cancel( + req.id, **req.params + ) + return interaction.model_dump(by_alias=True, exclude_none=True, mode="json") + + @activity.defn + async def gemini_agents_create( + req: _GeminiInteractionRequest, + ) -> dict[str, Any]: + """Create a managed agent using the real genai.Client on the worker.""" + agent = await self._client.aio.agents.create(**req.params) + return agent.model_dump(by_alias=True, exclude_none=True, mode="json") + + @activity.defn + async def gemini_agents_list( + req: _GeminiInteractionRequest, + ) -> dict[str, Any]: + """List managed agents using the real genai.Client on the worker.""" + response = await self._client.aio.agents.list(**req.params) + return response.model_dump(by_alias=True, exclude_none=True, mode="json") + + @activity.defn + async def gemini_agents_get( + req: _GeminiInteractionIdRequest, + ) -> dict[str, Any]: + """Get a managed agent using the real genai.Client on the worker.""" + agent = await self._client.aio.agents.get(req.id, **req.params) + return agent.model_dump(by_alias=True, exclude_none=True, mode="json") + + @activity.defn + async def gemini_agents_delete( + req: _GeminiInteractionIdRequest, + ) -> dict[str, Any]: + """Delete a managed agent using the real genai.Client on the worker.""" + response = await self._client.aio.agents.delete(req.id, **req.params) + return response.model_dump(by_alias=True, exclude_none=True, mode="json") + + return [ + gemini_api_client_async_request, + gemini_api_client_async_request_streamed, + gemini_files_upload, + gemini_files_download, + gemini_files_register, + gemini_file_search_stores_upload, + gemini_interactions_create, + gemini_interactions_create_streamed, + gemini_interactions_get, + gemini_interactions_get_streamed, + gemini_interactions_delete, + gemini_interactions_cancel, + gemini_agents_create, + gemini_agents_list, + gemini_agents_get, + gemini_agents_delete, + ] diff --git a/temporalio/contrib/google_genai/_google_genai_plugin.py b/temporalio/contrib/google_genai/_google_genai_plugin.py new file mode 100644 index 000000000..f7b65b769 --- /dev/null +++ b/temporalio/contrib/google_genai/_google_genai_plugin.py @@ -0,0 +1,158 @@ +"""Temporal plugin for Google Gemini SDK integration.""" + +from __future__ import annotations + +import dataclasses +from datetime import timedelta +from typing import TYPE_CHECKING + +import google.auth.credentials +from google.genai import Client as GeminiClient + +from temporalio.contrib.google_genai._errors import GoogleGenAIError +from temporalio.contrib.google_genai._gemini_activity import GeminiApiCaller +from temporalio.contrib.pydantic import PydanticPayloadConverter +from temporalio.converter import DataConverter, DefaultPayloadConverter +from temporalio.plugin import SimplePlugin +from temporalio.worker import WorkflowRunner +from temporalio.worker.workflow_sandbox import SandboxedWorkflowRunner + +if TYPE_CHECKING: + from temporalio.contrib.google_genai._mcp import McpSessionFactory + + +_RETRY_OPTIONS_MESSAGE = ( + "genai.Client is configured with http_options.retry_options, but Temporal " + "owns retries for durable execution. Remove retry_options from the client " + "and configure retries with the activity retry_policy instead — e.g. " + "TemporalAsyncClient(activity_config=ActivityConfig(retry_policy=...)) or " + "activity_as_tool(fn, activity_config=ActivityConfig(retry_policy=...))." +) + + +def _reject_sdk_retries(client: GeminiClient) -> None: + """Raise if the client enables the SDK's own retry loop. + + Temporal must own retries so each attempt is a separate, observable activity + attempt; an SDK-internal retry loop would hide retries inside one activity + and compound with Temporal's retry policy. + """ + http_options = getattr(client._api_client, "_http_options", None) + if http_options is not None and getattr(http_options, "retry_options", None): + raise ValueError(_RETRY_OPTIONS_MESSAGE) + + +def _data_converter(converter: DataConverter | None) -> DataConverter: + if converter is None: + return DataConverter(payload_converter_class=PydanticPayloadConverter) + elif converter.payload_converter_class is DefaultPayloadConverter: + return dataclasses.replace( + converter, payload_converter_class=PydanticPayloadConverter + ) + return converter + + +class GoogleGenAIPlugin(SimplePlugin): + """A Temporal Worker Plugin configured for the Google Gemini SDK. + + .. warning:: + This class is experimental and may change in future versions. + Use with caution in production environments. + + This plugin registers the ``gemini_api_client_async_request`` activity + using the provided ``genai.Client`` with real credentials. Workflows + construct a :class:`temporalio.contrib.google_genai.TemporalAsyncClient` + to get an ``AsyncClient`` backed by a ``TemporalApiClient`` that routes all + API calls through this activity. + + No credentials are passed to or from the workflow. Auth material never + appears in Temporal's event history. + + Example (Gemini Developer API):: + + client = genai.Client(api_key=os.environ["GOOGLE_API_KEY"]) + plugin = GoogleGenAIPlugin(client) + + Example (Vertex AI):: + + client = genai.Client( + vertexai=True, project="my-project", location="us-central1", + ) + plugin = GoogleGenAIPlugin(client) + + Example (with separate GCS credentials for file registration):: + + client = genai.Client(api_key=os.environ["GOOGLE_API_KEY"]) + gcs_creds, _ = google.auth.default() + plugin = GoogleGenAIPlugin(client, extra_credentials=gcs_creds) + """ + + def __init__( + self, + client: GeminiClient, + extra_credentials: google.auth.credentials.Credentials | None = None, + mcp_servers: dict[str, McpSessionFactory] | None = None, + mcp_connection_idle_timeout: timedelta | None = None, + ) -> None: + """Initialize the Gemini plugin. + + Args: + client: A fully configured ``genai.Client`` instance. + All credential management, HTTP client configuration, etc. + is the responsibility of the caller. + extra_credentials: Optional Google Cloud credentials used for + operations that require explicit auth (e.g. + ``files.register_files()``). If not provided, the + client's own credentials are used. + mcp_servers: MCP servers to expose to workflows, keyed by name. + Each value is a factory returning an async context manager that + yields a connected, initialized ``mcp.ClientSession``. A + workflow references a server by name with + ``TemporalMcpClientSession(name)`` in a ``generate_content`` + ``tools`` list; ``list_tools`` / ``call_tool`` then run as the + ``{name}-list-tools`` / ``{name}-call-tool`` activities against a + worker-side connection. Requires the ``mcp`` package. + mcp_connection_idle_timeout: How long a worker-process MCP + connection stays open while idle before being disconnected + (the timer resets on each reuse). Defaults to 5 minutes. + """ + _reject_sdk_retries(client) + self._api_caller = GeminiApiCaller(client, credentials=extra_credentials) + + activities = list(self._api_caller.activities()) + if mcp_servers: + # Imported lazily: ``mcp`` is an optional dependency, only needed + # when MCP servers are registered. + from temporalio.contrib.google_genai._mcp import build_mcp_activities + + activities.extend( + build_mcp_activities(mcp_servers, mcp_connection_idle_timeout) + ) + + def workflow_runner(runner: WorkflowRunner | None) -> WorkflowRunner: + if not runner: + raise ValueError("No WorkflowRunner provided to GoogleGenAIPlugin.") + if isinstance(runner, SandboxedWorkflowRunner): + return dataclasses.replace( + runner, + restrictions=runner.restrictions.with_passthrough_modules( + # The SDK's request formatting + AFC loop run in-workflow + # and validate google.genai's Pydantic models; mcp is + # imported to subclass ClientSession. pydantic itself is + # in the SDK default passthrough, but its compiled core + # and Annotated helper are not, so extend them. + "google.genai", + "mcp", + "pydantic_core", + "annotated_types", + ), + ) + return runner + + super().__init__( + name="google_genai.GoogleGenAIPlugin", + data_converter=_data_converter, + activities=activities, + workflow_runner=workflow_runner, + workflow_failure_exception_types=[GoogleGenAIError], + ) diff --git a/temporalio/contrib/google_genai/_mcp.py b/temporalio/contrib/google_genai/_mcp.py new file mode 100644 index 000000000..157ce458c --- /dev/null +++ b/temporalio/contrib/google_genai/_mcp.py @@ -0,0 +1,226 @@ +"""Worker-side MCP activities and a pooled-connection subsystem. + +The Gemini SDK's automatic-function-calling loop runs *inside* the workflow, +where it would otherwise call ``McpClientSession.list_tools`` / +``call_tool`` directly (network I/O — forbidden in a workflow). The +workflow-side ``TemporalMcpClientSession`` shim redirects those two methods to +the ``{server}-list-tools`` / ``{server}-call-tool`` activities defined here, +so the real ``mcp.ClientSession`` lives only on the worker. + +A single live session per server is held open in the worker process and reused +across activity invocations, with idle eviction — modeled on the strands +plugin's ``_temporal_mcp_client``. The MCP transport and ``ClientSession`` are +anyio context managers whose cancel scope is bound to the task that enters +them, so a dedicated owner task (``_ConnectionRecord._run``) holds them open for +the connection's lifetime while concurrent activities on the same event loop +call through the shared session. +""" + +from __future__ import annotations + +import asyncio +from collections.abc import Callable +from contextlib import AbstractAsyncContextManager +from datetime import timedelta + +from mcp import ClientSession +from mcp.types import CallToolResult, ListToolsResult + +from temporalio import activity +from temporalio.contrib.google_genai._models import _McpCallToolRequest + +# A factory yields a ready-to-use (connected and ``initialize()``-d) +# ``ClientSession`` as an async context manager. Mirrors the strands +# ``mcp_clients={name: factory}`` shape; the user writes a small +# ``@asynccontextmanager`` that enters the transport, opens the session, and +# initializes it before ``yield``. +McpSessionFactory = Callable[[], AbstractAsyncContextManager[ClientSession]] + +# Default time an idle MCP connection stays open before being disconnected. +# The timer resets on every call that reuses the connection. Override per +# worker via ``GoogleGenAIPlugin(mcp_connection_idle_timeout=...)``. +_MCP_CONNECTION_IDLE = timedelta(minutes=5) + +# Server name -> live connection held open in the activity worker process. +# Activities run in the worker process, so this module state is shared across +# activity invocations on the worker. +_CONNECTIONS: dict[str, _ConnectionRecord] = {} + + +class _ConnectionRecord: + """A single MCP session held open by a dedicated owner task. + + ``_run`` enters and exits the session's context manager in the same task + for the connection's whole lifetime; ``list_tools`` / ``call_tool`` + activities on the same event loop call through the shared session (MCP + multiplexes concurrent requests by id). + """ + + def __init__( + self, + server: str, + factory: McpSessionFactory, + idle_timeout: timedelta, + ) -> None: + loop = asyncio.get_running_loop() + self._server = server + self._idle_timeout = idle_timeout + self._stop = asyncio.Event() + self._ready: asyncio.Future[ClientSession] = loop.create_future() + self._idle_handle: asyncio.TimerHandle | None = None + self._inflight = 0 + self._owner = asyncio.create_task(self._run(factory)) + + async def _run(self, factory: McpSessionFactory) -> None: + try: + async with factory() as session: + self._ready.set_result(session) + await self._stop.wait() + except BaseException as err: + # A failed connect should not be cached; drop it so the next call + # retries instead of awaiting a permanently rejected future. + if not self._ready.done(): + self._ready.set_exception(err) + _CONNECTIONS.pop(self._server, None) + raise + + def acquire(self) -> None: + """Mark a call in flight; pause idle eviction while calls are active.""" + self._inflight += 1 + if self._idle_handle is not None: + self._idle_handle.cancel() + self._idle_handle = None + + def release(self) -> None: + """Mark a call done; arm idle eviction once no calls remain in flight.""" + self._inflight -= 1 + # Only the record still cached under this server arms a timer; a record + # already evicted or never cached must not schedule one, or it could + # later evict a different, healthy connection for the same server. + if self._inflight == 0 and _CONNECTIONS.get(self._server) is self: + loop = asyncio.get_running_loop() + self._idle_handle = loop.call_later( + self._idle_timeout.total_seconds(), self._on_idle + ) + + def _on_idle(self) -> None: + asyncio.ensure_future(self._maybe_evict()) + + async def _maybe_evict(self) -> None: + # A call may have acquired the connection between the timer firing and + # this task running; only evict if it is still idle. + if self._inflight == 0: + await _evict_connection(self._server) + + async def aclose(self) -> None: + """Signal the owner task to exit its context manager and wait for it.""" + if self._idle_handle is not None: + self._idle_handle.cancel() + self._idle_handle = None + self._stop.set() + try: + await self._owner + except BaseException: + pass + + async def session(self) -> ClientSession: + """Return the live session, or raise the connect failure.""" + return await self._ready + + +async def get_connection( + server: str, factory: McpSessionFactory, idle_timeout: timedelta +) -> tuple[ClientSession, _ConnectionRecord]: + """Return the cached session for ``server``, opening one lazily if needed. + + Concurrent first-callers dedupe onto a single connect handshake by awaiting + the same record. The returned record is acquired; the caller must + ``release()`` it once the call completes so idle eviction can resume. + """ + record = _CONNECTIONS.get(server) + if record is None: + record = _ConnectionRecord(server, factory, idle_timeout) + _CONNECTIONS[server] = record + record.acquire() + try: + session = await record.session() + except BaseException: + record.release() + raise + return session, record + + +async def _evict_connection(server: str) -> None: + record = _CONNECTIONS.pop(server, None) + if record is not None: + await record.aclose() + + +def build_list_tools_activity( + server: str, + factory: McpSessionFactory, + idle_timeout: timedelta | None = None, +) -> Callable: + """Return the per-server ``{server}-list-tools`` activity for registration. + + Reuses a lazily-opened, idle-evicted worker-process MCP session. Returns + the raw ``mcp.types.ListToolsResult`` so the workflow-side shim can hand it + to the Gemini SDK exactly as a live session would (preserving the full tool + parameter schema). + """ + idle = idle_timeout if idle_timeout is not None else _MCP_CONNECTION_IDLE + + @activity.defn(name=f"{server}-list-tools") + async def list_tools() -> ListToolsResult: + session, record = await get_connection(server, factory, idle) + try: + return await session.list_tools() + except Exception: + # The session may be broken; drop it so the next call reconnects. + await _evict_connection(server) + raise + finally: + record.release() + + return list_tools + + +def build_call_tool_activity( + server: str, + factory: McpSessionFactory, + idle_timeout: timedelta | None = None, +) -> Callable: + """Return the per-server ``{server}-call-tool`` activity for registration. + + Reuses the same lazily-opened, idle-evicted worker-process MCP session as + ``{server}-list-tools``. Returns the raw ``mcp.types.CallToolResult`` — + including tool-level error results (``isError=True``), which the model is + meant to see; only transport/protocol failures raise (and evict). + """ + idle = idle_timeout if idle_timeout is not None else _MCP_CONNECTION_IDLE + + @activity.defn(name=f"{server}-call-tool") + async def call_tool(req: _McpCallToolRequest) -> CallToolResult: + session, record = await get_connection(server, factory, idle) + try: + return await session.call_tool(name=req.name, arguments=req.arguments) + except Exception: + # The session may be broken; drop it so the next call reconnects. + await _evict_connection(server) + raise + finally: + record.release() + + return call_tool + + +def build_mcp_activities( + mcp_servers: dict[str, McpSessionFactory], + idle_timeout: timedelta | None = None, +) -> list[Callable]: + """Build the list-tools and call-tool activities for every registered server.""" + activities: list[Callable] = [] + for server, factory in mcp_servers.items(): + activities.append(build_list_tools_activity(server, factory, idle_timeout)) + activities.append(build_call_tool_activity(server, factory, idle_timeout)) + return activities diff --git a/temporalio/contrib/google_genai/_models.py b/temporalio/contrib/google_genai/_models.py new file mode 100644 index 000000000..2f70d9b4d --- /dev/null +++ b/temporalio/contrib/google_genai/_models.py @@ -0,0 +1,172 @@ +"""Serializable Pydantic models for the Gemini SDK Temporal integration. + +These models cross the activity boundary — they're constructed on the +workflow side and deserialized on the activity side (or vice versa). +""" + +from __future__ import annotations + +from typing import Any + +from google.genai import types +from pydantic import BaseModel + +__all__ = [ + "_GeminiApiRequest", + "_GeminiApiResponse", + "_GeminiApiStreamedResponse", + "_GeminiDownloadFileRequest", + "_GeminiInteractionIdRequest", + "_GeminiInteractionRequest", + "_GeminiInteractionStreamedResponse", + "_GeminiRegisterFilesRequest", + "_GeminiUploadFileRequest", + "_GeminiUploadToFileSearchStoreRequest", + "_McpCallToolRequest", + "_SerializableHttpOptions", +] + + +class _SerializableHttpOptions(BaseModel): + """Per-request HTTP options that can be serialized across the activity boundary. + + Non-serializable fields (httpx_client, httpx_async_client, aiohttp_client, + client_args, async_client_args) must be configured at GoogleGenAIPlugin init. + + ``timeout`` is excluded because Temporal owns timeouts/retries — configure + via ``ActivityConfig`` instead. + """ + + base_url: str | None = None + base_url_resource_scope: str | None = None + api_version: str | None = None + headers: dict[str, str] | None = None + extra_body: dict[str, Any] | None = None + + +# ── async_request models ────────────────────────────────────────────────── + + +class _GeminiApiRequest(BaseModel): + """Serializable activity input for a Gemini SDK API call. + + ``streaming_topic`` / ``streaming_batch_interval_ms`` are only read by the + streamed activity: when a topic is set, each streamed chunk is published to + that workflow-stream topic as it arrives (in addition to being returned + batched), so external consumers can observe the model output in real time. + """ + + http_method: str + path: str + request_dict: dict[str, object] + http_options_overrides: _SerializableHttpOptions | None = None + streaming_topic: str | None = None + streaming_batch_interval_ms: int = 100 + + +class _GeminiApiResponse(BaseModel): + """Serializable activity output for a Gemini SDK API call.""" + + headers: dict[str, str] + body: str + + +class _GeminiApiStreamedResponse(BaseModel): + """Serializable activity output for a batched streamed API call. + + The activity collects all streamed chunks and returns them as a list. + The ``TemporalApiClient`` then yields them one at a time to the SDK. + """ + + chunks: list[_GeminiApiResponse] + + +# ── files upload/download models ────────────────────────────────────────── + + +class _GeminiUploadFileRequest(BaseModel): + """Serializable activity input for a file upload. + + For file path uploads the path is resolved on the worker. For + in-memory uploads the raw bytes are sent across the activity boundary. + """ + + file_bytes: bytes | None = None + file_path: str | None = None + config: types.UploadFileConfig | None = None + + +class _GeminiDownloadFileRequest(BaseModel): + """Serializable activity input for a file download.""" + + file: str + config: types.DownloadFileConfig | None = None + + +class _GeminiRegisterFilesRequest(BaseModel): + """Serializable activity input for registering GCS files.""" + + uris: list[str] + config: types.RegisterFilesConfig | None = None + + +class _GeminiUploadToFileSearchStoreRequest(BaseModel): + """Serializable activity input for uploading a file to a file search store.""" + + file_search_store_name: str + file_bytes: bytes | None = None + file_path: str | None = None + config: types.UploadToFileSearchStoreConfig | None = None + + +# ── interactions / agents models ────────────────────────────────────────── + + +class _GeminiInteractionRequest(BaseModel): + """Serializable activity input for interactions/agents calls without an id. + + ``params`` is the caller's kwargs forwarded verbatim to the real SDK + method on the worker — ``stream`` and ``timeout`` are popped by the + workflow-side shim before dispatch (``stream`` selects the activity, + ``timeout`` maps to the activity's ``start_to_close_timeout``). + """ + + params: dict[str, Any] = {} + + +class _GeminiInteractionIdRequest(BaseModel): + """Serializable activity input for id-addressed interactions/agents calls.""" + + id: str + params: dict[str, Any] = {} + + +class _GeminiInteractionStreamedResponse(BaseModel): + """Serializable activity output for a batched streamed interaction call. + + ``events`` is the verbatim sequence of ``InteractionSSEEvent`` objects + yielded by the SDK's stream, each serialized via + ``model_dump(exclude_none=True, mode="json")``. The workflow-side shim + rehydrates each entry with ``_temporal_interactions._deserialize`` so + workflow code iterates the same typed events it would get from the SDK + directly. + """ + + events: list[dict[str, Any]] = [] + + +# ── MCP models ───────────────────────────────────────────────────────────── + + +class _McpCallToolRequest(BaseModel): + """Serializable activity input for an MCP ``call_tool`` invocation. + + Carries the tool name and arguments the Gemini SDK's AFC loop selected; + the worker-side activity forwards them to the real ``mcp.ClientSession``. + The ``mcp.types.ListToolsResult`` / ``CallToolResult`` returned by the + activities are themselves Pydantic models, so they serialize directly via + the plugin's ``PydanticPayloadConverter`` and need no wrapper here. + """ + + name: str + arguments: dict[str, Any] = {} diff --git a/temporalio/contrib/google_genai/_temporal_agents.py b/temporalio/contrib/google_genai/_temporal_agents.py new file mode 100644 index 000000000..2aaac8f78 --- /dev/null +++ b/temporalio/contrib/google_genai/_temporal_agents.py @@ -0,0 +1,143 @@ +"""Temporal-aware agents resource shim. + +``TemporalAsyncAgents`` exposes the same surface as google-genai's +``AsyncClient.agents`` resource, but each operation is dispatched through a +Temporal activity holding the real ``genai.Client`` on the worker. Agents are +server-side managed agent definitions (created once, then referenced by id in +``interactions.create(agent=...)``); like the Interactions API, the resource +lives in the vendored Stainless client that bypasses ``BaseApiClient``, so each +operation is routed as a whole through an activity instead. + +The shim depends only on the public ``google.genai.interactions`` surface, not +on google-genai internals. +""" + +from __future__ import annotations + +from datetime import timedelta +from typing import Any, cast + +# These types are runtime-public (in ``google.genai.interactions.__all__``) but +# pyright's stubs don't mark them re-exported; the alternative it suggests is a +# private ``_gaos`` path, so suppress the false positive. +from google.genai.interactions import ( + Agent, # pyright: ignore[reportPrivateImportUsage] + AgentDeleteResponse, # pyright: ignore[reportPrivateImportUsage] + AgentListResponse, # pyright: ignore[reportPrivateImportUsage] +) + +from temporalio import workflow as temporal_workflow +from temporalio.contrib.google_genai._models import ( + _GeminiInteractionIdRequest, + _GeminiInteractionRequest, +) +from temporalio.contrib.google_genai._temporal_interactions import ( + _deserialize, + _pop_timeout, +) +from temporalio.workflow import ActivityConfig + + +class TemporalAsyncAgents: + """Agents resource shim that routes calls through activities. + + Methods accept the same keyword arguments as the real resource and + forward them verbatim — the SDK validates them on the worker side, so + a bad argument surfaces as an activity failure (retried per the + activity's retry policy) rather than a workflow-side error. + + ``with_raw_response`` / ``with_streaming_response`` are not supported + in workflows. + """ + + def __init__( + self, + activity_config: ActivityConfig | None = None, + ) -> None: + """Initialize with activity config for agent operation timeouts.""" + self._activity_config = ( + ActivityConfig(start_to_close_timeout=timedelta(seconds=60)) + if activity_config is None + else activity_config + ) + + def _config(self, summary: str, params: dict[str, Any]) -> ActivityConfig: + config: ActivityConfig = {**self._activity_config} + if "summary" not in config: + config["summary"] = summary + _pop_timeout(params, config) + return config + + async def create( + self, + **kwargs: Any, + ) -> Agent: + """Create a managed agent definition via a Temporal activity.""" + params = dict(kwargs) + config = self._config("agents.create", params) + raw = await temporal_workflow.execute_activity( + "gemini_agents_create", + _GeminiInteractionRequest(params=params), + result_type=dict[str, Any], + **config, + ) + return cast(Agent, _deserialize(raw, Agent)) + + async def list( + self, + **kwargs: Any, + ) -> AgentListResponse: + """List managed agent definitions via a Temporal activity.""" + params = dict(kwargs) + config = self._config("agents.list", params) + raw = await temporal_workflow.execute_activity( + "gemini_agents_list", + _GeminiInteractionRequest(params=params), + result_type=dict[str, Any], + **config, + ) + return cast(AgentListResponse, _deserialize(raw, AgentListResponse)) + + async def get( + self, + id: str, + **kwargs: Any, + ) -> Agent: + """Get a managed agent definition via a Temporal activity.""" + params = dict(kwargs) + config = self._config("agents.get", params) + raw = await temporal_workflow.execute_activity( + "gemini_agents_get", + _GeminiInteractionIdRequest(id=id, params=params), + result_type=dict[str, Any], + **config, + ) + return cast(Agent, _deserialize(raw, Agent)) + + async def delete( + self, + id: str, + **kwargs: Any, + ) -> AgentDeleteResponse: + """Delete a managed agent definition via a Temporal activity.""" + params = dict(kwargs) + config = self._config("agents.delete", params) + raw = await temporal_workflow.execute_activity( + "gemini_agents_delete", + _GeminiInteractionIdRequest(id=id, params=params), + result_type=dict[str, Any], + **config, + ) + return cast(AgentDeleteResponse, _deserialize(raw, AgentDeleteResponse)) + + @property + def with_raw_response(self) -> Any: + """Raise — raw responses are not available in workflows.""" + raise RuntimeError("with_raw_response is not supported in Temporal workflows.") + + @property + def with_streaming_response(self) -> Any: + """Raise — streaming responses are not available in workflows.""" + raise RuntimeError( + "with_streaming_response is not supported in Temporal workflows." + ) diff --git a/temporalio/contrib/google_genai/_temporal_api_client.py b/temporalio/contrib/google_genai/_temporal_api_client.py new file mode 100644 index 000000000..0763d6890 --- /dev/null +++ b/temporalio/contrib/google_genai/_temporal_api_client.py @@ -0,0 +1,319 @@ +"""Temporal-aware BaseApiClient that routes SDK calls through activities. + +This module provides ``_TemporalApiClient``, a ``BaseApiClient`` subclass +whose HTTP methods dispatch through Temporal activities instead of making +direct calls. The real ``genai.Client`` with real credentials only exists +on the worker side inside the activity. + +This ensures: + +- No credential fetching or refreshing happens in the workflow. +- No auth material (tokens, API keys) appears in Temporal event history. +- The SDK's AFC (automatic function calling) loop runs in the workflow, + so ``activity_as_tool()`` wrappers work naturally. +""" + +from __future__ import annotations + +from datetime import timedelta +from typing import Any + +from google.genai._api_client import BaseApiClient +from google.genai.types import HttpOptions, HttpOptionsOrDict +from google.genai.types import HttpResponse as SdkHttpResponse + +from temporalio import workflow as temporal_workflow +from temporalio.contrib.google_genai._errors import GoogleGenAIError +from temporalio.contrib.google_genai._models import ( + _GeminiApiRequest, + _GeminiApiResponse, + _GeminiApiStreamedResponse, + _SerializableHttpOptions, +) +from temporalio.contrib.workflow_streams._stream import _PUBLISH_SIGNAL +from temporalio.workflow import ActivityConfig + +# Fields on HttpOptions that cannot be serialized or should not be forwarded. +_REJECTED_HTTP_OPTION_FIELDS = frozenset( + { + "httpx_client", + "httpx_async_client", + "aiohttp_client", + "client_args", + "async_client_args", + } +) + + +def _validate_http_options(http_options: HttpOptions | None) -> None: + """Raise if http_options contains non-serializable fields.""" + if http_options is None: + return + bad_fields = [ + f + for f in _REJECTED_HTTP_OPTION_FIELDS + if getattr(http_options, f, None) is not None + ] + if bad_fields: + raise ValueError( + f"http_options cannot include {bad_fields}. " + f"Configure custom HTTP clients at GoogleGenAIPlugin init instead." + ) + + +class _TemporalApiClient(BaseApiClient): # pyright: ignore[reportUnusedClass] + """A ``BaseApiClient`` that routes all API calls through Temporal activities. + + This client is used on the workflow side. It does NOT initialize HTTP + clients, load credentials, or make any network calls. It only holds the + minimal configuration needed for the SDK's request formatting logic + (e.g., choosing between Vertex AI and ML Dev parameter transformations). + + All actual HTTP calls are dispatched via ``workflow.execute_activity``. + """ + + def __init__( # pyright: ignore[reportMissingSuperCall] + self, + *, + vertexai: bool = False, + project: str | None = None, + location: str | None = None, + activity_config: ActivityConfig | None = None, + streaming_topic: str | None = None, + streaming_batch_interval: timedelta = timedelta(milliseconds=100), + ) -> None: + """Initialize without calling super (no HTTP clients needed).""" + # Do NOT call super().__init__() — it creates HTTP clients, loads + # credentials, etc. We only set the properties the SDK's request + # formatting code accesses. + self.vertexai = vertexai + self.project = project + self.location = location + self.api_key: str | None = None + self.custom_base_url: str | None = None + + self._activity_config = ( + ActivityConfig(start_to_close_timeout=timedelta(seconds=60)) + if activity_config is None + else activity_config + ) + self._streaming_topic = streaming_topic + self._streaming_batch_interval = streaming_batch_interval + + def _verify_response(self, response_model: Any) -> None: + """No-op — matches the base implementation.""" + pass + + def close(self) -> None: + """No-op — no HTTP resources to close.""" + pass + + async def aclose(self) -> None: + """No-op — no HTTP resources to close.""" + pass + + def __del__(self) -> None: + """No-op — no HTTP resources to clean up.""" + pass + + @staticmethod + def _process_http_options( + http_options: HttpOptionsOrDict | None, + config: ActivityConfig, + ) -> _SerializableHttpOptions | None: + """Validate and extract serializable per-request HTTP options. + + Rejects non-serializable fields (custom HTTP clients), maps timeout + to the Temporal activity config, and returns the remaining options + for forwarding to the activity. + + Args: + http_options: Per-request options from the SDK call. + config: Mutable activity config dict — timeout is applied here. + + Returns: + Serializable options to forward, or None if nothing to forward. + """ + if http_options is None: + return None + + if isinstance(http_options, HttpOptions): + opts = http_options + else: + opts = HttpOptions.model_validate(http_options) + + _validate_http_options(opts) + + if opts.retry_options is not None: + raise GoogleGenAIError( + "Per-request http_options.retry_options is not supported in " + "Temporal workflows. Temporal owns retries; configure them with " + "the activity retry_policy via activity_config instead." + ) + + # timeout is owned by Temporal — apply it to the activity config + # rather than forwarding to the underlying HTTP client. + if opts.timeout is not None: + config["start_to_close_timeout"] = timedelta(milliseconds=opts.timeout) + + result = _SerializableHttpOptions( + base_url=opts.base_url, + base_url_resource_scope=( + opts.base_url_resource_scope.value + if opts.base_url_resource_scope + else None + ), + api_version=opts.api_version, + headers=opts.headers, + extra_body=opts.extra_body, + ) + # Only return if there are actual values set + if not result.model_dump(exclude_none=True): + return None + return result + + # ── Async (primary path for workflows) ────────────────────────────── + + async def async_request( + self, + http_method: str, + path: str, + request_dict: dict[str, object], + http_options: HttpOptionsOrDict | None = None, + ) -> SdkHttpResponse: + """Dispatch an async API request through a Temporal activity.""" + config: ActivityConfig = {**self._activity_config} + if "summary" not in config: + # Default summary is the API path (e.g. "models/gemini-2.5-flash:generateContent"). + config["summary"] = f"{http_method.upper()} {path}" + overrides = self._process_http_options(http_options, config) + + resp = await temporal_workflow.execute_activity( + "gemini_api_client_async_request", + _GeminiApiRequest( + http_method=http_method, + path=path, + request_dict=request_dict, + http_options_overrides=overrides, + ), + result_type=_GeminiApiResponse, + **config, + ) + return SdkHttpResponse(headers=resp.headers, body=resp.body) + + # ── Sync (not expected in async workflows, but raise clearly) ─────── + + def request( + self, + http_method: str, + path: str, + request_dict: dict[str, object], + http_options: HttpOptionsOrDict | None = None, + ) -> SdkHttpResponse: + """Raise — sync requests not supported in workflows.""" + raise RuntimeError( + "Synchronous requests are not supported in Temporal workflows. " + "Use TemporalAsyncClient instead." + ) + + def request_streamed( + self, + http_method: str, + path: str, + request_dict: dict[str, object], + http_options: HttpOptionsOrDict | None = None, + ) -> Any: + """Raise — sync streaming not supported in workflows.""" + raise RuntimeError( + "Synchronous streaming is not supported in Temporal workflows. " + "Use TemporalAsyncClient instead." + ) + + async def async_request_streamed( + self, + http_method: str, + path: str, + request_dict: dict[str, object], + http_options: HttpOptionsOrDict | None = None, + ) -> Any: + """Dispatch a streamed request, batching chunks in the activity. + + When a ``streaming_topic`` is configured, the activity also publishes + each chunk to that workflow-stream topic as it arrives; the workflow + must host a ``WorkflowStream`` to receive them. + """ + config: ActivityConfig = {**self._activity_config} + if "summary" not in config: + config["summary"] = f"{http_method.upper()} {path}" + overrides = self._process_http_options(http_options, config) + + if self._streaming_topic is not None: + self._require_workflow_stream() + + resp = await temporal_workflow.execute_activity( + "gemini_api_client_async_request_streamed", + _GeminiApiRequest( + http_method=http_method, + path=path, + request_dict=request_dict, + http_options_overrides=overrides, + streaming_topic=self._streaming_topic, + streaming_batch_interval_ms=int( + self._streaming_batch_interval.total_seconds() * 1000 + ), + ), + result_type=_GeminiApiStreamedResponse, + **config, + ) + + async def _yield_chunks(): + for chunk in resp.chunks: + yield SdkHttpResponse(headers=chunk.headers, body=chunk.body) + + return _yield_chunks() + + def _require_workflow_stream(self) -> None: + """Fail fast if streaming is configured but no WorkflowStream is hosted. + + Published chunks are delivered to the workflow's ``WorkflowStream`` via + a signal; without a registered handler the signals would be silently + dropped, so surface a clear error instead. + """ + if temporal_workflow.get_signal_handler(_PUBLISH_SIGNAL) is None: + raise GoogleGenAIError( + "streaming_topic is set but this workflow has no WorkflowStream. " + "Construct WorkflowStream() in the workflow's @workflow.init " + "(from temporalio.contrib.workflow_streams).", + non_retryable=True, + ) + + # ── File upload/download ───────────────────────────────────────────── + # File operations are handled at a higher level by TemporalAsyncFiles + # (in _temporal_files.py), which dispatches the entire upload/download + # as a Temporal activity using the real client on the worker side. + # These internal BaseApiClient methods are not called in that path, + # so we raise here to catch any unexpected direct usage. + + def upload_file(self, *args: Any, **kwargs: Any) -> Any: + """Raise — use client.files.upload() instead.""" + raise NotImplementedError( + "Use client.files.upload() instead of the internal upload_file() method." + ) + + async def async_upload_file(self, *args: Any, **kwargs: Any) -> Any: + """Raise — use client.files.upload() instead.""" + raise NotImplementedError( + "Use client.files.upload() instead of the internal async_upload_file() method." + ) + + def download_file(self, *args: Any, **kwargs: Any) -> Any: + """Raise — use client.files.download() instead.""" + raise NotImplementedError( + "Use client.files.download() instead of the internal download_file() method." + ) + + async def async_download_file(self, *args: Any, **kwargs: Any) -> Any: + """Raise — use client.files.download() instead.""" + raise NotImplementedError( + "Use client.files.download() instead of the internal async_download_file() method." + ) diff --git a/temporalio/contrib/google_genai/_temporal_async_client.py b/temporalio/contrib/google_genai/_temporal_async_client.py new file mode 100644 index 000000000..c77cd5cee --- /dev/null +++ b/temporalio/contrib/google_genai/_temporal_async_client.py @@ -0,0 +1,285 @@ +"""Temporal-aware ``AsyncClient``. + +``TemporalAsyncClient`` is an ``AsyncClient`` whose every Gemini API call runs +as a Temporal activity. It builds and wraps a private ``BaseApiClient`` that +dispatches HTTP through ``workflow.execute_activity`` instead of making network +calls, so the SDK's request-formatting code (including the AFC loop) runs in +the workflow while the real ``genai.Client`` with real credentials only exists +on the worker side inside the activity. + +Construct it from within a workflow:: + + client = TemporalAsyncClient(activity_config=...) + response = await client.models.generate_content(...) + +This ensures: + +- No credential fetching or refreshing happens in the workflow. +- No auth material (tokens, API keys) appears in Temporal event history. +- The SDK's AFC (automatic function calling) loop runs in the workflow, so + ``activity_as_tool()`` wrappers work naturally. + +``AsyncFiles`` and ``AsyncFileSearchStores`` are replaced with shims that run +upload/download as activities; ``interactions`` and ``agents`` — which bypass +``BaseApiClient`` via a vendored HTTP client — are likewise replaced with +activity-backed shims; ``webhooks`` is not supported in workflows and raises. + +Replay determinism +------------------ +The SDK's request-formatting and automatic-function-calling loop run *in the +workflow*, so they must be deterministic. A survey of ``google.genai`` found no +``time``/``uuid``/``random`` use on the ``generate_content``/AFC path; the SDK's +own non-deterministic code (HTTP retry backoff, the interactions/agents vendored +client, local tokenizer temp paths) runs only inside activities on the worker. +The SDK exposes no time/id provider hooks to override, and none are needed. + +The one in-workflow exception is ``batches.create`` on Vertex AI: when +``display_name``/``dest`` are omitted the SDK auto-generates them from a +timestamp + UUID (``_common.timestamped_unique_name``), which is not +replay-safe. Pass explicit ``display_name`` and ``dest`` when creating Vertex +batch jobs from a workflow. +""" + +from __future__ import annotations + +import functools +import inspect +from collections.abc import AsyncIterator, Callable +from datetime import timedelta +from typing import Any, NoReturn, cast + +from google.genai import types +from google.genai.client import AsyncClient +from google.genai.models import AsyncModels + +from temporalio.contrib.google_genai._temporal_agents import ( + TemporalAsyncAgents, +) +from temporalio.contrib.google_genai._temporal_api_client import ( + _TemporalApiClient, +) +from temporalio.contrib.google_genai._temporal_file_search_stores import ( + TemporalAsyncFileSearchStores, +) +from temporalio.contrib.google_genai._temporal_files import ( + TemporalAsyncFiles, +) +from temporalio.contrib.google_genai._temporal_interactions import ( + TemporalAsyncInteractions, +) +from temporalio.workflow import ActivityConfig + + +def _closure_if_bound_method(tool: object) -> object: + """Return a plain-function wrapper for a bound method, else ``tool`` as-is. + + google-genai >= 2.8.0 deep-copies the request config internally + (``config.model_copy(deep=True)``). ``copy.deepcopy`` clones a bound + method's ``__self__``, so a workflow-method tool would run against a throwaway + clone of the workflow instance and its in-workflow state mutation would be + lost. ``deepcopy`` leaves plain functions untouched, so wrapping the method + in a closure keeps the tool bound to the real instance. Plain functions and + ``activity_as_tool`` wrappers are already closures and pass through unchanged. + """ + if not inspect.ismethod(tool): + return tool + method: Callable = tool + if inspect.iscoroutinefunction(method): + + @functools.wraps(method) + async def async_wrapper(*args: object, **kwargs: object) -> object: + return await method(*args, **kwargs) + + return async_wrapper + + @functools.wraps(method) + def sync_wrapper(*args: object, **kwargs: object) -> object: + return method(*args, **kwargs) + + return sync_wrapper + + +def _wrap_bound_method_tools( + config: types.GenerateContentConfigOrDict | None, +) -> types.GenerateContentConfigOrDict | None: + """Closure-wrap any bound-method tools in ``config`` (see :func:`_closure_if_bound_method`). + + Returns ``config`` unchanged when it holds no bound-method tools; otherwise + returns a shallow copy with the tools list rewritten, so the caller's config + is never mutated. + """ + if not config: + return config + if isinstance(config, dict): + tools = config.get("tools") + if not tools or not any(inspect.ismethod(t) for t in tools): + return config + updated: Any = { + **config, + "tools": [_closure_if_bound_method(t) for t in tools], + } + return cast(types.GenerateContentConfigDict, updated) + if not config.tools or not any(inspect.ismethod(t) for t in config.tools): + return config + return config.model_copy( + update={"tools": [_closure_if_bound_method(t) for t in config.tools]} + ) + + +class _TemporalAsyncModels(AsyncModels): + """``AsyncModels`` that closure-wraps bound-method tools before each call. + + This shields workflow-method tools from google-genai's internal deep-copy of + the config (>= 2.8.0), which would otherwise clone the workflow instance and + silently drop the tool's in-workflow state mutations. + """ + + async def generate_content( # type: ignore[override] + self, + *, + model: str, + contents: types.ContentListUnion | types.ContentListUnionDict, + config: types.GenerateContentConfigOrDict | None = None, + ) -> types.GenerateContentResponse: + return await super().generate_content( + model=model, + contents=contents, + config=_wrap_bound_method_tools(config), + ) + + async def generate_content_stream( # type: ignore[override] + self, + *, + model: str, + contents: types.ContentListUnion | types.ContentListUnionDict, + config: types.GenerateContentConfigOrDict | None = None, + ) -> AsyncIterator[types.GenerateContentResponse]: + return await super().generate_content_stream( + model=model, + contents=contents, + config=_wrap_bound_method_tools(config), + ) + + +class TemporalAsyncClient(AsyncClient): + """An ``AsyncClient`` whose API calls run as Temporal activities. + + .. warning:: + This API is experimental and may change in future versions. + Use with caution in production environments. + + Builds a private ``BaseApiClient`` that dispatches HTTP calls through + ``workflow.execute_activity`` and wraps it, so the SDK's request-formatting + code (including the AFC loop) runs in the workflow while only the actual + API calls cross into activities. Credentials are never fetched or stored + in the workflow — the activity worker handles authentication independently. + + ``AsyncFiles`` and ``AsyncFileSearchStores`` are replaced with shims that + run upload/download as activities; ``interactions`` and ``agents`` — which + bypass ``BaseApiClient`` via a vendored HTTP client — are likewise replaced + with activity-backed shims; ``webhooks`` is not supported in workflows and + raises. Other modules (models, tunings, caches, batches, live, tokens, + operations) are inherited unchanged and work through the private api + client's activity-backed HTTP methods. + + Construct it from within a workflow ``run`` method: + + .. code-block:: python + + @workflow.defn + class MyWorkflow: + @workflow.run + async def run(self, query: str) -> str: + client = TemporalAsyncClient() + response = await client.models.generate_content( + model="gemini-2.0-flash", + contents=query, + config=GenerateContentConfig( + tools=[ + activity_as_tool( + my_tool, + activity_config=ActivityConfig( + start_to_close_timeout=timedelta(seconds=30), + ), + ), + ], + ), + ) + return response.text + """ + + def __init__( + self, + *, + vertexai: bool = False, + project: str | None = None, + location: str | None = None, + activity_config: ActivityConfig | None = None, + streaming_topic: str | None = None, + streaming_batch_interval: timedelta = timedelta(milliseconds=100), + ) -> None: + """Initialize a Temporal-aware client. + + Args: + vertexai: Whether to use Vertex AI API endpoints. Must match the + ``GoogleGenAIPlugin`` configuration on the worker side. + Defaults to ``False`` (Gemini Developer API). + project: Google Cloud project ID. Only needed when + ``vertexai=True`` and the SDK's request formatting requires it + (e.g., cache operations). + location: Google Cloud location. Same conditions as ``project``. + activity_config: Override the default activity configuration + (timeouts, retry policy, etc.) for Gemini API call activities. + When not provided, every operation (model calls, files, + interactions, managed agents) defaults to a 60-second + ``start_to_close_timeout`` and Temporal's default retry policy. + streaming_topic: When set, ``generate_content_stream`` publishes each + streamed ``GenerateContentResponse`` to this + :class:`temporalio.contrib.workflow_streams.WorkflowStream` + topic as it arrives, so external consumers can observe the model + output in real time. The workflow must construct a + ``WorkflowStream`` in ``@workflow.init``; otherwise the call + raises. The workflow's own stream iteration is unchanged. + streaming_batch_interval: How often the streaming activity flushes + published chunks to the workflow stream. Defaults to 100ms. + """ + api_client = _TemporalApiClient( + vertexai=vertexai, + project=project, + location=location, + activity_config=activity_config, + streaming_topic=streaming_topic, + streaming_batch_interval=streaming_batch_interval, + ) + super().__init__(api_client) + # Closure-wrap bound-method tools so google-genai's internal + # config deep-copy (>= 2.8.0) can't clone the workflow instance. + self._models = _TemporalAsyncModels(api_client) + self._files = TemporalAsyncFiles(api_client, activity_config) + self._file_search_stores = TemporalAsyncFileSearchStores( + api_client, activity_config + ) + self._temporal_interactions = TemporalAsyncInteractions(activity_config) + self._temporal_agents = TemporalAsyncAgents(activity_config) + + @property + def interactions( # type: ignore[override] + self, + ) -> TemporalAsyncInteractions: # pyright: ignore[reportIncompatibleMethodOverride] + """Temporal-aware interactions resource; operations run as activities.""" + return self._temporal_interactions + + @property + def agents( # type: ignore[override] + self, + ) -> TemporalAsyncAgents: # pyright: ignore[reportIncompatibleMethodOverride] + """Temporal-aware agents resource; operations run as activities.""" + return self._temporal_agents + + @property + def webhooks(self) -> NoReturn: # pyright: ignore[reportIncompatibleMethodOverride] + """Raise — webhooks are not supported in Temporal workflows.""" + raise RuntimeError( + "client.webhooks is not supported in Temporal workflows. " + "Manage webhooks outside the workflow with a regular genai.Client." + ) diff --git a/temporalio/contrib/google_genai/_temporal_file_search_stores.py b/temporalio/contrib/google_genai/_temporal_file_search_stores.py new file mode 100644 index 000000000..7b7e3bbc8 --- /dev/null +++ b/temporalio/contrib/google_genai/_temporal_file_search_stores.py @@ -0,0 +1,109 @@ +"""Temporal-aware AsyncFileSearchStores shim. + +``TemporalAsyncFileSearchStores`` is an ``AsyncFileSearchStores`` subclass +whose ``upload_to_file_search_store`` method dispatches through a Temporal +activity so the entire upload (including filesystem access and resumable +upload negotiation) runs on the activity worker. +""" + +from __future__ import annotations + +import io +import os +from datetime import timedelta + +from google.genai import types +from google.genai.file_search_stores import AsyncFileSearchStores + +from temporalio import workflow as temporal_workflow +from temporalio.contrib.google_genai._models import ( + _GeminiUploadToFileSearchStoreRequest, +) +from temporalio.contrib.google_genai._temporal_api_client import ( + _TemporalApiClient, + _validate_http_options, +) +from temporalio.workflow import ActivityConfig + + +class TemporalAsyncFileSearchStores(AsyncFileSearchStores): + """``AsyncFileSearchStores`` subclass that routes ``upload_to_file_search_store`` through an activity. + + The entire upload operation — including filesystem access, resumable + upload negotiation, and chunked transfer — runs inside a Temporal + activity on the worker. All other methods (``create``, ``get``, + ``delete``, ``list``, ``import_file``, ``documents``) are inherited + and already work through the ``_TemporalApiClient``'s ``async_request`` + activity. + """ + + def __init__( + self, + api_client: _TemporalApiClient, + activity_config: ActivityConfig | None = None, + ) -> None: + """Initialize with activity config for upload timeouts.""" + super().__init__(api_client) + self._activity_config = ( + ActivityConfig(start_to_close_timeout=timedelta(seconds=60)) + if activity_config is None + else activity_config + ) + + async def upload_to_file_search_store( + self, + *, + file_search_store_name: str, + file: str | os.PathLike[str] | io.IOBase, + config: types.UploadToFileSearchStoreConfigOrDict | None = None, + ) -> types.UploadToFileSearchStoreOperation: + """Upload a file to a file search store via a Temporal activity. + + Accepts a file path (resolved on the worker), ``os.PathLike``, or + an ``io.IOBase`` (bytes sent across the activity boundary). + """ + act_config: ActivityConfig = {**self._activity_config} + if "summary" not in act_config: + act_config["summary"] = "file_search_stores.upload" + + upload_config = None + if config is not None: + if isinstance(config, dict): + upload_config = types.UploadToFileSearchStoreConfig.model_validate( + config + ) + else: + upload_config = config + _validate_http_options(upload_config.http_options) + + if isinstance(file, io.IOBase): + file_bytes = file.read() + if not isinstance(file_bytes, bytes): + raise TypeError( + "file must be a binary stream when passing an io.IOBase; " + f"file.read() must return bytes (got {type(file_bytes).__name__})" + ) + req = _GeminiUploadToFileSearchStoreRequest( + file_search_store_name=file_search_store_name, + file_bytes=file_bytes, + config=upload_config, + ) + elif isinstance(file, str): + req = _GeminiUploadToFileSearchStoreRequest( + file_search_store_name=file_search_store_name, + file_path=file, + config=upload_config, + ) + else: + req = _GeminiUploadToFileSearchStoreRequest( + file_search_store_name=file_search_store_name, + file_path=file.__fspath__(), + config=upload_config, + ) + + return await temporal_workflow.execute_activity( + "gemini_file_search_stores_upload", + req, + result_type=types.UploadToFileSearchStoreOperation, + **act_config, + ) diff --git a/temporalio/contrib/google_genai/_temporal_files.py b/temporalio/contrib/google_genai/_temporal_files.py new file mode 100644 index 000000000..f785c00cb --- /dev/null +++ b/temporalio/contrib/google_genai/_temporal_files.py @@ -0,0 +1,169 @@ +"""Temporal-aware AsyncFiles shim. + +``TemporalAsyncFiles`` is an ``AsyncFiles`` subclass whose ``upload`` +and ``download`` methods dispatch through Temporal activities so the +entire file operation (including filesystem access) runs on the +activity worker. +""" + +from __future__ import annotations + +import io +import os +from datetime import timedelta +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + import google.auth.credentials +from google.genai import types +from google.genai.files import AsyncFiles + +from temporalio import workflow as temporal_workflow +from temporalio.contrib.google_genai._models import ( + _GeminiDownloadFileRequest, + _GeminiRegisterFilesRequest, + _GeminiUploadFileRequest, +) +from temporalio.contrib.google_genai._temporal_api_client import ( + _TemporalApiClient, + _validate_http_options, +) +from temporalio.workflow import ActivityConfig + + +class TemporalAsyncFiles(AsyncFiles): + """``AsyncFiles`` subclass that routes ``upload`` and ``download`` through activities. + + The entire file operation — including filesystem access, resumable + upload negotiation, and chunked transfer — runs inside a Temporal + activity on the worker. ``get``, ``delete``, and ``list`` are + inherited from ``AsyncFiles`` and already work through the + ``_TemporalApiClient``'s ``async_request`` activity. + """ + + def __init__( + self, + api_client: _TemporalApiClient, + activity_config: ActivityConfig | None = None, + ) -> None: + """Initialize with activity config for file operation timeouts.""" + super().__init__(api_client) + self._activity_config = ( + ActivityConfig(start_to_close_timeout=timedelta(seconds=60)) + if activity_config is None + else activity_config + ) + + async def upload( + self, + *, + file: str | os.PathLike[str] | io.IOBase, + config: types.UploadFileConfigOrDict | None = None, + ) -> types.File: + """Upload a file via a Temporal activity. + + Accepts a file path (resolved on the worker), ``os.PathLike``, or + an ``io.IOBase`` (bytes sent across the activity boundary). + """ + act_config: ActivityConfig = {**self._activity_config} + if "summary" not in act_config: + act_config["summary"] = "files.upload" + + upload_config = None + if config is not None: + if isinstance(config, dict): + upload_config = types.UploadFileConfig.model_validate(config) + else: + upload_config = config + _validate_http_options(upload_config.http_options) + + if isinstance(file, io.IOBase): + file_bytes = file.read() + if not isinstance(file_bytes, bytes): + raise TypeError( + "file must be a binary stream when passing an io.IOBase; " + f"file.read() must return bytes (got {type(file_bytes).__name__})" + ) + req = _GeminiUploadFileRequest(file_bytes=file_bytes, config=upload_config) + elif isinstance(file, str): + req = _GeminiUploadFileRequest(file_path=file, config=upload_config) + else: + # os.PathLike — convert via __fspath__() to avoid importing os + req = _GeminiUploadFileRequest( + file_path=file.__fspath__(), config=upload_config + ) + + return await temporal_workflow.execute_activity( + "gemini_files_upload", + req, + result_type=types.File, + **act_config, + ) + + async def download( + self, + *, + file: str | types.File, + config: types.DownloadFileConfigOrDict | None = None, + ) -> bytes: + """Download a file via a Temporal activity.""" + act_config: ActivityConfig = {**self._activity_config} + if "summary" not in act_config: + act_config["summary"] = "files.download" + + download_config = None + if config is not None: + if isinstance(config, dict): + download_config = types.DownloadFileConfig.model_validate(config) + else: + download_config = config + _validate_http_options(download_config.http_options) + + if isinstance(file, types.File): + if not file.name: + raise ValueError("File object must have a name to download.") + file_name = file.name + else: + file_name = file + + return await temporal_workflow.execute_activity( + "gemini_files_download", + _GeminiDownloadFileRequest(file=file_name, config=download_config), + result_type=bytes, + **act_config, + ) + + async def register_files( + self, + *, + auth: google.auth.credentials.Credentials, + uris: list[str], + config: types.RegisterFilesConfigOrDict | None = None, + ) -> types.RegisterFilesResponse: + """Register GCS files via a Temporal activity. + + .. note:: + The ``auth`` parameter is **ignored**. The activity uses + ``credentials`` if provided to ``GoogleGenAIPlugin``, + otherwise falls back to the ``genai.Client``'s own credentials. + Either way, those credentials must have access to the GCS URIs + being registered. + """ + act_config: ActivityConfig = {**self._activity_config} + if "summary" not in act_config: + act_config["summary"] = "files.register_files" + + register_config = None + if config is not None: + if isinstance(config, dict): + register_config = types.RegisterFilesConfig.model_validate(config) + else: + register_config = config + _validate_http_options(register_config.http_options) + + return await temporal_workflow.execute_activity( + "gemini_files_register", + _GeminiRegisterFilesRequest(uris=uris, config=register_config), + result_type=types.RegisterFilesResponse, + **act_config, + ) diff --git a/temporalio/contrib/google_genai/_temporal_interactions.py b/temporalio/contrib/google_genai/_temporal_interactions.py new file mode 100644 index 000000000..54d8353cd --- /dev/null +++ b/temporalio/contrib/google_genai/_temporal_interactions.py @@ -0,0 +1,258 @@ +"""Temporal-aware interactions resource shim. + +``TemporalAsyncInteractions`` exposes the same surface as google-genai's +``AsyncClient.interactions`` resource, but each operation is dispatched through +a Temporal activity holding the real ``genai.Client`` on the worker. The +Interactions API does not go through ``BaseApiClient`` — it uses a vendored, +Stainless-generated HTTP client that the ``TemporalApiClient`` shim never sees — +so each operation is routed as a whole through an activity instead. + +The shim depends only on the public ``google.genai.interactions`` surface (types +plus ``client.aio.interactions`` on the worker), not on google-genai internals, +so it is unaffected by regeneration of the vendored client. +""" + +from __future__ import annotations + +from collections.abc import AsyncIterator +from datetime import timedelta +from types import TracebackType +from typing import Any, cast + +import pydantic +from google.genai.interactions import InteractionSSEEvent + +from temporalio import workflow as temporal_workflow +from temporalio.contrib.google_genai._compat import Interaction +from temporalio.contrib.google_genai._models import ( + _GeminiInteractionIdRequest, + _GeminiInteractionRequest, + _GeminiInteractionStreamedResponse, +) +from temporalio.workflow import ActivityConfig + +_DEFAULT_INTERACTION_TIMEOUT = timedelta(seconds=60) + +# ``InteractionSSEEvent`` is a discriminated union (on ``event_type``); a +# ``TypeAdapter`` dispatches it — including nested unions such as a +# ``step.start`` event's ``step`` — leniently. +_SSE_EVENT_ADAPTER: pydantic.TypeAdapter[Any] = pydantic.TypeAdapter( + InteractionSSEEvent +) + + +def _deserialize(value: dict[str, Any], type_: Any) -> Any: + """Rehydrate a dict returned by an activity into its public genai model. + + ``InteractionSSEEvent`` is deserialized through its ``TypeAdapter``, which + dispatches the discriminated union (and nested unions) and tolerates the + sparse nested payloads the API legitimately emits (e.g. an + ``interaction.created`` event carrying an ``Interaction`` with just ``id`` + and ``object``). Plain models use ``model_validate``, which recurses nested + models (e.g. ``AgentListResponse.agents`` into ``Agent``) and resolves + aliases; the SDK's optional fields keep it tolerant of the minimal objects + the API returns. Both paths are pure functions, safe to run in the workflow + on every replay. + """ + if type_ is InteractionSSEEvent: + return _SSE_EVENT_ADAPTER.validate_python(value) + return type_.model_validate(value) + + +def _pop_timeout(params: dict[str, Any], config: ActivityConfig) -> None: + """Pop a per-call ``timeout`` kwarg and apply it to the activity config. + + The Interactions API expresses timeouts in seconds. Temporal owns + timeouts/retries, so the value maps to ``start_to_close_timeout`` + rather than being forwarded to the underlying HTTP client. + """ + timeout = params.pop("timeout", None) + if timeout is None: + return + if not isinstance(timeout, (int, float)) or isinstance(timeout, bool): + raise ValueError( + "timeout must be numeric seconds when calling the Interactions " + "API from a workflow; configure anything more granular via " + "activity_config instead." + ) + config["start_to_close_timeout"] = timedelta(seconds=timeout) + + +class _TemporalInteractionAsyncStream: + """Async stream over interaction events already drained in an activity. + + Presents the same ``async for`` / ``async with`` / ``close()`` surface as + the SDK's streaming response, but iterates an in-memory event list drained + inside the activity (there is no httpx response or client on the workflow + side), rehydrating each event back into its typed form on iteration. + """ + + def __init__( + self, + events: list[dict[str, Any]], + ) -> None: + self._events = events + + def __aiter__(self) -> AsyncIterator[InteractionSSEEvent]: + return self._iter() + + async def _iter(self) -> AsyncIterator[InteractionSSEEvent]: + for event in self._events: + yield cast(InteractionSSEEvent, _deserialize(event, InteractionSSEEvent)) + + async def __aenter__(self) -> _TemporalInteractionAsyncStream: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + pass + + async def close(self) -> None: + """No-op — the upstream stream was drained inside the activity.""" + pass + + +class TemporalAsyncInteractions: + """Interactions resource shim that routes calls through activities. + + Methods accept the same keyword arguments as the real resource and + forward them verbatim — the SDK validates them on the worker side, so + a bad argument surfaces as an activity failure (retried per the + activity's retry policy) rather than a workflow-side error. + + ``with_raw_response`` / ``with_streaming_response`` are not supported + in workflows. + """ + + def __init__( + self, + activity_config: ActivityConfig | None = None, + ) -> None: + """Initialize with activity config for interaction operation timeouts.""" + self._activity_config = ( + ActivityConfig(start_to_close_timeout=_DEFAULT_INTERACTION_TIMEOUT) + if activity_config is None + else activity_config + ) + + def _config(self, summary: str, params: dict[str, Any]) -> ActivityConfig: + config: ActivityConfig = {**self._activity_config} + if "summary" not in config: + config["summary"] = summary + _pop_timeout(params, config) + return config + + async def create( + self, + *, + stream: bool = False, + **kwargs: Any, + ) -> Interaction | _TemporalInteractionAsyncStream: + """Create an interaction via a Temporal activity. + + ``kwargs`` is forwarded verbatim to ``client.aio.interactions.create`` + on the worker. With ``stream=True`` the activity drains the SSE + stream and returns all events batched; the returned object supports + ``async for`` / ``async with`` like the SDK's streaming response. + """ + params = dict(kwargs) + config = self._config( + "interactions.create (stream)" if stream else "interactions.create", + params, + ) + req = _GeminiInteractionRequest(params=params) + if stream: + resp = await temporal_workflow.execute_activity( + "gemini_interactions_create_streamed", + req, + result_type=_GeminiInteractionStreamedResponse, + **config, + ) + return _TemporalInteractionAsyncStream(resp.events) + raw = await temporal_workflow.execute_activity( + "gemini_interactions_create", + req, + result_type=dict[str, Any], + **config, + ) + return cast(Interaction, _deserialize(raw, Interaction)) + + async def get( + self, + id: str, + *, + stream: bool = False, + **kwargs: Any, + ) -> Interaction | _TemporalInteractionAsyncStream: + """Get an interaction via a Temporal activity. + + Supports ``stream=True`` (with the SDK's ``last_event_id`` kwarg + for resumption); events come back batched like :meth:`create`. + """ + params = dict(kwargs) + config = self._config( + "interactions.get (stream)" if stream else "interactions.get", + params, + ) + req = _GeminiInteractionIdRequest(id=id, params=params) + if stream: + resp = await temporal_workflow.execute_activity( + "gemini_interactions_get_streamed", + req, + result_type=_GeminiInteractionStreamedResponse, + **config, + ) + return _TemporalInteractionAsyncStream(resp.events) + raw = await temporal_workflow.execute_activity( + "gemini_interactions_get", + req, + result_type=dict[str, Any], + **config, + ) + return cast(Interaction, _deserialize(raw, Interaction)) + + async def delete( + self, + id: str, + **kwargs: Any, + ) -> object: + """Delete an interaction via a Temporal activity.""" + params = dict(kwargs) + config = self._config("interactions.delete", params) + return await temporal_workflow.execute_activity( + "gemini_interactions_delete", + _GeminiInteractionIdRequest(id=id, params=params), + **config, + ) + + async def cancel( + self, + id: str, + **kwargs: Any, + ) -> Interaction: + """Cancel an interaction via a Temporal activity.""" + params = dict(kwargs) + config = self._config("interactions.cancel", params) + raw = await temporal_workflow.execute_activity( + "gemini_interactions_cancel", + _GeminiInteractionIdRequest(id=id, params=params), + result_type=dict[str, Any], + **config, + ) + return cast(Interaction, _deserialize(raw, Interaction)) + + @property + def with_raw_response(self) -> Any: + """Raise — raw responses are not available in workflows.""" + raise RuntimeError("with_raw_response is not supported in Temporal workflows.") + + @property + def with_streaming_response(self) -> Any: + """Raise — streaming responses are not available in workflows.""" + raise RuntimeError( + "with_streaming_response is not supported in Temporal workflows." + ) diff --git a/temporalio/contrib/google_genai/_temporal_mcp.py b/temporalio/contrib/google_genai/_temporal_mcp.py new file mode 100644 index 000000000..718c6aaa3 --- /dev/null +++ b/temporalio/contrib/google_genai/_temporal_mcp.py @@ -0,0 +1,116 @@ +"""Temporal-aware ``mcp.ClientSession`` shim. + +``TemporalMcpClientSession`` is an ``mcp.ClientSession`` subclass that the user +places in ``generate_content(config=GenerateContentConfig(tools=[...]))`` just +like a real MCP session. The Gemini SDK recognizes it via +``isinstance(tool, McpClientSession)`` and, inside ``generate_content`` (which +runs in the workflow), calls only two methods on it: ``list_tools()`` at tool +discovery and ``call_tool(name, arguments)`` in the automatic-function-calling +loop. Both are overridden here to dispatch to the ``{server}-list-tools`` / +``{server}-call-tool`` activities, so the real ``mcp.ClientSession`` lives only +on the worker (registered via ``GoogleGenAIPlugin(mcp_servers=...)``). + +This mirrors strands' ``TemporalMCPClient``: the handle carries only the server +name (which selects the worker-side factory) plus activity options; the +connection factory is never passed to the workflow or the root client. +""" + +from __future__ import annotations + +from datetime import timedelta +from typing import Any + +from mcp import ClientSession +from mcp.shared.session import ProgressFnT +from mcp.types import CallToolResult, ListToolsResult, PaginatedRequestParams + +from temporalio import workflow as temporal_workflow +from temporalio.contrib.google_genai._models import _McpCallToolRequest +from temporalio.workflow import ActivityConfig + +_DEFAULT_MCP_TIMEOUT = timedelta(seconds=60) + + +class TemporalMcpClientSession(ClientSession): + """``mcp.ClientSession`` whose tool discovery and calls run as activities. + + .. warning:: + This API is experimental and may change in future versions. + + Construct inside a workflow and pass it in the ``tools`` list of a + ``generate_content`` call. The matching server name must be registered on + the worker via ``GoogleGenAIPlugin(mcp_servers={name: factory})``. + + ``cache_tools`` controls how often tools are listed. When ``False`` (the + default) the ``{server}-list-tools`` activity runs each time the SDK + discovers tools (i.e. per ``generate_content`` call), so a server whose + tools changed mid-workflow is picked up. When ``True`` the first listing is + cached on this instance and reused for its lifetime (replay-safe in-workflow + state). + + Args: + server_name: Name selecting the worker-side factory; also the activity + prefix (``{server_name}-list-tools`` / ``{server_name}-call-tool``). + cache_tools: Cache the tool listing after the first call. + activity_config: Activity configuration (timeouts, retry policy, etc.) + for the MCP activities. Defaults to a 60-second + ``start_to_close_timeout``. + """ + + def __init__( # pyright: ignore[reportMissingSuperCall] + self, + server_name: str, + *, + cache_tools: bool = False, + activity_config: ActivityConfig | None = None, + ) -> None: + """Initialize without calling super (no real streams exist here).""" + self._server_name = server_name + self._cache_tools = cache_tools + self._cached_tools: ListToolsResult | None = None + self._activity_config: ActivityConfig = ( + ActivityConfig(start_to_close_timeout=_DEFAULT_MCP_TIMEOUT) + if activity_config is None + else activity_config + ) + + def _config(self, summary: str) -> ActivityConfig: + config: ActivityConfig = {**self._activity_config} + if "summary" not in config: + config["summary"] = summary + return config + + async def list_tools( # pyright: ignore[reportIncompatibleMethodOverride] + self, + cursor: str | None = None, + *, + params: PaginatedRequestParams | None = None, + ) -> ListToolsResult: + """List the server's tools via the ``{server}-list-tools`` activity.""" + if self._cache_tools and self._cached_tools is not None: + return self._cached_tools + result = await temporal_workflow.execute_activity( + f"{self._server_name}-list-tools", + result_type=ListToolsResult, + **self._config(f"mcp.{self._server_name}.list_tools"), + ) + if self._cache_tools: + self._cached_tools = result + return result + + async def call_tool( # pyright: ignore[reportIncompatibleMethodOverride] + self, + name: str, + arguments: dict[str, Any] | None = None, + read_timeout_seconds: timedelta | None = None, + progress_callback: ProgressFnT | None = None, + *, + meta: dict[str, Any] | None = None, + ) -> CallToolResult: + """Call a tool via the ``{server}-call-tool`` activity.""" + return await temporal_workflow.execute_activity( + f"{self._server_name}-call-tool", + _McpCallToolRequest(name=name, arguments=arguments or {}), + result_type=CallToolResult, + **self._config(f"mcp.{self._server_name}.call_tool:{name}"), + ) diff --git a/temporalio/contrib/google_genai/testing.py b/temporalio/contrib/google_genai/testing.py new file mode 100644 index 000000000..5d4a40486 --- /dev/null +++ b/temporalio/contrib/google_genai/testing.py @@ -0,0 +1,177 @@ +"""Testing utilities for the Google Gemini SDK Temporal integration. + +These let you exercise workflows that use +:class:`temporalio.contrib.google_genai.TemporalAsyncClient` without making +real Gemini API calls. Script the model's responses with :func:`text_response` +/ :func:`function_call_response`, build a plugin with +:class:`GeminiTestServer`, and register it on your worker like the real +:class:`temporalio.contrib.google_genai.GoogleGenAIPlugin`. + +Example:: + + server = GeminiTestServer( + [ + function_call_response("get_weather", {"city": "Tokyo"}), + text_response("It's sunny in Tokyo."), + ] + ) + async with Worker( + client, + task_queue="test", + workflows=[MyAgentWorkflow], + activities=[get_weather], + plugins=[server.plugin()], + ): + ... + assert len(server.requests) == 2 # one per model turn +""" + +from __future__ import annotations + +import json +from collections.abc import Sequence +from datetime import timedelta +from typing import TYPE_CHECKING, Any + +from google.genai import Client as GeminiClient +from google.genai.types import HttpResponse as SdkHttpResponse + +from temporalio.contrib.google_genai._google_genai_plugin import GoogleGenAIPlugin + +if TYPE_CHECKING: + from temporalio.contrib.google_genai._mcp import McpSessionFactory + +__all__ = [ + "GeminiTestServer", + "function_call_response", + "text_response", +] + + +def text_response(text: str) -> str: + """Build a ``generate_content`` response body with a single text part.""" + return json.dumps( + { + "candidates": [ + { + "content": {"role": "model", "parts": [{"text": text}]}, + "finishReason": "STOP", + } + ], + "usageMetadata": { + "promptTokenCount": 5, + "candidatesTokenCount": 10, + }, + } + ) + + +def function_call_response(name: str, args: dict[str, Any]) -> str: + """Build a ``generate_content`` response body with a single function call. + + The Gemini SDK's automatic function calling loop will invoke the matching + tool, then request another response — so pair each function-call response + with a following :func:`text_response` (or further calls). + """ + return json.dumps( + { + "candidates": [ + { + "content": { + "role": "model", + "parts": [{"functionCall": {"name": name, "args": args}}], + }, + "finishReason": "STOP", + } + ], + "usageMetadata": { + "promptTokenCount": 10, + "candidatesTokenCount": 15, + }, + } + ) + + +class GeminiTestServer: + """Scripts Gemini model responses so workflows run without real API calls. + + Pass canned response bodies built with :func:`text_response` / + :func:`function_call_response`. Each model call — including each turn of an + automatic-function-calling loop and each ``generate_content_stream`` call — + consumes the next response in order. Build a plugin with :meth:`plugin` + and register it on your worker; inspect :attr:`requests` afterwards to + assert exactly what the integration sent. + + Only model calls (``client.models``) are scripted. File, interaction, and + agent operations are not; mock those on a ``genai.Client`` directly if a + test needs them. MCP servers can be registered with + ``plugin(mcp_servers=...)`` and run for real. + """ + + def __init__(self, responses: Sequence[str]) -> None: + """Initialize with the response bodies to serve, in order.""" + self._responses = list(responses) + self._index = 0 + self.requests: list[dict[str, Any]] = [] + + def _next(self) -> str: + idx = self._index + self._index += 1 + if idx >= len(self._responses): + raise AssertionError( + f"GeminiTestServer ran out of responses (call {idx + 1}, " + f"have {len(self._responses)}); script another response." + ) + return self._responses[idx] + + def plugin( + self, + *, + mcp_servers: dict[str, McpSessionFactory] | None = None, + mcp_connection_idle_timeout: timedelta | None = None, + ) -> GoogleGenAIPlugin: + """Return a :class:`GoogleGenAIPlugin` whose model calls serve the script. + + The real plugin activities run; only the underlying HTTP layer is + replaced, so request formatting and the AFC loop are exercised exactly + as in production. + + Args: + mcp_servers: MCP servers to expose to workflows, as on + :class:`temporalio.contrib.google_genai.GoogleGenAIPlugin`. + These are *not* scripted — ``list_tools`` / ``call_tool`` run + for real against the given sessions — so a test can drive an + MCP tool call with a scripted + :func:`function_call_response` and assert on what the server + actually returned. + mcp_connection_idle_timeout: How long an idle worker-side MCP + connection stays open, as on + :class:`temporalio.contrib.google_genai.GoogleGenAIPlugin`. + """ + client = GeminiClient(api_key="fake-test-key") + + async def fake_async_request(*_args: Any, **kwargs: Any) -> SdkHttpResponse: + self.requests.append(dict(kwargs.get("request_dict") or {})) + return SdkHttpResponse( + headers={"content-type": "application/json"}, + body=self._next(), + ) + + async def fake_async_request_streamed(*_args: Any, **kwargs: Any) -> Any: + self.requests.append(dict(kwargs.get("request_dict") or {})) + body = self._next() + + async def _gen() -> Any: + yield SdkHttpResponse( + headers={"content-type": "application/json"}, body=body + ) + + return _gen() + + client._api_client.async_request = fake_async_request # type: ignore[assignment] + client._api_client.async_request_streamed = fake_async_request_streamed # type: ignore[assignment] + return GoogleGenAIPlugin( + client, + mcp_servers=mcp_servers, + mcp_connection_idle_timeout=mcp_connection_idle_timeout, + ) diff --git a/temporalio/contrib/google_genai/workflow.py b/temporalio/contrib/google_genai/workflow.py new file mode 100644 index 000000000..08e40f0c5 --- /dev/null +++ b/temporalio/contrib/google_genai/workflow.py @@ -0,0 +1,111 @@ +"""Workflow utilities for Google Gemini SDK integration with Temporal. + +This module provides utilities for using the Google Gemini SDK within Temporal +workflows. The key entry points are: + +- :func:`activity_as_tool` — converts a Temporal activity into a Gemini tool + callable for use with automatic function calling (AFC). +""" + +from __future__ import annotations + +import functools +import inspect +from collections.abc import Callable +from typing import Any + +from temporalio import activity +from temporalio import workflow as temporal_workflow +from temporalio.contrib.google_genai._errors import GoogleGenAIError +from temporalio.workflow import ActivityConfig + + +def activity_as_tool( + fn: Callable, + *, + activity_config: ActivityConfig | None = None, +) -> Callable: + """Convert a Temporal activity into a Gemini-compatible async tool callable. + + .. warning:: + This API is experimental and may change in future versions. + Use with caution in production environments. + + Returns an async callable with the same name, docstring, and type signature as + ``fn``. When Gemini's automatic function calling (AFC) invokes the returned + callable from within a Temporal workflow, the call is executed as a Temporal + activity via :func:`workflow.execute_activity`. Each tool invocation therefore + appears as a separate, durable entry in the workflow event history. + + Because AFC is left **enabled**, the Gemini SDK owns the agentic loop — no + manual ``while`` loop or ``run_agent()`` helper is required. Pass the returned + callable directly to ``GenerateContentConfig(tools=[...])``. + + Args: + fn: A Temporal activity function decorated with ``@activity.defn``. + activity_config: Configuration for the activity execution (timeouts, + retry policy, etc.). Must set ``start_to_close_timeout`` or + ``schedule_to_close_timeout`` — Temporal requires one, and there is + no default; otherwise the tool call raises when the activity is + invoked. + + Returns: + An async callable suitable for use as a Gemini tool. + + Raises: + GoogleGenAIError: If ``fn`` is not decorated with ``@activity.defn`` or + has no activity name. + """ + ret = activity._Definition.from_callable(fn) + if not ret: + raise GoogleGenAIError( + "Bare function without @activity.defn decorator is not supported", + "invalid_tool", + ) + if ret.name is None: + raise GoogleGenAIError( + "Activity must have a name to be used as a Gemini tool", + "invalid_tool", + ) + + config: ActivityConfig = {**(activity_config or {})} + if "summary" not in config: + config["summary"] = "tool_call" + + # For class-based activities the first parameter is 'self'. Partially apply + # it so that Gemini inspects only the user-facing parameters when building + # the function-call schema, while the worker resolves the real instance at + # execution time. + params = list(inspect.signature(fn).parameters.keys()) + schema_fn: Callable = fn + if params and params[0] == "self": + partial = functools.partial(fn, None) + setattr(partial, "__name__", fn.__name__) + partial.__annotations__ = getattr(fn, "__annotations__", {}) + setattr( + partial, + "__temporal_activity_definition", + getattr(fn, "__temporal_activity_definition", None), + ) + partial.__doc__ = fn.__doc__ + schema_fn = partial + + activity_name: str = ret.name + + async def wrapper(*args: Any, **kwargs: Any) -> Any: + sig = inspect.signature(schema_fn) + bound = sig.bind(*args, **kwargs) + bound.apply_defaults() + activity_args = list(bound.arguments.values()) + return await temporal_workflow.execute_activity( + activity_name, + args=activity_args, + **config, + ) + + wrapper.__name__ = schema_fn.__name__ # type: ignore + wrapper.__doc__ = schema_fn.__doc__ + setattr(wrapper, "__signature__", inspect.signature(schema_fn)) + wrapper.__annotations__ = getattr(schema_fn, "__annotations__", {}) + + return wrapper diff --git a/temporalio/contrib/langgraph/README.md b/temporalio/contrib/langgraph/README.md index dafe598b7..d5b7d4e0f 100644 --- a/temporalio/contrib/langgraph/README.md +++ b/temporalio/contrib/langgraph/README.md @@ -143,6 +143,46 @@ await g.ainvoke({...}, context=Context(user_id="alice")) Your `context` object must be serializable by the configured Temporal payload converter, since it crosses the Activity boundary. +## Summaries + +Summaries are short, human-readable labels that show up in the Temporal UI and CLI, making it easier to see what each step of a run is doing. + +### Static summary + +`summary` is an ordinary Activity option, so a fixed per-node label works today — pass it like any other option: + +```python +g.add_node("plan", plan, metadata={"execute_in": "activity", "summary": "Planning step"}) +``` + +It is attached to the node's scheduled-activity event (`execute_in="activity"` only). + +### Dynamic summary (`summary_fn`) + +To derive the label from the node's input at runtime, supply a `summary_fn`. It receives the node's `(args, kwargs)` and returns a summary string, or `None`/`""` for no summary. For a `StateGraph` node `args[0]` is the state; for a Functional `@task` it is the task's arguments. + +```python +def summarize(args, kwargs) -> str | None: + state = args[0] + return f"stage={state['stage']} doc={state['doc_id']}" + +# Graph API: per-node +g.add_node("plan", plan, metadata={"execute_in": "activity", "summary_fn": summarize}) + +# Functional API: per-task +plugin = LangGraphPlugin( + tasks=[plan], + activity_options={"plan": {"execute_in": "activity", "summary_fn": summarize}}, +) +``` + +`summary_fn` is set per node/task (like the static `summary`), so different nodes — which receive different inputs — can compute their summaries independently. You can also put a `summary` or `summary_fn` in `default_activity_options` as a fallback for every node; a node/task that sets either form overrides the inherited default (you just can't set both forms at the same level). + +- For `execute_in="activity"` nodes the result sets the activity `summary` (one per scheduled-activity event, visible in history). +- For `execute_in="workflow"` nodes there is no activity, so the result updates the workflow's current details via [`workflow.set_current_details()`](https://python.temporal.io/temporalio.workflow.html#set_current_details). This is a single workflow-level slot (last-writer-wins) reflecting the most recent workflow-bound node that defines a `summary_fn`; a `None`/`""` result clears it. It is queryable via `__temporal_workflow_metadata`. + +`summary_fn` runs in workflow context on every replay, so it **must be deterministic and must not raise** (an exception fails the workflow task). Setting both a static `summary` and a `summary_fn` on the same node raises `ValueError`. + ## Streaming When `streaming_topic` is set on `LangGraphPlugin`, calls to `langgraph.config.get_stream_writer()` inside a node publish to the named topic on the workflow's [`WorkflowStream`](https://github.com/temporalio/sdk-python/tree/main/temporalio/contrib/workflow_streams). Activity-side nodes publish via `WorkflowStreamClient` (a signal carrying batched items, controlled by `streaming_batch_interval`); workflow-side nodes publish synchronously to the in-workflow stream (no signal). External subscribers consume the stream with `WorkflowStreamClient.create(...).topic(...).subscribe(...)`. diff --git a/temporalio/contrib/langgraph/_activity.py b/temporalio/contrib/langgraph/_activity.py index d75dbac2e..c8447df47 100644 --- a/temporalio/contrib/langgraph/_activity.py +++ b/temporalio/contrib/langgraph/_activity.py @@ -109,6 +109,7 @@ def thread_safe_writer(value: Any) -> None: def wrap_execute_activity( afunc: Callable[[ActivityInput], Awaitable[ActivityOutput]], task_id: str = "", + summary_fn: Callable[[tuple[Any, ...], dict[str, Any]], str | None] | None = None, **execute_activity_kwargs: Any, ) -> Callable[..., Any]: """Wrap an activity function to be called via workflow.execute_activity with caching.""" @@ -156,9 +157,15 @@ async def wrapper(*args: Any, **kwargs: Any) -> Any: input = ActivityInput( args=args, kwargs=kwargs, langgraph_config=langgraph_config ) - output = await workflow.execute_activity( - afunc, input, **execute_activity_kwargs - ) + # Compute a dynamic activity summary (if configured) on the schedule + # path only; a cache hit above returns before reaching here, so no + # activity is scheduled and no summary is needed. + call_kwargs = dict(execute_activity_kwargs) + if summary_fn is not None: + summary = summary_fn(args, kwargs) + if summary: + call_kwargs["summary"] = summary + output = await workflow.execute_activity(afunc, input, **call_kwargs) if output.langgraph_interrupts is not None: raise GraphInterrupt(output.langgraph_interrupts) diff --git a/temporalio/contrib/langgraph/_plugin.py b/temporalio/contrib/langgraph/_plugin.py index a1320d1a8..03881ca2a 100644 --- a/temporalio/contrib/langgraph/_plugin.py +++ b/temporalio/contrib/langgraph/_plugin.py @@ -35,6 +35,36 @@ _ACTIVITY_OPTION_KEYS: frozenset[str] = frozenset( {"execute_in", *inspect.signature(workflow.execute_activity).parameters} ) +# Node/task option keys beyond the raw execute_activity parameters: +# 'summary_fn' is a callable consumed in the workflow (not a Temporal +# option), so it must be split out of Graph API metadata too. +_LANGGRAPH_OPTION_KEYS: frozenset[str] = _ACTIVITY_OPTION_KEYS | frozenset( + {"summary_fn"} +) + + +def _constant_summary_fn( + value: str, +) -> Callable[[tuple[Any, ...], dict[str, Any]], str]: + """Adapt a static summary string to the summary_fn interface.""" + return lambda args, kwargs: value + + +def _merge_activity_opts( + defaults: dict[str, Any] | None, specific: dict[str, Any] +) -> dict[str, Any]: + """Layer per-node/task options over the plugin defaults. + + ``summary`` and ``summary_fn`` are two forms of one setting, so a node or + task that supplies either form overrides an inherited default of *either* + form, rather than coexisting with it and tripping the exclusivity check. + """ + merged = dict(defaults or {}) + if "summary" in specific or "summary_fn" in specific: + merged.pop("summary", None) + merged.pop("summary_fn", None) + merged.update(specific) + return merged class LangGraphPlugin(SimplePlugin): @@ -69,7 +99,9 @@ class LangGraphPlugin(SimplePlugin): Functional API has no per-task ``metadata`` channel. default_activity_options: Activity options applied to every activity-bound node and task, overridable per-node (Graph API - ``metadata``) or per-task (``activity_options[name]``). + ``metadata``) or per-task (``activity_options[name]``). A + node/task that sets ``summary`` or ``summary_fn`` overrides an + inherited default of either form. streaming_topic: When set, ``langgraph.config.get_stream_writer()`` inside a node publishes to this topic on the workflow's :class:`WorkflowStream`. The workflow must construct @@ -130,6 +162,16 @@ def __init__( "activity_options[task_name] (Functional API)." ) + if ( + default_activity_options + and "summary" in default_activity_options + and "summary_fn" in default_activity_options + ): + raise ValueError( + "Set either 'summary' or 'summary_fn' in default_activity_options, " + "not both." + ) + self.activities: list = [] self._streaming_topic = streaming_topic self._streaming_batch_interval = streaming_batch_interval @@ -168,12 +210,14 @@ def __init__( # the node function via config["metadata"]. node_meta = node.metadata or {} node_opts = { - k: v for k, v in node_meta.items() if k in _ACTIVITY_OPTION_KEYS + k: v + for k, v in node_meta.items() + if k in _LANGGRAPH_OPTION_KEYS } node.metadata = { k: v for k, v in node_meta.items() - if k not in _ACTIVITY_OPTION_KEYS + if k not in _LANGGRAPH_OPTION_KEYS } if "execute_in" not in node_opts: raise ValueError( @@ -181,7 +225,7 @@ def __init__( f"'execute_in' in metadata. Set it to 'activity' or " f"'workflow'." ) - opts = {**(default_activity_options or {}), **node_opts} + opts = _merge_activity_opts(default_activity_options, node_opts) # Route all LangGraph node calls through afunc so the async # activity wrapper is always used. wrap_activity handles # sync vs. async user functions inside the activity itself. @@ -208,10 +252,7 @@ def __init__( f"activity_options[{name!r}]. Set it to 'activity' or " f"'workflow'." ) - opts = { - **(default_activity_options or {}), - **task_opts, - } + opts = _merge_activity_opts(default_activity_options, task_opts) task.func = self.execute(task_id(task.func), task.func, opts) task.func.__name__ = name @@ -253,6 +294,18 @@ def execute( """Prepare a node or task to execute as an activity or inline in the workflow.""" opts = kwargs or {} execute_in = opts.pop("execute_in") + # Normalize the node's summary to a single summary_fn. Both keys are + # popped so neither reaches workflow.execute_activity (which takes no + # summary_fn, and would get a duplicate summary kwarg); a static + # summary becomes a summary_fn that ignores its input. + summary_fn = opts.pop("summary_fn", None) + static_summary = opts.pop("summary", None) + if summary_fn is not None and static_summary is not None: + raise ValueError( + f"{activity_name}: set either 'summary' or 'summary_fn', not both." + ) + if static_summary is not None: + summary_fn = _constant_summary_fn(static_summary) if execute_in == "activity": wrapped = wrap_activity( @@ -262,9 +315,13 @@ def execute( ) a = activity.defn(name=activity_name)(wrapped) self.activities.append(a) - return wrap_execute_activity(a, task_id=task_id(func), **opts) + return wrap_execute_activity( + a, task_id=task_id(func), summary_fn=summary_fn, **opts + ) elif execute_in == "workflow": - return wrap_workflow(func, streaming_topic=self._streaming_topic) + return wrap_workflow( + func, streaming_topic=self._streaming_topic, summary_fn=summary_fn + ) else: raise ValueError(f"Invalid execute_in value: {execute_in}") diff --git a/temporalio/contrib/langgraph/_workflow.py b/temporalio/contrib/langgraph/_workflow.py index 67bfd4f68..43b3d06ae 100644 --- a/temporalio/contrib/langgraph/_workflow.py +++ b/temporalio/contrib/langgraph/_workflow.py @@ -20,6 +20,7 @@ def wrap_workflow( func: Callable[..., Any], *, streaming_topic: str | None = None, + summary_fn: Callable[[tuple[Any, ...], dict[str, Any]], str | None] | None = None, ) -> Callable[..., Awaitable[Any]]: """Wrap a function as a workflow-side LangGraph node. @@ -28,9 +29,19 @@ def wrap_workflow( function with the writer installed. Workflow-side nodes publish synchronously to the in-workflow ``WorkflowStream`` (no signal round-trip); activity-side nodes go through ``WorkflowStreamClient``. + + Workflow-side nodes have no activity to carry a summary, so a + ``summary_fn`` result updates the workflow's current details via + :func:`temporalio.workflow.set_current_details` (last-writer-wins); + an empty result clears it. """ async def wrapper(*args: Any, **kwargs: Any) -> Any: + if summary_fn is not None: + # Always write (clearing when empty) so this node never shows a + # stale summary left by an earlier workflow-bound node. + workflow.set_current_details(summary_fn(args, kwargs) or "") + async def run(stream_writer: Callable[[Any], None] | None) -> Any: token = None if stream_writer is not None: diff --git a/temporalio/contrib/openai_agents/README.md b/temporalio/contrib/openai_agents/README.md index 31f668b16..83539044c 100644 --- a/temporalio/contrib/openai_agents/README.md +++ b/temporalio/contrib/openai_agents/README.md @@ -130,11 +130,10 @@ The key to making this work is to separate the applications repeatable (determin Workflow code can run for extended periods and, if interrupted, resume exactly where it left off. Activity code faces no restrictions on I/O or external interactions, but if it fails part-way through it restarts from the beginning. -In the AI-agent example above, model invocations and tool calls run inside activities, while the logic that coordinates them lives in the workflow. +In this integration, model invocations are automatically routed through Temporal activities, while the logic that coordinates them lives in the workflow. +Tools that perform I/O or other non-deterministic work should run as Temporal activities, while deterministic, workflow-safe tools can run directly in the workflow. This pattern generalizes to more sophisticated agents. -We refer to that coordinating logic as _agent orchestration_. - -As a general rule, agent orchestration code executes within the Temporal workflow, whereas model calls and any I/O-bound tool invocations execute as Temporal activities. +We refer to the coordinating logic as _agent orchestration_. The diagram below shows the overall architecture of an agentic application in Temporal. The Temporal Server is responsible to tracking program execution and making sure associated state is preserved reliably (i.e., stored to a database, possibly replicated across cloud regions). @@ -154,13 +153,14 @@ Temporal Server manages data in encrypted form, so all data processing occurs on | Worker | | +----------------------------------------------+ | | | Workflow Code | | -| | (Agent Orchestration Loop) | | +| | (Agent orchestration + deterministic tools) | | | +----------------------------------------------+ | | | | | | | v v v | | +-----------+ +-----------+ +-------------+ | | | Activity | | Activity | | Activity | | -| | (Tool 1) | | (Tool 2) | | (Model API) | | +| | (I/O Tool | | (I/O Tool | | (Model API) | | +| | 1) | | 2) | | | | | +-----------+ +-----------+ +-------------+ | | | | | | +------------------------------------------------------+ @@ -267,10 +267,22 @@ To run this example, see the detailed instructions in the [Temporal Python Sampl ## Tool Calling +Model invocations are automatically routed through Temporal activities. +OpenAI-hosted tools are passed through the model invocation and executed by the model provider. +User-defined `FunctionTool`s, including tools created with `@function_tool`, are not automatically converted into Temporal activities; they execute in the workflow unless explicitly backed by a Temporal activity. +Where a tool executes depends on how it is defined: + +| Tool | Execution | Use for | +| --- | --- | --- | +| `activity_as_tool()` | Temporal activity | External I/O and non-deterministic operations | +| `FunctionTool` / `@function_tool` | Workflow | Deterministic, workflow-safe computation | +| OpenAI-hosted tool | Model provider | Provider-hosted features executed as part of the model invocation | + ### Temporal Activities as OpenAI Agents Tools One of the powerful features of this integration is the ability to convert Temporal activities into agent tools using `activity_as_tool`. This allows your agent to leverage Temporal's durable execution for tool calls. +`activity_as_tool()` creates an OpenAI Agents `FunctionTool` whose invocation schedules the underlying Temporal activity. In the example below, we apply the `@activity.defn` decorator to the `get_weather` function to create a Temporal activity. We then pass this through the `activity_as_tool` helper function to create an OpenAI Agents tool that is passed to the `Agent`. @@ -311,9 +323,23 @@ class WeatherAgent: return result.final_output ``` +The activity must also be registered with a Worker. +`activity_as_tool()` controls how the Agent invokes the activity; it does not register the activity with the Worker. + +```python +from temporalio.worker import Worker + +worker = Worker( + client, + task_queue="my-task-queue", + workflows=[WeatherAgent], + activities=[get_weather], +) +``` + ### Calling OpenAI Agents Tools inside Temporal Workflows -For simple computations that don't involve external calls you can call the tool directly from the workflow by using the standard OpenAI Agents SDK `@functiontool` annotation. +For simple computations that don't involve external calls, you can call the tool directly from the workflow by using the standard OpenAI Agents SDK `@function_tool` decorator. ```python from temporalio import workflow @@ -339,8 +365,10 @@ class MathAssistantAgent: return result.final_output ``` -Note that any tools that run in the workflow must respect the workflow execution restrictions, meaning no I/O or non-deterministic operations. -Of course, code running in the workflow can invoke a Temporal activity at any time. +Use regular `@function_tool` tools only for deterministic, workflow-safe logic. +Do not perform network, database, filesystem, or other external I/O directly from these tools. +Use a Temporal activity with `activity_as_tool()` instead. +Code running in the workflow can also invoke a Temporal activity directly when needed. Tools that run in the workflow can also update OpenAI Agents context, which is read-only for tools run as Temporal activities. @@ -447,6 +475,14 @@ For implementation details and examples, see the [samples repository](https://gi When using stateful servers, the dedicated worker maintaining the connection may fail due to network issues or server problems. When this happens, Temporal raises an `ApplicationError` and cannot automatically recover because it cannot restore the lost server state. To recover from such failures, you need to implement your own application-level retry logic. +### Factory Arguments + +Both `stateless_mcp_server()` and `stateful_mcp_server()` accept an optional `factory_argument`, which is passed to the registered server factory when the MCP server is created. + +A stateless factory that declares no parameters — like the `lambda: MCPServerStdio(...)` example above — ignores the value, but it is still recorded in history. + +**Do not pass secrets, credentials, or API keys through `factory_argument`.** It is an activity argument, so it is recorded in workflow history and, without a payload codec, visible in the web UI. Resolve credentials worker-side inside the server factory instead. + ### Hosted MCP Tool For network-accessible MCP servers, you can also use `HostedMCPTool` from the OpenAI Agents SDK, which uses an MCP client hosted by OpenAI. diff --git a/temporalio/contrib/openai_agents/_mcp.py b/temporalio/contrib/openai_agents/_mcp.py index ba494c42d..8f5294c42 100644 --- a/temporalio/contrib/openai_agents/_mcp.py +++ b/temporalio/contrib/openai_agents/_mcp.py @@ -152,7 +152,8 @@ def __init__( Args: name: The name of the MCP server. server_factory: A function which will produce MCPServer instances. It should return a new server each time - so that state is not shared between workflow runs. + so that state is not shared between workflow runs. It may accept a single positional parameter, which + receives a ``factory_argument`` from the workflow. """ self._server_factory = server_factory @@ -414,7 +415,7 @@ async def get_prompt( class StatefulMCPServerProvider: """A stateful MCP server implementation for Temporal workflows. - This class wraps an function to create MCP servers to maintain a persistent connection throughout + This class wraps a function to create MCP servers to maintain a persistent connection throughout the workflow execution. It creates a dedicated worker that stays connected to the MCP server and processes operations on a dedicated task queue. @@ -437,7 +438,8 @@ def __init__( Args: name: The name of the MCP server. server_factory: A function which will produce MCPServer instances. It should return a new server each time - so that state is not shared between workflow runs + so that state is not shared between workflow runs. It receives an optional ``factory_argument`` from the + workflow. """ self._server_factory = server_factory self._name = name + "-stateful" diff --git a/temporalio/contrib/openai_agents/_openai_runner.py b/temporalio/contrib/openai_agents/_openai_runner.py index 478217c8f..ea2e6e5df 100644 --- a/temporalio/contrib/openai_agents/_openai_runner.py +++ b/temporalio/contrib/openai_agents/_openai_runner.py @@ -99,6 +99,28 @@ def _has_sandbox_agent(agent: Agent[Any], seen: set[int] | None = None) -> bool: return False +def _coerce_run_config(value: object) -> RunConfig: + """openai-agents >= 0.19 also accepts a plain dict for ``run_config``. + + This function normalizes to a RunConfig instance. + """ + if isinstance(value, RunConfig): + return value + if not isinstance(value, dict): + raise TypeError( + f"run_config must be a RunConfig instance or a dict, got {type(value).__name__}" + ) + field_names = { + config_field.name + for config_field in dataclasses.fields(RunConfig) + if config_field.init + } + unknown_fields = sorted(str(name) for name in value if name not in field_names) + if unknown_fields: + raise TypeError(f"Unknown run_config settings: {', '.join(unknown_fields)}") + return RunConfig(**value) + + class TemporalOpenAIRunner(AgentRunner): """Temporal Runner for OpenAI agents. @@ -148,8 +170,9 @@ def _prepare_workflow_run( raise ValueError("Temporal workflows don't support SQLite sessions.") run_config = kwargs.get("run_config") - if run_config is None: - run_config = RunConfig() + run_config = ( + RunConfig() if run_config is None else _coerce_run_config(run_config) + ) if run_config.model and not isinstance(run_config.model, _TemporalModelStub): if not isinstance(run_config.model, str): diff --git a/temporalio/contrib/openai_agents/_temporal_openai_agents.py b/temporalio/contrib/openai_agents/_temporal_openai_agents.py index 43594657f..63e8cb10b 100644 --- a/temporalio/contrib/openai_agents/_temporal_openai_agents.py +++ b/temporalio/contrib/openai_agents/_temporal_openai_agents.py @@ -2,6 +2,7 @@ import dataclasses import json +import threading import typing from collections.abc import AsyncIterator, Callable, Iterator, Sequence from contextlib import asynccontextmanager, contextmanager @@ -54,6 +55,81 @@ ) +_otel_trace_start_patch_lock = threading.RLock() +_otel_trace_start_patch_ref_count = 0 +_otel_trace_start_original: Callable[..., typing.Any] | None = None +_otel_trace_start_instrumentor: typing.Any | None = None + + +def _install_otel_instrumentation(tracer_provider: typing.Any) -> None: + """Configure OpenInference while at least one tracing context is active.""" + global _otel_trace_start_instrumentor + global _otel_trace_start_original + global _otel_trace_start_patch_ref_count + + from openinference.instrumentation.openai_agents import OpenAIAgentsInstrumentor + from openinference.instrumentation.openai_agents._processor import ( + OpenInferenceTracingProcessor, + ) + from opentelemetry.context import attach + from opentelemetry.trace import set_span_in_context + + with _otel_trace_start_patch_lock: + if _otel_trace_start_patch_ref_count == 0: + original_on_trace_start = OpenInferenceTracingProcessor.on_trace_start + _otel_trace_start_original = original_on_trace_start + + def on_trace_start(self: typing.Any, trace: Trace) -> None: # type: ignore[reportUnusedFunction] + original_on_trace_start(self, trace) + attach(set_span_in_context(self._root_spans[trace.trace_id])) + + setattr(OpenInferenceTracingProcessor, "on_trace_start", on_trace_start) + try: + _otel_trace_start_instrumentor = OpenAIAgentsInstrumentor() + _otel_trace_start_instrumentor.instrument( + tracer_provider=tracer_provider + ) + except BaseException: + setattr( + OpenInferenceTracingProcessor, + "on_trace_start", + _otel_trace_start_original, + ) + _otel_trace_start_original = None + _otel_trace_start_instrumentor = None + raise + _otel_trace_start_patch_ref_count += 1 + + +def _uninstall_otel_instrumentation() -> None: + """Tear down OpenInference after the final tracing context exits.""" + global _otel_trace_start_instrumentor + global _otel_trace_start_original + global _otel_trace_start_patch_ref_count + + from openinference.instrumentation.openai_agents._processor import ( + OpenInferenceTracingProcessor, + ) + + with _otel_trace_start_patch_lock: + if _otel_trace_start_patch_ref_count == 0: + raise RuntimeError("OpenInference instrumentation was not acquired") + _otel_trace_start_patch_ref_count -= 1 + if _otel_trace_start_patch_ref_count == 0: + try: + if _otel_trace_start_instrumentor is not None: + _otel_trace_start_instrumentor.uninstrument() + finally: + if _otel_trace_start_original is not None: + setattr( + OpenInferenceTracingProcessor, + "on_trace_start", + _otel_trace_start_original, + ) + _otel_trace_start_original = None + _otel_trace_start_instrumentor = None + + @contextmanager def _set_open_ai_agent_temporal_overrides( model_params: ModelActivityParameters, @@ -264,8 +340,6 @@ def __init__( "When configuring a custom provider, the model activity must have start_to_close_timeout or schedule_to_close_timeout" ) - # Store OTEL configuration for later setup - self._instrumented = False self._use_otel_instrumentation = use_otel_instrumentation # Delay activity construction until they are actually needed @@ -375,36 +449,16 @@ def tracing_context(self) -> Iterator[None]: Yields: Context with tracing instrumentation enabled. """ - # Set up OTEL instrumentation if exporters are provided - otel_instrumentor = None - if self._use_otel_instrumentation and not self._instrumented: - from openinference.instrumentation.openai_agents import ( - OpenAIAgentsInstrumentor, - ) - from openinference.instrumentation.openai_agents._processor import ( - OpenInferenceTracingProcessor, - ) + # Set up OTEL instrumentation if enabled + otel_instrumentation_installed = False + if self._use_otel_instrumentation: from opentelemetry import trace - from opentelemetry.context import attach - from opentelemetry.trace import set_span_in_context - - # Unfortunate monkey patching is needed to ensure the trace is set in context so we can propagate it. - original_on_trace_start = OpenInferenceTracingProcessor.on_trace_start - - def on_trace_start(self, trace: Trace) -> None: # type: ignore[reportMissingParameterType] - original_on_trace_start(self, trace) - otel_span = self._root_spans[trace.trace_id] - attach(set_span_in_context(otel_span)) - - OpenInferenceTracingProcessor.on_trace_start = on_trace_start # type:ignore[method-assign] - # Set up instrumentor - otel_instrumentor = OpenAIAgentsInstrumentor() - otel_instrumentor.instrument(tracer_provider=trace.get_tracer_provider()) - self._instrumented = True + _install_otel_instrumentation(trace.get_tracer_provider()) + otel_instrumentation_installed = True try: yield finally: # Clean up OTEL instrumentation - if otel_instrumentor is not None: - otel_instrumentor.uninstrument() + if otel_instrumentation_installed: + _uninstall_otel_instrumentation() diff --git a/temporalio/contrib/openai_agents/testing.py b/temporalio/contrib/openai_agents/testing.py index d4641105c..110ca20b7 100644 --- a/temporalio/contrib/openai_agents/testing.py +++ b/temporalio/contrib/openai_agents/testing.py @@ -90,7 +90,7 @@ def output_message(text: str) -> ModelResponse: class TestModelProvider(ModelProvider): - """Test model provider which simply returns the given module.""" + """Test model provider which simply returns the given model.""" __test__ = False diff --git a/temporalio/contrib/openai_agents/workflow.py b/temporalio/contrib/openai_agents/workflow.py index b37a82bdc..d99028d68 100644 --- a/temporalio/contrib/openai_agents/workflow.py +++ b/temporalio/contrib/openai_agents/workflow.py @@ -291,12 +291,19 @@ def stateless_mcp_server( and you don't need to maintain state between operations. It should be preferred to stateful when possible due to its superior durability guarantees. + .. warning:: + Do not pass secrets, credentials, or API keys through ``factory_argument``. It is an + activity argument, so it is recorded in workflow history and, without a payload codec, + visible in the web UI. Resolve credentials worker-side inside the server factory + instead. + Args: name: A string name for the server. Should match that provided in the plugin. config: Optional activity configuration for MCP operation activities. Defaults to 1-minute start-to-close timeout. cache_tools_list: If true, the list of tools will be cached for the duration of the server - factory_argument: Optional argument to be provided to the factory when producing an MCPServer + factory_argument: Optional argument to be provided to the factory when producing an MCPServer. + Must not contain secrets. """ from temporalio.contrib.openai_agents._mcp import ( _StatelessMCPServerReference, @@ -326,13 +333,20 @@ def stateful_mcp_server( The caller will have to handle cases where the dedicated worker fails, as Temporal is unable to seamlessly recreate any lost state in that case. + .. warning:: + Do not pass secrets, credentials, or API keys through ``factory_argument``. It is an + activity argument, so it is recorded in workflow history and, without a payload codec, + visible in the web UI. Resolve credentials worker-side inside the server factory + instead. + Args: name: A string name for the server. Should match that provided in the plugin. config: Optional activity configuration for MCP operation activities. Defaults to 1-minute start-to-close and 30-second schedule-to-start timeouts. server_session_config: Optional activity configuration for the connection activity. Defaults to 1-hour start-to-close timeout. - factory_argument: Optional argument to be provided to the factory when producing an MCPServer + factory_argument: Optional argument to be provided to the factory when producing an MCPServer. + Must not contain secrets. """ from temporalio.contrib.openai_agents._mcp import ( _StatefulMCPServerReference, diff --git a/temporalio/contrib/opentelemetry/README.md b/temporalio/contrib/opentelemetry/README.md index 9f1e1303b..2c6e39817 100644 --- a/temporalio/contrib/opentelemetry/README.md +++ b/temporalio/contrib/opentelemetry/README.md @@ -68,7 +68,7 @@ worker = Worker( - **Accurate Duration Spans**: Workflow spans have real durations reflecting actual execution time - **Direct OpenTelemetry Usage**: Use `opentelemetry.trace.get_tracer()` directly within workflows - **Better Span Hierarchy**: More accurate parent-child relationships within workflows -- **Workflow Context Access**: Access spans within workflows using `temporalio.contrib.opentelemetry.workflow.tracer()` +- **Workflow Context Access**: Access spans within workflows using `opentelemetry.trace.get_tracer()` #### ⚠️ Considerations: - **Experimental Status**: Subject to breaking changes in future versions diff --git a/temporalio/contrib/opentelemetry/_plugin.py b/temporalio/contrib/opentelemetry/_plugin.py index 80a17de52..2537c1776 100644 --- a/temporalio/contrib/opentelemetry/_plugin.py +++ b/temporalio/contrib/opentelemetry/_plugin.py @@ -18,7 +18,7 @@ class OpenTelemetryPlugin(SimplePlugin): It uses the new OpenTelemetryInterceptor implementation. Unlike the prior TracingInterceptor, this allows for accurate duration spans and parenting inside a workflow - with temporalio.contrib.opentelemetry.workflow.tracer() + using opentelemetry.trace.get_tracer() directly. Your tracer provider should be created with `create_tracer_provider` for it to be used within a Temporal worker. """ @@ -34,7 +34,9 @@ def __init__(self, *, add_temporal_spans: bool = False): def workflow_runner(runner: WorkflowRunner | None) -> WorkflowRunner: if not runner: - raise ValueError("No WorkflowRunner provided to the OpenAI plugin.") + raise ValueError( + "No WorkflowRunner provided to the OpenTelemetry plugin." + ) # If in sandbox, add additional passthrough if isinstance(runner, SandboxedWorkflowRunner): diff --git a/temporalio/contrib/opentelemetry/workflow.py b/temporalio/contrib/opentelemetry/workflow.py index 299e72b24..e872979a4 100644 --- a/temporalio/contrib/opentelemetry/workflow.py +++ b/temporalio/contrib/opentelemetry/workflow.py @@ -30,7 +30,7 @@ def completed_span( span and this interceptor is configured on the worker and the span is on the context). - To create a long-running span or to create a span that actually spans other code use OpenTelemetryPlugin and tracer(). + To create a long-running span or to create a span that actually spans other code use OpenTelemetryPlugin and opentelemetry.trace.get_tracer(). Args: name: Name of the span. diff --git a/temporalio/contrib/pydantic.py b/temporalio/contrib/pydantic.py index c5f2deb41..dd5d0e67a 100644 --- a/temporalio/contrib/pydantic.py +++ b/temporalio/contrib/pydantic.py @@ -13,6 +13,7 @@ Pydantic v1 is not supported. """ +import functools from dataclasses import dataclass from typing import Any @@ -53,10 +54,28 @@ class PydanticJSONPlainPayloadConverter(EncodingPayloadConverter): See https://docs.pydantic.dev/latest/api/standard_library_types/ """ - def __init__(self, to_json_options: ToJsonOptions | None = None): - """Create a new payload converter.""" + def __init__( + self, + to_json_options: ToJsonOptions | None = None, + *, + max_cached_type_adapters: int | None = 1024, + ) -> None: + """Create a new payload converter. + + Args: + to_json_options: Options for serializing values to JSON. + max_cached_type_adapters: Maximum number of type adapters to + cache, with least-recently-used eviction. Defaults to 1024. + If ``None``, the cache is unbounded. If zero, caching is + disabled. + """ + if max_cached_type_adapters is not None and max_cached_type_adapters < 0: + raise ValueError("max_cached_type_adapters cannot be negative") self._schema_serializer = SchemaSerializer(any_schema()) self._to_json_options = to_json_options + self._type_adapter = functools.lru_cache(maxsize=max_cached_type_adapters)( + TypeAdapter + ) @property def encoding(self) -> str: @@ -91,12 +110,26 @@ def from_payload( Uses ``pydantic.TypeAdapter.validate_json`` to construct an instance of the type specified by ``type_hint`` from the JSON payload. + Type adapters are cached per hashable type hint; see + ``max_cached_type_adapters`` on the constructor. See https://docs.pydantic.dev/latest/api/type_adapter/#pydantic.type_adapter.TypeAdapter.validate_json. """ _type_hint = type_hint if type_hint is not None else Any - return TypeAdapter(_type_hint).validate_json(payload.data) + type_adapter: TypeAdapter[Any] + try: + type_adapter = self._type_adapter(_type_hint) + except TypeError: + # Distinguish an unhashable hint (bypass the cache) from a + # TypeError raised while constructing the adapter (re-raise). + try: + hash(_type_hint) + except TypeError: + type_adapter = TypeAdapter(_type_hint) + else: + raise + return type_adapter.validate_json(payload.data) class PydanticPayloadConverter(CompositePayloadConverter): @@ -106,9 +139,36 @@ class PydanticPayloadConverter(CompositePayloadConverter): :py:class:`PydanticJSONPlainPayloadConverter`. """ - def __init__(self, to_json_options: ToJsonOptions | None = None) -> None: - """Initialize object""" - json_payload_converter = PydanticJSONPlainPayloadConverter(to_json_options) + def __init__( + self, + to_json_options: ToJsonOptions | None = None, + *, + max_cached_type_adapters: int | None = 1024, + ) -> None: + """Initialize object. + + Args: + to_json_options: Options for serializing values to JSON. + max_cached_type_adapters: Maximum number of type adapters to + cache, with least-recently-used eviction. Defaults to 1024. + If ``None``, the cache is unbounded. If zero, caching is + disabled. + + To configure this through a :py:class:`DataConverter`, use a + nullary subclass as the payload converter class:: + + class MyPayloadConverter(PydanticPayloadConverter): + def __init__(self) -> None: + super().__init__(max_cached_type_adapters=128) + + my_data_converter = DataConverter( + payload_converter_class=MyPayloadConverter + ) + """ + json_payload_converter = PydanticJSONPlainPayloadConverter( + to_json_options, + max_cached_type_adapters=max_cached_type_adapters, + ) super().__init__( *( c diff --git a/temporalio/converter/__init__.py b/temporalio/converter/__init__.py index 3821cbd68..ebd2b8396 100644 --- a/temporalio/converter/__init__.py +++ b/temporalio/converter/__init__.py @@ -33,12 +33,10 @@ JSONTypeConverter, JSONTypeConverterUnhandled, PayloadConverter, + TransferTypeConverter, + transfer_type_convertible, value_to_type, ) -from temporalio.converter._payload_limits import ( - PayloadLimitsConfig, - PayloadSizeWarning, -) from temporalio.converter._search_attributes import ( decode_search_attributes, decode_typed_search_attributes, @@ -68,6 +66,7 @@ "BinaryPlainPayloadConverter", "BinaryProtoPayloadConverter", "CompositePayloadConverter", + "TransferTypeConverter", "DataConverter", "DefaultFailureConverter", "DefaultFailureConverterWithEncodedAttributes", @@ -80,11 +79,10 @@ "JSONTypeConverterUnhandled", "PayloadCodec", "PayloadConverter", - "PayloadLimitsConfig", - "PayloadSizeWarning", "SerializationContext", "WithSerializationContext", "WorkflowSerializationContext", + "transfer_type_convertible", "decode_search_attributes", "decode_typed_search_attributes", "default", diff --git a/temporalio/converter/_data_converter.py b/temporalio/converter/_data_converter.py index 13b48e695..8604ea196 100644 --- a/temporalio/converter/_data_converter.py +++ b/temporalio/converter/_data_converter.py @@ -3,7 +3,6 @@ from __future__ import annotations import dataclasses -import warnings from collections.abc import Mapping, Sequence from dataclasses import dataclass from logging import getLogger @@ -14,9 +13,9 @@ import temporalio.api.common.v1 import temporalio.api.failure.v1 import temporalio.common -from temporalio.api.sdk.v1.external_storage_pb2 import ExternalStorageReference from temporalio.converter._extstore import ( _REFERENCE_ENCODING, + _REFERENCE_MESSAGE_TYPE, ExternalStorage, StorageDriverStoreContext, ) @@ -29,20 +28,13 @@ ) from temporalio.converter._payload_converter import ( PayloadConverter, -) -from temporalio.converter._payload_limits import ( - PayloadLimitsConfig, - PayloadSizeWarning, - _PayloadSizeError, - _ServerPayloadErrorLimits, + _TemporalTransferTypePayloadConverter, ) from temporalio.converter._serialization_context import ( SerializationContext, WithSerializationContext, ) -_REFERENCE_MESSAGE_TYPE = ExternalStorageReference.DESCRIPTOR.full_name.encode() - def _is_reference_payload(p: temporalio.api.common.v1.Payload) -> bool: """Return True if *p* is an external-storage reference payload.""" @@ -86,9 +78,6 @@ class DataConverter(WithSerializationContext): failure_converter: FailureConverter = dataclasses.field(init=False) """Failure converter created from the :py:attr:`failure_converter_class`.""" - payload_limits: PayloadLimitsConfig = PayloadLimitsConfig() - """Settings for payload size limits.""" - external_storage: ExternalStorage | None = None """Options for external storage. If None, external storage is disabled. @@ -99,13 +88,16 @@ class DataConverter(WithSerializationContext): default: ClassVar[DataConverter] """Singleton default data converter.""" - _payload_error_limits: _ServerPayloadErrorLimits | None = None - """Server-reported limits for payloads.""" - def __post_init__(self) -> None: # noqa: D105 - object.__setattr__(self, "payload_converter", self.payload_converter_class()) + object.__setattr__(self, "payload_converter", self._new_payload_converter()) object.__setattr__(self, "failure_converter", self.failure_converter_class()) + def _new_payload_converter(self) -> PayloadConverter: + """Create a payload converter instance with SDK transfer type hooks enabled.""" + return _TemporalTransferTypePayloadConverter.wrap( + self.payload_converter_class() + ) + async def encode( self, values: Sequence[Any] ) -> list[temporalio.api.common.v1.Payload]: @@ -124,7 +116,6 @@ async def encode( payloads = self.payload_converter.to_payloads(values) payloads = await self._encode_payload_sequence(payloads) payloads = await self._external_store_payload_sequence(payloads) - self._validate_payload_limits(payloads) return payloads async def decode( @@ -230,11 +221,6 @@ def _with_contexts( """Return an instance with both serialization and store contexts applied.""" return self.with_context(serialization_ctx)._with_store_context(store_ctx) - def _with_payload_error_limits( - self, limits: _ServerPayloadErrorLimits | None - ) -> DataConverter: - return dataclasses.replace(self, _payload_error_limits=limits) - async def _decode_memo( self, source: temporalio.api.common.v1.Memo, @@ -273,16 +259,6 @@ async def _encode_memo_existing( if not isinstance(v, temporalio.api.common.v1.Payload): payload = (await self.encode([v]))[0] memo.fields[k].CopyFrom(payload) - # Memos have their field payloads validated all together in one unit - DataConverter._validate_limits( - list(memo.fields.values()), - self._payload_error_limits.memo_size_error - if self._payload_error_limits - else None, - "[TMPRL1103] Attempted to upload memo with size that exceeded the error limit.", - self.payload_limits.memo_size_warning, - "[TMPRL1103] Attempted to upload memo with size that exceeded the warning limit.", - ) async def _transform_outbound_payload( self, payload: temporalio.api.common.v1.Payload @@ -291,7 +267,6 @@ async def _transform_outbound_payload( payload = (await self.payload_codec.encode([payload]))[0] if self.external_storage: payload = await self.external_storage._store_payload(payload) - self._validate_payload_limits([payload]) return payload async def _transform_outbound_payloads( @@ -301,7 +276,6 @@ async def _transform_outbound_payloads( await self.payload_codec.encode_wrapper(payloads) if self.external_storage: await self.external_storage._store_payloads(payloads) - self._validate_payload_limits(payloads.payloads) async def _transform_inbound_payload( self, payload: temporalio.api.common.v1.Payload @@ -376,42 +350,6 @@ async def _decode_payload_sequence( def _decode_payload_has_effect(self) -> bool: return self.payload_codec is not None or self.external_storage is not None - def _validate_payload_limits( - self, - payloads: Sequence[temporalio.api.common.v1.Payload], - ): - DataConverter._validate_limits( - payloads, - self._payload_error_limits.payload_size_error - if self._payload_error_limits - else None, - "[TMPRL1103] Attempted to upload payloads with size that exceeded the error limit.", - self.payload_limits.payload_size_warning, - "[TMPRL1103] Attempted to upload payloads with size that exceeded the warning limit.", - ) - - @staticmethod - def _validate_limits( - payloads: Sequence[temporalio.api.common.v1.Payload], - error_limit: int | None, - error_message: str, - warning_limit: int, - warning_message: str, - ): - total_size = sum(payload.ByteSize() for payload in payloads) - - if error_limit and error_limit > 0 and total_size > error_limit: - raise _PayloadSizeError( - f"{error_message} Size: {total_size} bytes, Limit: {error_limit} bytes" - ) - - if warning_limit > 0 and total_size > warning_limit: - # TODO: Use a context aware logger to log extra information about workflow/activity/etc - warnings.warn( - f"{warning_message} Size: {total_size} bytes, Limit: {warning_limit} bytes", - PayloadSizeWarning, - ) - def default() -> DataConverter: """Default data converter. diff --git a/temporalio/converter/_extstore.py b/temporalio/converter/_extstore.py index c31424acf..a946b2c0f 100644 --- a/temporalio/converter/_extstore.py +++ b/temporalio/converter/_extstore.py @@ -27,6 +27,7 @@ _T = TypeVar("_T") _REFERENCE_ENCODING = b"json/external-storage-reference" +_REFERENCE_MESSAGE_TYPE = ExternalStorageReference.DESCRIPTOR.full_name.encode() @dataclass @@ -455,8 +456,6 @@ async def _store_payload_sequence( def _decode_reference(self, payload: Payload) -> ExternalStorageReference | None: """Decode an external storage reference from a payload.""" - if len(payload.external_payloads) == 0: - return None encoding = payload.metadata.get("encoding", b"") if encoding == _REFERENCE_ENCODING: legacy = self._legacy_claim_converter.from_payload( @@ -468,6 +467,11 @@ def _decode_reference(self, payload: Payload) -> ExternalStorageReference | None driver_name=legacy.driver_name, claim_data=legacy.driver_claim.claim_data, ) + if not ( + encoding == b"json/protobuf" + and payload.metadata.get("messageType") == _REFERENCE_MESSAGE_TYPE + ): + return None ref = self._claim_converter.from_payload(payload, ExternalStorageReference) return ref if isinstance(ref, ExternalStorageReference) else None diff --git a/temporalio/converter/_failure_converter.py b/temporalio/converter/_failure_converter.py index c76f95c23..848dbc038 100644 --- a/temporalio/converter/_failure_converter.py +++ b/temporalio/converter/_failure_converter.py @@ -17,7 +17,6 @@ import temporalio.api.failure.v1 import temporalio.exceptions from temporalio.converter._payload_converter import PayloadConverter -from temporalio.converter._payload_limits import _PayloadSizeError logger = getLogger("temporalio.converter") @@ -108,9 +107,7 @@ def to_failure( # Convert to failure error failure_error = temporalio.exceptions.ApplicationError( str(exception), - type="PayloadSizeError" - if isinstance(exception, _PayloadSizeError) - else exception.__class__.__name__, + type=exception.__class__.__name__, ) failure_error.__traceback__ = exception.__traceback__ failure_error.__cause__ = exception.__cause__ diff --git a/temporalio/converter/_payload_converter.py b/temporalio/converter/_payload_converter.py index 8ee85ef72..a8bc35e28 100644 --- a/temporalio/converter/_payload_converter.py +++ b/temporalio/converter/_payload_converter.py @@ -21,6 +21,7 @@ from typing import ( Any, ClassVar, + Generic, Literal, NewType, TypeVar, @@ -51,6 +52,80 @@ ) _sym_db = google.protobuf.symbol_database.Default() +ValueT = TypeVar("ValueT") +TransferTypeT = TypeVar("TransferTypeT") +_TRANSFER_TYPE_CONVERTER_ATTR = "__temporal_transfer_type_converter" + + +class TransferTypeConverter(Generic[ValueT, TransferTypeT], ABC): + """Converter between a user-facing value and a transfer type value. + + .. warning:: + This API is experimental and subject to change. + """ + + transfer_type: type[TransferTypeT] | None = None + """Optional type hint for the transfer type to use when decoding payloads. + + .. warning:: + This API is experimental and subject to change. + """ + + @abstractmethod + def to_transfer_type(self, value: ValueT) -> TransferTypeT: + """Convert a user-facing value to its transfer type value. + + .. warning:: + This API is experimental and subject to change. + """ + raise NotImplementedError + + @abstractmethod + def from_transfer_type( + self, value: TransferTypeT, type_hint: type[ValueT] + ) -> ValueT: + """Convert a transfer type value to its user-facing value. + + ``type_hint`` is the requested user-facing type, including concrete + generic arguments. + + .. warning:: + This API is experimental and subject to change. + """ + raise NotImplementedError + + +class _TransferTypeConvertibleDecorator(Generic[ValueT, TransferTypeT]): + def __init__( + self, converter_type: type[TransferTypeConverter[ValueT, TransferTypeT]] + ) -> None: + self._converter_type = converter_type + + def __call__(self, cls: type[ValueT]) -> type[ValueT]: + if hasattr(cls, _TRANSFER_TYPE_CONVERTER_ATTR): + raise TypeError("class already has a transfer type converter") + setattr(cls, _TRANSFER_TYPE_CONVERTER_ATTR, self._converter_type()) + return cls + + +def transfer_type_convertible( + converter_type: type[TransferTypeConverter[ValueT, TransferTypeT]], +) -> _TransferTypeConvertibleDecorator[ValueT, TransferTypeT]: + """Decorate a class with a transfer type converter class. + + .. warning:: + This API is experimental and subject to change. + """ + return _TransferTypeConvertibleDecorator(converter_type) + + +def _get_transfer_type_converter( + value_type: object, +) -> TransferTypeConverter[Any, Any] | None: + converter = getattr(value_type, _TRANSFER_TYPE_CONVERTER_ATTR, None) + if isinstance(converter, TransferTypeConverter): + return converter + return None class PayloadConverter(ABC): @@ -514,6 +589,76 @@ def from_payload( raise RuntimeError("Failed parsing") from err +class _TemporalTransferTypePayloadConverter(PayloadConverter, WithSerializationContext): + """Payload converter wrapper for registered Temporal transfer type converters. + + Values with a registered transfer type converter are first converted to their + transfer type value, then encoded by the wrapped payload converter. When + decoding to a type with a registered transfer type converter, the wrapped + converter first decodes the payload to the transfer type value and this wrapper + constructs the requested user-facing type from it. + """ + + _inner_payload_converter: PayloadConverter + + def __init__(self, inner_payload_converter: PayloadConverter) -> None: + """Create a Temporal transfer type payload converter.""" + self._inner_payload_converter = inner_payload_converter + + @staticmethod + def wrap(payload_converter: PayloadConverter) -> PayloadConverter: + """Wrap a payload converter unless it is already wrapped.""" + if isinstance(payload_converter, _TemporalTransferTypePayloadConverter): + return payload_converter + return _TemporalTransferTypePayloadConverter(payload_converter) + + def to_payloads( + self, values: Sequence[Any] + ) -> list[temporalio.api.common.v1.Payload]: + """See base class.""" + transfer_type_values: list[Any] = [] + for value in values: + converter = _get_transfer_type_converter(type(value)) + if converter is not None: + value = converter.to_transfer_type(value) + transfer_type_values.append(value) + return self._inner_payload_converter.to_payloads(transfer_type_values) + + def from_payloads( + self, + payloads: Sequence[temporalio.api.common.v1.Payload], + type_hints: list[type] | None = None, + ) -> list[Any]: + """See base class.""" + if type_hints is None: + return self._inner_payload_converter.from_payloads(payloads, None) + converters = [ + _get_transfer_type_converter(type_hint) for type_hint in type_hints + ] + inner_type_hints = [ + converter.transfer_type if converter is not None else type_hint + for converter, type_hint in zip(converters, type_hints) + ] + values = self._inner_payload_converter.from_payloads( + payloads, typing.cast("list[type]", inner_type_hints) + ) + return [ + converter.from_transfer_type(value, type_hint) + if converter is not None + else value + for value, converter, type_hint in zip(values, converters, type_hints) + ] + + def with_context(self, context: SerializationContext) -> Self: + """Return a new instance with context set on the inner converter.""" + if not isinstance(self._inner_payload_converter, WithSerializationContext): + return self + inner_payload_converter = self._inner_payload_converter.with_context(context) + if inner_payload_converter is self._inner_payload_converter: + return self + return type(self)(inner_payload_converter) + + class AdvancedJSONEncoder(json.JSONEncoder): """Advanced JSON encoder. diff --git a/temporalio/converter/_payload_limits.py b/temporalio/converter/_payload_limits.py deleted file mode 100644 index d6eb0b1d2..000000000 --- a/temporalio/converter/_payload_limits.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Payload size limit configuration and related types.""" - -from __future__ import annotations - -from dataclasses import dataclass - -import temporalio.exceptions - - -@dataclass(frozen=True) -class PayloadLimitsConfig: - """Configuration for when payload sizes exceed limits.""" - - memo_size_warning: int = 2 * 1024 - """The limit (in bytes) at which a memo size warning is logged.""" - - payload_size_warning: int = 512 * 1024 - """The limit (in bytes) at which a payload size warning is logged.""" - - -class PayloadSizeWarning(RuntimeWarning): - """The size of payloads is above the warning limit.""" - - -class _PayloadSizeError(temporalio.exceptions.TemporalError): # type:ignore[reportUnusedClass] - """Error raised when payloads size exceeds payload size limits.""" - - def __init__(self, message: str): - """Initialize a payloads size error.""" - super().__init__(message) - self._message = message - - @property - def message(self) -> str: - """Message.""" - return self._message - - -@dataclass(frozen=True) -class _ServerPayloadErrorLimits: # type:ignore[reportUnusedClass] - """Error limits for payloads as described by the Temporal server.""" - - memo_size_error: int - """The limit (in bytes) at which a memo size error is raised.""" - - payload_size_error: int - """The limit (in bytes) at which a payload size error is raised.""" diff --git a/temporalio/nexus/__init__.py b/temporalio/nexus/__init__.py index 402e4b04e..3abc9b0f2 100644 --- a/temporalio/nexus/__init__.py +++ b/temporalio/nexus/__init__.py @@ -25,6 +25,8 @@ wait_for_worker_shutdown_sync, ) from ._operation_handlers import ( + CancelActivityOptions, + CancelUpdateWorkflowOptions, CancelWorkflowRunOptions, TemporalOperationHandler, ) @@ -33,7 +35,9 @@ __all__ = ( "workflow_run_operation", + "CancelActivityOptions", "CancelWorkflowRunOptions", + "CancelUpdateWorkflowOptions", "Info", "LoggerAdapter", "NexusCallback", diff --git a/temporalio/nexus/_link_conversion.py b/temporalio/nexus/_link_conversion.py index 54ed3869a..e3ef3988b 100644 --- a/temporalio/nexus/_link_conversion.py +++ b/temporalio/nexus/_link_conversion.py @@ -23,6 +23,10 @@ r"^/namespaces/(?P[^/]+)/nexus-operations/(?P[^/]+)/(?P[^/]*)/details$" ) +_ACTIVITY_LINK_URL_PATH_REGEX = re.compile( + r"^/namespaces/(?P[^/]+)/activities/(?P[^/]+)/(?P[^/]+)/details$" +) + _WORKFLOW_LINK_URL_PATH_REGEX = re.compile( r"^/namespaces/(?P[^/]+)/workflows/(?P[^/]+)/(?P[^/]+)(?P/history)?$" ) @@ -32,6 +36,7 @@ class _LinkType(str, Enum): WORKFLOW_EVENT = temporalio.api.common.v1.Link.WorkflowEvent.DESCRIPTOR.full_name WORKFLOW = temporalio.api.common.v1.Link.Workflow.DESCRIPTOR.full_name NEXUS_OPERATION = temporalio.api.common.v1.Link.NexusOperation.DESCRIPTOR.full_name + ACTIVITY = temporalio.api.common.v1.Link.Activity.DESCRIPTOR.full_name LINK_EVENT_ID_PARAM_NAME = "eventID" @@ -88,6 +93,9 @@ def nexus_link_to_temporal_link( case _LinkType.NEXUS_OPERATION: return nexus_link_to_nexus_operation_link(nexus_link) + case _LinkType.ACTIVITY: + return nexus_link_to_activity_link(nexus_link) + def temporal_link_to_nexus_link( temporal_link: temporalio.api.common.v1.Link, @@ -106,10 +114,11 @@ def temporal_link_to_nexus_link( case "nexus_operation": return nexus_operation_to_nexus_link(temporal_link.nexus_operation) - case "activity" | "batch_job": - raise NotImplementedError( - "only workflow_event and nexus operation links are supported" - ) + case "activity": + return activity_link_to_nexus_link(temporal_link.activity) + + case "batch_job": + raise NotImplementedError("batch_job links are not supported") case None: logger.warning("Invalid Temporal link: missing variant") @@ -190,6 +199,17 @@ def nexus_operation_to_nexus_link( ) +def activity_link_to_nexus_link( + activity: temporalio.api.common.v1.Link.Activity, +) -> nexusrpc.Link: + """Convert an Activity link into a nexusrpc link.""" + namespace = urllib.parse.quote(activity.namespace, safe="") + activity_id = urllib.parse.quote(activity.activity_id, safe="") + run_id = urllib.parse.quote(activity.run_id, safe="") + path = f"/namespaces/{namespace}/activities/{activity_id}/{run_id}/details" + return nexusrpc.Link(url=_temporal_nexus_url(path), type=_LinkType.ACTIVITY.value) + + def _workflow_nexus_url( namespace: str, workflow_id: str, @@ -334,6 +354,28 @@ def nexus_link_to_nexus_operation_link( return temporalio.api.common.v1.Link(nexus_operation=nexus_op_link) +def nexus_link_to_activity_link( + nexus_link: nexusrpc.Link, +) -> temporalio.api.common.v1.Link | None: + """Convert a Nexus Activity link into a Temporal Activity link.""" + match = _ACTIVITY_LINK_URL_PATH_REGEX.match( + urllib.parse.urlparse(nexus_link.url).path + ) + if not match: + logger.warning( + f"Invalid Nexus link: {nexus_link}. Expected path to match {_ACTIVITY_LINK_URL_PATH_REGEX.pattern}" + ) + return None + groups = match.groupdict() + return temporalio.api.common.v1.Link( + activity=temporalio.api.common.v1.Link.Activity( + namespace=urllib.parse.unquote(groups["namespace"]), + activity_id=urllib.parse.unquote(groups["activity_id"]), + run_id=urllib.parse.unquote(groups["run_id"]), + ) + ) + + def _event_reference_to_query_params( event_ref: temporalio.api.common.v1.Link.WorkflowEvent.EventReference, ) -> str: diff --git a/temporalio/nexus/_operation_context.py b/temporalio/nexus/_operation_context.py index 1128d1b71..54f8a987d 100644 --- a/temporalio/nexus/_operation_context.py +++ b/temporalio/nexus/_operation_context.py @@ -46,7 +46,6 @@ from ._link_conversion import ( nexus_link_to_temporal_link, temporal_link_to_nexus_link, - workflow_event_to_nexus_link, workflow_execution_started_event_link_from_workflow_handle, ) from ._token import OperationToken, OperationTokenType, WorkflowHandle @@ -66,12 +65,12 @@ ContextVar("temporal-cancel-operation-context") ) -# A Nexus start handler might start zero or more workflows as usual using a Temporal client. In -# addition, it may start one "nexus-backing" workflow, using -# WorkflowRunOperationContext.start_workflow. This context is active while the latter is being done. +# A Nexus start handler might start zero or more async Temporal actions as usual using a Temporal client. In +# addition, it may start one "nexus-backing" async Temporal action, using +# WorkflowRunOperationContext.start_workflow or methods from TemporalNexusClient. This context is active while the latter is being done. # It is thus a narrower context than _temporal_start_operation_context. -_temporal_nexus_backing_workflow_start_context: ContextVar[bool] = ContextVar( - "temporal-nexus-backing-workflow-start-context" +_temporal_nexus_backing_start_context: ContextVar[bool] = ContextVar( + "temporal-nexus-backing-start-context" ) @@ -170,21 +169,21 @@ def _try_temporal_context() -> ( def _try_start_operation_context() -> _TemporalStartOperationContext | None: # pyright: ignore[reportUnusedFunction] - """The Nexus start-operation context if a handler is currently running, else None.""" + """Return the active Nexus start-operation context, if any.""" return _temporal_start_operation_context.get(None) @contextmanager -def _nexus_backing_workflow_start_context() -> Generator[None]: - token = _temporal_nexus_backing_workflow_start_context.set(True) +def _nexus_backing_start_context() -> Generator[None]: + token = _temporal_nexus_backing_start_context.set(True) try: yield finally: - _temporal_nexus_backing_workflow_start_context.reset(token) + _temporal_nexus_backing_start_context.reset(token) -def _in_nexus_backing_workflow_start_context() -> bool: # type:ignore[reportUnusedClass] - return _temporal_nexus_backing_workflow_start_context.get(False) +def _in_nexus_backing_start_context() -> bool: # type:ignore[reportUnusedClass] + return _temporal_nexus_backing_start_context.get(False) _OperationCtxT = TypeVar("_OperationCtxT", bound=OperationContext) @@ -254,11 +253,11 @@ def _get_request_links(self) -> list[temporalio.api.common.v1.Link]: ``links`` field so the callee's history event links back to whatever scheduled this Nexus operation. """ - event_links: list[temporalio.api.common.v1.Link] = [] + links: list[temporalio.api.common.v1.Link] = [] for inbound_link in self.nexus_context.inbound_links: if link := nexus_link_to_temporal_link(inbound_link): - event_links.append(link) - return event_links + links.append(link) + return links def _add_start_workflow_response_link( self, workflow_handle: temporalio.client.WorkflowHandle[Any, Any] @@ -302,17 +301,17 @@ def _add_response_link(self, link: temporalio.api.common.v1.Link | None) -> None """Append a response link returned by an RPC the operation handler issued. ``link`` is the ``common.v1.Link`` returned on a signal, signal-with-start, or start - response (or ``None`` against a server that did not return one). When present and of the - ``workflow_event`` variant, it is converted to a Nexus link and added to the operation's - outbound links so the caller workflow's Nexus history event links to the callee event. + response (or ``None`` against a server that did not return one). When present, it is + converted to a Nexus link and added to the operation's outbound links. This is only safe to call from the single thread/task that runs the operation handler. """ - if link is None or not link.HasField("workflow_event"): - return - self.nexus_context.outbound_links.append( - workflow_event_to_nexus_link(link.workflow_event) - ) + if link is not None: + try: + if response_link := temporal_link_to_nexus_link(link): + self.nexus_context.outbound_links.append(response_link) + except Exception as e: + logger.warning(f"Failed to create Nexus link from Temporal link: {e}") class WorkflowRunOperationContext(StartOperationContext): @@ -668,7 +667,7 @@ async def _start_nexus_backing_workflow( priority: temporalio.common.Priority = temporalio.common.Priority.default, versioning_override: temporalio.common.VersioningOverride | None = None, ) -> WorkflowHandle[ReturnType]: - # We must pass nexus_completion_callbacks, workflow_event_links, and request_id, + # We must pass nexus_completion_callbacks, links, and request_id, # but these are deliberately not exposed in overloads, hence the type-check # violation. @@ -677,7 +676,7 @@ async def _start_nexus_backing_workflow( # namespace to deliver the result to the caller namespace when the workflow reaches a # terminal state) and inbound links to the caller workflow (attached to history events of # the workflow started in the handler namespace, and displayed in the UI). - with _nexus_backing_workflow_start_context(): + with _nexus_backing_start_context(): token = OperationToken( type=OperationTokenType.WORKFLOW, namespace=temporal_context.client.namespace, @@ -715,3 +714,107 @@ async def _start_nexus_backing_workflow( ) return WorkflowHandle[ReturnType]._unsafe_from_client_workflow_handle(wf_handle) + + +async def _start_nexus_operation_workflow_update( # pyright: ignore[reportUnusedFunction] + *, + temporal_context: _TemporalStartOperationContext, + workflow_id: str, + update: str | Callable, + arg: Any = temporalio.common._arg_unset, + args: Sequence[Any] = [], + update_id: str | None = None, + result_type: type | None = None, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + run_id: str | None = None, + first_execution_run_id: str | None = None, +) -> temporalio.client.WorkflowUpdateHandle[Any]: + # Default update ID to the Nexus request ID for retry-safety (matches sdk-go). + update_id = update_id or temporal_context.nexus_context.request_id + # This token is different from the actual token returned to the caller + # because return token will have the run_id that is unknowable before + # making the call. If run_id is passed, then it will be the same + token = OperationToken( + type=OperationTokenType.UPDATE_WORKFLOW, + namespace=temporal_context.client.namespace, + workflow_id=workflow_id, + update_id=update_id, + run_id=run_id, + ).encode() + workflow_handle = temporal_context.client.get_workflow_handle( + workflow_id, run_id=run_id, first_execution_run_id=first_execution_run_id + ) + return await workflow_handle._start_update( + update, + arg, + args=args, + wait_for_stage=temporalio.client.WorkflowUpdateStage.ACCEPTED, # hardcoded as nexus only supports async updates + id=update_id, + result_type=result_type, + rpc_metadata=rpc_metadata, + rpc_timeout=rpc_timeout, + callbacks=temporal_context._get_callbacks(token), + links=temporal_context._get_request_links(), + request_id=temporal_context.nexus_context.request_id, + ) + + +def _apply_nexus_context_to_start_activity_request( # pyright: ignore[reportUnusedFunction] + req: temporalio.api.workflowservice.v1.StartActivityExecutionRequest, +) -> None: + """Apply the current Nexus operation context to an activity start request. + + This is a no-op outside a Nexus operation context. Within one, it attaches + the Nexus request ID and configures conflict handling to preserve the Nexus + metadata. Inbound links are attached to the completion callback when the + activity backs the operation and to the request otherwise. + """ + nexus_ctx = _try_start_operation_context() + if nexus_ctx is not None: + req.on_conflict_options.attach_request_id = True + req.on_conflict_options.attach_completion_callbacks = True + req.on_conflict_options.attach_links = True + + req.request_id = nexus_ctx.nexus_context.request_id + request_links = nexus_ctx._get_request_links() + + if _in_nexus_backing_start_context(): + callbacks = nexus_ctx._get_callbacks( + OperationToken( + type=OperationTokenType.ACTIVITY, + namespace=nexus_ctx.client.namespace, + activity_id=req.activity_id, + ).encode() + ) + req.completion_callbacks.extend( + temporalio.api.common.v1.Callback( + nexus=temporalio.api.common.v1.Callback.Nexus( + url=callback.url, + header=callback.headers, + ), + links=request_links, + ) + for callback in callbacks + ) + else: + req.links.extend(request_links) + + +def _apply_start_activity_response_to_nexus_context( # pyright: ignore[reportUnusedFunction] + activity_id: str, + resp: temporalio.api.workflowservice.v1.StartActivityExecutionResponse, +): + nexus_ctx = _try_start_operation_context() + if nexus_ctx is not None: + if resp.HasField("link"): + response_link = resp.link + else: + response_link = temporalio.api.common.v1.Link( + activity=temporalio.api.common.v1.Link.Activity( + namespace=nexus_ctx.client.namespace, + activity_id=activity_id, + run_id=resp.run_id, + ) + ) + nexus_ctx._add_response_link(response_link) diff --git a/temporalio/nexus/_operation_handlers.py b/temporalio/nexus/_operation_handlers.py index e5c3bd762..36c44e96c 100644 --- a/temporalio/nexus/_operation_handlers.py +++ b/temporalio/nexus/_operation_handlers.py @@ -138,6 +138,35 @@ class CancelWorkflowRunOptions: """The ID of the workflow to cancel.""" +@dataclass(frozen=True) +class CancelUpdateWorkflowOptions: + """Options for cancelling the workflow update backing a Nexus operation. + + These options are built by :py:class:`TemporalOperationHandler` and passed to + :py:meth:`TemporalOperationHandler.cancel_workflow_update`. + + .. warning:: + This API is experimental and unstable. + """ + + workflow_id: str + """The ID of the workflow where the update is running.""" + update_id: str + """The ID of the update to cancel.""" + run_id: str + """The workflow runID that accepted the update.""" + + +@dataclass(frozen=True) +class CancelActivityOptions: + """Options for cancelling the activity backing a Nexus operation.""" + + activity_id: str + """The activity ID to cancel.""" + run_id: str + """The run ID of the activity to cancel.""" + + class TemporalOperationHandler(OperationHandler[InputT, OutputT], ABC): """Operation handler for Nexus operations that interact with Temporal. Implementations override the start_operation method. @@ -181,15 +210,43 @@ async def cancel(self, ctx: CancelOperationContext, token: str) -> None: raise HandlerError( "Unable to decode operation token to cancel", type=HandlerErrorType.INTERNAL, + retryable_override=False, ) from err cancel_ctx = TemporalCancelOperationContext._from_cancel_operation_context(ctx) match operation_token.type: case OperationTokenType.WORKFLOW: + assert operation_token.workflow_id is not None options = CancelWorkflowRunOptions( workflow_id=operation_token.workflow_id ) await self.cancel_workflow_run(cancel_ctx, options) + case OperationTokenType.UPDATE_WORKFLOW: + assert operation_token.workflow_id is not None + assert operation_token.update_id is not None + assert operation_token.run_id is not None + cancel_options = CancelUpdateWorkflowOptions( + workflow_id=operation_token.workflow_id, + update_id=operation_token.update_id, + run_id=operation_token.run_id, + ) + await self.cancel_workflow_update(cancel_ctx, cancel_options) + case OperationTokenType.ACTIVITY: + assert operation_token.activity_id is not None + if not operation_token.run_id: + raise HandlerError( + "Expected operation token of type ACTIVITY to have a valid run id.", + type=HandlerErrorType.INTERNAL, + retryable_override=False, + ) + + await self.cancel_activity( + cancel_ctx, + CancelActivityOptions( + activity_id=operation_token.activity_id, + run_id=operation_token.run_id, + ), + ) async def cancel_workflow_run( self, @@ -205,3 +262,34 @@ async def cancel_workflow_run( options.workflow_id ) await workflow_handle.cancel() + + async def cancel_workflow_update( + self, + ctx: TemporalCancelOperationContext, # pyright: ignore[reportUnusedParameter] + options: CancelUpdateWorkflowOptions, # pyright: ignore[reportUnusedParameter] + ) -> None: + """Cancels the Workflow Update triggered by the Nexus operation. There is no native way to cancel an accepted Update, so cancellation depends on the Update handler itself. + Override this method and coordinate with the specific Update handler to trigger a cancellation. + + + .. warning:: + This API is experimental and unstable. + """ + raise HandlerError( + """ + There is no native way to cancel an accepted Update, so cancellation depends on the Update handler itself. + Override this method and coordinate with the specific Update handler to trigger a cancellation. + """, + type=HandlerErrorType.NOT_IMPLEMENTED, + ) + + async def cancel_activity( + self, + ctx: TemporalCancelOperationContext, # pyright: ignore[reportUnusedParameter] + options: CancelActivityOptions, + ) -> None: + """Requests cancellation of the standalone activity backing the operation.""" + activity_handle = temporalio.nexus.client().get_activity_handle( + options.activity_id, run_id=options.run_id + ) + await activity_handle.cancel() diff --git a/temporalio/nexus/_temporal_client.py b/temporalio/nexus/_temporal_client.py index 08204d89b..393da2fae 100644 --- a/temporalio/nexus/_temporal_client.py +++ b/temporalio/nexus/_temporal_client.py @@ -15,16 +15,23 @@ overload, ) -from nexusrpc import HandlerError, HandlerErrorType +from nexusrpc import HandlerError, HandlerErrorType, OperationError, OperationErrorState from nexusrpc.handler import StartOperationResultAsync, StartOperationResultSync from typing_extensions import Self import temporalio.common from temporalio.nexus._operation_context import ( + _nexus_backing_start_context, _start_nexus_backing_workflow, + _start_nexus_operation_workflow_update, _TemporalStartOperationContext, ) +from temporalio.nexus._token import OperationToken, OperationTokenType from temporalio.types import ( + CallableAsyncNoParam, + CallableAsyncSingleParam, + CallableSyncNoParam, + CallableSyncSingleParam, MethodAsyncNoParam, MethodAsyncSingleParam, MultiParamSpec, @@ -35,6 +42,7 @@ if TYPE_CHECKING: import temporalio.client + import temporalio.workflow _ResultT = TypeVar("_ResultT") @@ -279,6 +287,292 @@ async def start_workflow( """ ... + # Overload for no-param update + @overload + async def start_workflow_update( + self, + workflow_id: str, + update: temporalio.workflow.UpdateMethodMultiParam[[Any], ReturnType], + *, + update_id: str | None = None, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + run_id: str | None = None, + first_execution_run_id: str | None = None, + ) -> TemporalOperationResult[ReturnType]: ... + + # Overload for single-param update + @overload + async def start_workflow_update( + self, + workflow_id: str, + update: temporalio.workflow.UpdateMethodMultiParam[ + [Any, ParamType], ReturnType + ], + arg: ParamType, + *, + update_id: str | None = None, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + run_id: str | None = None, + first_execution_run_id: str | None = None, + ) -> TemporalOperationResult[ReturnType]: ... + + # Overload for multi-param update + @overload + async def start_workflow_update( + self, + workflow_id: str, + update: temporalio.workflow.UpdateMethodMultiParam[MultiParamSpec, ReturnType], + *, + args: MultiParamSpec.args, # type: ignore + update_id: str | None = None, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + run_id: str | None = None, + first_execution_run_id: str | None = None, + ) -> TemporalOperationResult[ReturnType]: ... + + # Overload for string-name update + @overload + async def start_workflow_update( + self, + workflow_id: str, + update: str, + arg: Any = temporalio.common._arg_unset, + *, + args: Sequence[Any] = [], + update_id: str | None = None, + result_type: type[ReturnType] | None = None, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + run_id: str | None = None, + first_execution_run_id: str | None = None, + ) -> TemporalOperationResult[ReturnType]: ... + + @abstractmethod + async def start_workflow_update( + self, + workflow_id: str, + update: str | Callable, + arg: Any = temporalio.common._arg_unset, + *, + args: Sequence[Any] = [], + update_id: str | None = None, + result_type: type | None = None, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + run_id: str | None = None, + first_execution_run_id: str | None = None, + ) -> TemporalOperationResult[Any]: + """Start a Workflow Update-backed Nexus Operation. + + .. warning:: + This API is experimental and unstable. + """ + ... + + # async no-param activity + @overload + async def start_activity( + self, + activity: CallableAsyncNoParam[ReturnType], + *, + id: str, + task_queue: str | None = None, + schedule_to_start_timeout: timedelta | None = None, + schedule_to_close_timeout: timedelta | None = None, + start_to_close_timeout: timedelta | None = None, + heartbeat_timeout: timedelta | None = None, + start_delay: timedelta | None = None, + id_reuse_policy: temporalio.common.ActivityIDReusePolicy = temporalio.common.ActivityIDReusePolicy.ALLOW_DUPLICATE, + id_conflict_policy: temporalio.common.ActivityIDConflictPolicy = temporalio.common.ActivityIDConflictPolicy.FAIL, + retry_policy: temporalio.common.RetryPolicy | None = None, + search_attributes: temporalio.common.TypedSearchAttributes | None = None, + summary: str | None = None, + priority: temporalio.common.Priority = temporalio.common.Priority.default, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + ) -> TemporalOperationResult[ReturnType]: ... + + # sync no-param activity + @overload + async def start_activity( + self, + activity: CallableSyncNoParam[ReturnType], + *, + id: str, + task_queue: str | None = None, + schedule_to_start_timeout: timedelta | None = None, + schedule_to_close_timeout: timedelta | None = None, + start_to_close_timeout: timedelta | None = None, + heartbeat_timeout: timedelta | None = None, + start_delay: timedelta | None = None, + id_reuse_policy: temporalio.common.ActivityIDReusePolicy = temporalio.common.ActivityIDReusePolicy.ALLOW_DUPLICATE, + id_conflict_policy: temporalio.common.ActivityIDConflictPolicy = temporalio.common.ActivityIDConflictPolicy.FAIL, + retry_policy: temporalio.common.RetryPolicy | None = None, + search_attributes: temporalio.common.TypedSearchAttributes | None = None, + summary: str | None = None, + priority: temporalio.common.Priority = temporalio.common.Priority.default, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + ) -> TemporalOperationResult[ReturnType]: ... + + # async single-param activity + @overload + async def start_activity( + self, + activity: CallableAsyncSingleParam[ParamType, ReturnType], + arg: ParamType, + *, + id: str, + task_queue: str | None = None, + schedule_to_start_timeout: timedelta | None = None, + schedule_to_close_timeout: timedelta | None = None, + start_to_close_timeout: timedelta | None = None, + heartbeat_timeout: timedelta | None = None, + start_delay: timedelta | None = None, + id_reuse_policy: temporalio.common.ActivityIDReusePolicy = temporalio.common.ActivityIDReusePolicy.ALLOW_DUPLICATE, + id_conflict_policy: temporalio.common.ActivityIDConflictPolicy = temporalio.common.ActivityIDConflictPolicy.FAIL, + retry_policy: temporalio.common.RetryPolicy | None = None, + search_attributes: temporalio.common.TypedSearchAttributes | None = None, + summary: str | None = None, + priority: temporalio.common.Priority = temporalio.common.Priority.default, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + ) -> TemporalOperationResult[ReturnType]: ... + + # sync single-param activity + @overload + async def start_activity( + self, + activity: CallableSyncSingleParam[ParamType, ReturnType], + arg: ParamType, + *, + id: str, + task_queue: str | None = None, + schedule_to_start_timeout: timedelta | None = None, + schedule_to_close_timeout: timedelta | None = None, + start_to_close_timeout: timedelta | None = None, + heartbeat_timeout: timedelta | None = None, + start_delay: timedelta | None = None, + id_reuse_policy: temporalio.common.ActivityIDReusePolicy = temporalio.common.ActivityIDReusePolicy.ALLOW_DUPLICATE, + id_conflict_policy: temporalio.common.ActivityIDConflictPolicy = temporalio.common.ActivityIDConflictPolicy.FAIL, + retry_policy: temporalio.common.RetryPolicy | None = None, + search_attributes: temporalio.common.TypedSearchAttributes | None = None, + summary: str | None = None, + priority: temporalio.common.Priority = temporalio.common.Priority.default, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + ) -> TemporalOperationResult[ReturnType]: ... + + # async multi-param activity + @overload + async def start_activity( + self, + activity: Callable[..., Awaitable[ReturnType]], + *, + args: Sequence[Any], + id: str, + task_queue: str | None = None, + schedule_to_start_timeout: timedelta | None = None, + schedule_to_close_timeout: timedelta | None = None, + start_to_close_timeout: timedelta | None = None, + heartbeat_timeout: timedelta | None = None, + start_delay: timedelta | None = None, + id_reuse_policy: temporalio.common.ActivityIDReusePolicy = temporalio.common.ActivityIDReusePolicy.ALLOW_DUPLICATE, + id_conflict_policy: temporalio.common.ActivityIDConflictPolicy = temporalio.common.ActivityIDConflictPolicy.FAIL, + retry_policy: temporalio.common.RetryPolicy | None = None, + search_attributes: temporalio.common.TypedSearchAttributes | None = None, + summary: str | None = None, + priority: temporalio.common.Priority = temporalio.common.Priority.default, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + ) -> TemporalOperationResult[ReturnType]: ... + + # sync multi-param activity + @overload + async def start_activity( + self, + activity: Callable[..., ReturnType], + *, + args: Sequence[Any], + id: str, + task_queue: str | None = None, + schedule_to_start_timeout: timedelta | None = None, + schedule_to_close_timeout: timedelta | None = None, + start_to_close_timeout: timedelta | None = None, + heartbeat_timeout: timedelta | None = None, + start_delay: timedelta | None = None, + id_reuse_policy: temporalio.common.ActivityIDReusePolicy = temporalio.common.ActivityIDReusePolicy.ALLOW_DUPLICATE, + id_conflict_policy: temporalio.common.ActivityIDConflictPolicy = temporalio.common.ActivityIDConflictPolicy.FAIL, + retry_policy: temporalio.common.RetryPolicy | None = None, + search_attributes: temporalio.common.TypedSearchAttributes | None = None, + summary: str | None = None, + priority: temporalio.common.Priority = temporalio.common.Priority.default, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + ) -> TemporalOperationResult[ReturnType]: ... + + # string-name activity + @overload + async def start_activity( + self, + activity: str, + arg: Any = temporalio.common._arg_unset, + *, + args: Sequence[Any] = [], + id: str, + task_queue: str | None = None, + result_type: type[ReturnType] | None = None, + schedule_to_start_timeout: timedelta | None = None, + schedule_to_close_timeout: timedelta | None = None, + start_to_close_timeout: timedelta | None = None, + heartbeat_timeout: timedelta | None = None, + start_delay: timedelta | None = None, + id_reuse_policy: temporalio.common.ActivityIDReusePolicy = temporalio.common.ActivityIDReusePolicy.ALLOW_DUPLICATE, + id_conflict_policy: temporalio.common.ActivityIDConflictPolicy = temporalio.common.ActivityIDConflictPolicy.FAIL, + retry_policy: temporalio.common.RetryPolicy | None = None, + search_attributes: temporalio.common.TypedSearchAttributes | None = None, + summary: str | None = None, + priority: temporalio.common.Priority = temporalio.common.Priority.default, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + ) -> TemporalOperationResult[ReturnType]: ... + + @abstractmethod + async def start_activity( + self, + activity: ( + str | Callable[..., Awaitable[ReturnType]] | Callable[..., ReturnType] + ), + arg: Any = temporalio.common._arg_unset, + *, + args: Sequence[Any] = [], + id: str, + task_queue: str | None = None, + result_type: type | None = None, + schedule_to_start_timeout: timedelta | None = None, + schedule_to_close_timeout: timedelta | None = None, + start_to_close_timeout: timedelta | None = None, + heartbeat_timeout: timedelta | None = None, + start_delay: timedelta | None = None, + id_reuse_policy: temporalio.common.ActivityIDReusePolicy = temporalio.common.ActivityIDReusePolicy.ALLOW_DUPLICATE, + id_conflict_policy: temporalio.common.ActivityIDConflictPolicy = temporalio.common.ActivityIDConflictPolicy.FAIL, + retry_policy: temporalio.common.RetryPolicy | None = None, + search_attributes: temporalio.common.TypedSearchAttributes | None = None, + summary: str | None = None, + priority: temporalio.common.Priority = temporalio.common.Priority.default, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + ) -> TemporalOperationResult[ReturnType]: + """Start a standalone activity that will deliver the Nexus operation result. + + If ``task_queue`` is not specified, the Nexus worker's task queue is used. + See :py:meth:`temporalio.client.Client.start_activity` for all other arguments. + """ + ... + class _TemporalNexusClient(TemporalNexusClient): # pyright: ignore[reportUnusedClass] """Nexus-aware wrapper around a Temporal Client. @@ -377,3 +671,124 @@ async def start_workflow( ) return TemporalOperationResult.async_token(wf_handle.to_token()) + + async def start_workflow_update( + self, + workflow_id: str, + update: str | Callable, + arg: Any = temporalio.common._arg_unset, + *, + args: Sequence[Any] = [], + update_id: str | None = None, + result_type: type | None = None, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + run_id: str | None = None, + first_execution_run_id: str | None = None, + ) -> TemporalOperationResult[Any]: + """Start a Workflow Update-backed Nexus Operation.""" + if not self._temporal_context.nexus_context.callback_url: + raise HandlerError( + "callback URL is required for a workflow update Nexus operation", + type=HandlerErrorType.BAD_REQUEST, + ) + with self._reserve_async_start(): + update_handle = await _start_nexus_operation_workflow_update( + temporal_context=self._temporal_context, + workflow_id=workflow_id, + update=update, + arg=arg, + args=args, + update_id=update_id, + result_type=result_type, + rpc_metadata=rpc_metadata, + rpc_timeout=rpc_timeout, + run_id=run_id, + first_execution_run_id=first_execution_run_id, + ) + # If the update has already completed, return the result synchronously + if update_handle._known_outcome is not None: + try: + result = await update_handle.result() + except temporalio.client.WorkflowUpdateFailedError as err: + raise OperationError( + str(err), state=OperationErrorState.FAILED + ) from err + return TemporalOperationResult.sync(result) + token = OperationToken( + type=OperationTokenType.UPDATE_WORKFLOW, + namespace=update_handle._client.namespace, + workflow_id=update_handle.workflow_id, + update_id=update_handle.id, + run_id=update_handle.workflow_run_id, + ).encode() + return TemporalOperationResult.async_token(token) + + async def start_activity( + self, + activity: ( + str | Callable[..., Awaitable[ReturnType]] | Callable[..., ReturnType] + ), + arg: Any = temporalio.common._arg_unset, + *, + args: Sequence[Any] = [], + id: str, + task_queue: str | None = None, + result_type: type | None = None, + schedule_to_start_timeout: timedelta | None = None, + schedule_to_close_timeout: timedelta | None = None, + start_to_close_timeout: timedelta | None = None, + heartbeat_timeout: timedelta | None = None, + start_delay: timedelta | None = None, + id_reuse_policy: temporalio.common.ActivityIDReusePolicy = temporalio.common.ActivityIDReusePolicy.ALLOW_DUPLICATE, + id_conflict_policy: temporalio.common.ActivityIDConflictPolicy = temporalio.common.ActivityIDConflictPolicy.FAIL, + retry_policy: temporalio.common.RetryPolicy | None = None, + search_attributes: temporalio.common.TypedSearchAttributes | None = None, + summary: str | None = None, + priority: temporalio.common.Priority = temporalio.common.Priority.default, + rpc_metadata: Mapping[str, str | bytes] = {}, + rpc_timeout: timedelta | None = None, + ) -> TemporalOperationResult[ReturnType]: + """Start a standalone activity that will deliver the Nexus operation result. + + If ``task_queue`` is not specified, the Nexus worker's task queue is used. + See :py:meth:`temporalio.client.Client.start_activity` for all other arguments. + """ + with self._reserve_async_start(): + # Here we are starting a "nexus-backing" standalone activity. The start request + # carries the Nexus completion callback so the activity result is delivered to + # the Nexus caller when the activity reaches a terminal state. + + with _nexus_backing_start_context(): + activity_handle: temporalio.client.ActivityHandle[ + ReturnType + ] = await self._temporal_context.client.start_activity( + activity=activity, # type: ignore + arg=arg, + args=args, + id=id, + task_queue=task_queue or self._temporal_context.info().task_queue, + result_type=result_type, + schedule_to_start_timeout=schedule_to_start_timeout, + schedule_to_close_timeout=schedule_to_close_timeout, + start_to_close_timeout=start_to_close_timeout, + heartbeat_timeout=heartbeat_timeout, + start_delay=start_delay, + id_reuse_policy=id_reuse_policy, + id_conflict_policy=id_conflict_policy, + retry_policy=retry_policy, + search_attributes=search_attributes, + summary=summary, + priority=priority, + rpc_metadata=rpc_metadata, + rpc_timeout=rpc_timeout, + ) + + activity_token = OperationToken( + type=OperationTokenType.ACTIVITY, + namespace=self._temporal_context.client.namespace, + activity_id=activity_handle.id, + run_id=activity_handle.run_id, + ) + + return TemporalOperationResult.async_token(activity_token.encode()) diff --git a/temporalio/nexus/_token.py b/temporalio/nexus/_token.py index d52b54180..a7c732f2a 100644 --- a/temporalio/nexus/_token.py +++ b/temporalio/nexus/_token.py @@ -14,6 +14,8 @@ class OperationTokenType(IntEnum): """Type discriminator for Nexus operation tokens.""" WORKFLOW = 1 + ACTIVITY = 2 + UPDATE_WORKFLOW = 3 if TYPE_CHECKING: @@ -27,17 +29,27 @@ class OperationToken: version: int | None = None type: OperationTokenType namespace: str - workflow_id: str + workflow_id: str | None = None + activity_id: str | None = None + run_id: str | None = None + update_id: str | None = None def encode(self) -> str: """Convert handle to a base64url-encoded token string.""" token_details: dict[str, Any] = { "t": self.type, "ns": self.namespace, - "wid": self.workflow_id, } + if self.workflow_id is not None: + token_details["wid"] = self.workflow_id + if self.activity_id is not None: + token_details["aid"] = self.activity_id if self.version is not None: token_details["v"] = self.version + if self.run_id is not None: + token_details["rid"] = self.run_id + if self.update_id is not None: + token_details["uid"] = self.update_id return _base64url_encode_no_padding( json.dumps( token_details, @@ -83,14 +95,40 @@ def decode(cls, token: str) -> Self: ) workflow_id = token_details.get("wid") - if not isinstance(workflow_id, str): + if workflow_id is not None and not isinstance(workflow_id, str): raise TypeError( f"invalid token: expected workflow id to be a string, got {type(workflow_id)}" ) - if token_type == OperationTokenType.WORKFLOW and not workflow_id: + if ( + token_type == OperationTokenType.WORKFLOW + or token_type == OperationTokenType.UPDATE_WORKFLOW + ): + if not workflow_id: + raise TypeError( + f"invalid token: expected non-empty workflow id for token type `{token_type.name}`" + ) + + activity_id = token_details.get("aid") + if activity_id is not None and not isinstance(activity_id, str): + raise TypeError( + f"invalid token: expected activity id to be a string, got {type(activity_id)}" + ) + + if token_type == OperationTokenType.ACTIVITY and not activity_id: + raise TypeError( + f"invalid token: expected non-empty activity id for token type `{token_type.name}`" + ) + + update_id = token_details.get("uid") + if not isinstance(update_id, str | None): raise TypeError( - "invalid token: expected non-empty workflow id for token type `WORKFLOW`" + f"invalid token: expected update_id to be a string or None, got {type(update_id)}" + ) + + if token_type == OperationTokenType.UPDATE_WORKFLOW and not update_id: + raise TypeError( + "invalid token: expected non-empty update id for token type `UPDATE_WORKFLOW`" ) namespace = token_details.get("ns") @@ -100,11 +138,20 @@ def decode(cls, token: str) -> Self: f"invalid token: expected namespace to be a string, got {type(namespace)}" ) + run_id = token_details.get("rid") + if not isinstance(run_id, str | None): + raise TypeError( + f"invalid token: expected run_id to be a string or None, got {type(run_id)}" + ) + return cls( type=OperationTokenType(token_type), namespace=namespace, workflow_id=workflow_id, + activity_id=activity_id, + run_id=run_id, version=version, + update_id=update_id, ) @@ -168,6 +215,9 @@ def from_token(cls, token: str) -> WorkflowHandle[OutputT]: f"invalid workflow token type: {op_token.type}, expected: {OperationTokenType.WORKFLOW}" ) + if not op_token.workflow_id: + raise TypeError("invalid workflow token: missing workflow id") + if op_token.version is not None and op_token.version != 0: raise TypeError( "invalid workflow token: 'v' field, if present, must be 0 or null/absent" diff --git a/temporalio/nexus/system/__init__.py b/temporalio/nexus/system/__init__.py index 21c5a1408..b3d4d2d6f 100644 --- a/temporalio/nexus/system/__init__.py +++ b/temporalio/nexus/system/__init__.py @@ -1,57 +1,151 @@ -"""System Nexus operation helpers.""" +"""System Nexus operation helpers. + +.. warning:: + This API is experimental and subject to change. +""" from __future__ import annotations +import contextlib +import contextvars +from collections.abc import Iterator, Sequence +from typing import Any + import temporalio.api.common.v1 +import temporalio.common import temporalio.converter from temporalio.bridge._visitor_functions import VisitorFunctions from temporalio.converter import BinaryProtoPayloadConverter, CompositePayloadConverter +from temporalio.converter._payload_converter import ( + _TemporalTransferTypePayloadConverter, +) TEMPORAL_SYSTEM_ENDPOINT = "__temporal_system" +_user_payload_converter: contextvars.ContextVar[ + temporalio.converter.PayloadConverter | None +] = contextvars.ContextVar("temporal-system-nexus-user-payload-converter", default=None) +_SYSTEM_PAYLOAD_METADATA_KEY = "__temporal_system_payload" +_SYSTEM_PAYLOAD_METADATA_VALUE = b"true" -class SystemNexusPayloadConverter(CompositePayloadConverter): - """Payload converter for system Nexus outer envelopes.""" +@contextlib.contextmanager +def _user_payload_converter_context( + payload_converter: temporalio.converter.PayloadConverter, +) -> Iterator[None]: + """Set the user payload converter for system Nexus model conversion.""" + token = _user_payload_converter.set(payload_converter) + try: + yield + finally: + _user_payload_converter.reset(token) + + +def _current_user_payload_converter() -> temporalio.converter.PayloadConverter: # pyright: ignore[reportUnusedFunction] + """Return the active user payload converter for system Nexus model conversion.""" + payload_converter = _user_payload_converter.get() + if payload_converter is None: + raise RuntimeError("System Nexus user payload converter context is not active") + return payload_converter + + +class _SystemNexusOuterPayloadConverter(CompositePayloadConverter): + """Payload converter for system Nexus outer proto envelopes.""" def __init__(self) -> None: """Create a payload converter for system Nexus outer envelopes.""" super().__init__(BinaryProtoPayloadConverter()) + def to_payloads( + self, values: Sequence[Any] + ) -> list[temporalio.api.common.v1.Payload]: + """See base class.""" + payloads = super().to_payloads(values) + for value, payload in zip(values, payloads): + if isinstance(value, temporalio.common.RawValue): + continue + payload.metadata[_SYSTEM_PAYLOAD_METADATA_KEY] = ( + _SYSTEM_PAYLOAD_METADATA_VALUE + ) + return payloads + + +class _SystemNexusPayloadConverter(temporalio.converter.PayloadConverter): + """Payload converter for system Nexus outer envelopes.""" + + _user_payload_converter: temporalio.converter.PayloadConverter + _outer_payload_converter: temporalio.converter.PayloadConverter + + def __init__( + self, user_payload_converter: temporalio.converter.PayloadConverter + ) -> None: + """Create a payload converter for system Nexus outer envelopes.""" + self._user_payload_converter = user_payload_converter + self._outer_payload_converter = _TemporalTransferTypePayloadConverter.wrap( + _SystemNexusOuterPayloadConverter() + ) + + def to_payloads( + self, values: Sequence[Any] + ) -> list[temporalio.api.common.v1.Payload]: + """See base class.""" + with _user_payload_converter_context(self._user_payload_converter): + return self._outer_payload_converter.to_payloads(values) + + def from_payloads( + self, + payloads: Sequence[temporalio.api.common.v1.Payload], + type_hints: list[type] | None = None, + ) -> list[Any]: + """See base class.""" + with _user_payload_converter_context(self._user_payload_converter): + return self._outer_payload_converter.from_payloads(payloads, type_hints) + def is_system_endpoint(endpoint: str) -> bool: - """Return whether a Nexus endpoint is the Temporal system endpoint.""" + """Return whether a Nexus endpoint is the Temporal system endpoint. + + .. warning:: + This API is experimental and subject to change. + """ return endpoint == TEMPORAL_SYSTEM_ENDPOINT +def _is_system_payload(payload: temporalio.api.common.v1.Payload) -> bool: + return ( + payload.metadata.get(_SYSTEM_PAYLOAD_METADATA_KEY) + == _SYSTEM_PAYLOAD_METADATA_VALUE + ) + + async def maybe_visit_payload( - endpoint: str, payload: temporalio.api.common.v1.Payload, visitor_functions: VisitorFunctions, skip_search_attributes: bool, ) -> temporalio.api.common.v1.Payload | None: - """Visit nested payloads if the payload is for the Temporal system endpoint.""" - if not is_system_endpoint(endpoint): + """Visit nested payloads if the payload is a Temporal system Nexus envelope.""" + if not _is_system_payload(payload): return None - payload_converter = get_payload_converter() + payload_converter = _SystemNexusOuterPayloadConverter() value = payload_converter.from_payload(payload) - from ._payload_visitor import PayloadVisitor + from temporalio.bridge._visitor import PayloadVisitor - await PayloadVisitor(skip_search_attributes=skip_search_attributes).visit( - visitor_functions, value - ) + payload_visitor = PayloadVisitor(skip_search_attributes=skip_search_attributes) + checkpoint = visitor_functions.checkpoint() + await payload_visitor.visit(visitor_functions, value) + if checkpoint is not None: + await visitor_functions.drain_since(checkpoint) return payload_converter.to_payload(value) -def get_payload_converter() -> temporalio.converter.PayloadConverter: +def _get_payload_converter( # pyright: ignore[reportUnusedFunction] + user_payload_converter: temporalio.converter.PayloadConverter, +) -> temporalio.converter.PayloadConverter: """Return the fixed payload converter for system Nexus outer envelopes.""" - return SystemNexusPayloadConverter() + return _SystemNexusPayloadConverter(user_payload_converter) __all__ = [ "TEMPORAL_SYSTEM_ENDPOINT", - "get_payload_converter", "is_system_endpoint", - "maybe_visit_payload", - "SystemNexusPayloadConverter", ] diff --git a/temporalio/nexus/system/_payload_visitor.py b/temporalio/nexus/system/_payload_visitor.py deleted file mode 100644 index 5b4178ff1..000000000 --- a/temporalio/nexus/system/_payload_visitor.py +++ /dev/null @@ -1,131 +0,0 @@ -from __future__ import annotations - -# This file is generated by gen_payload_visitor.py. Changes should be made there. -from typing import Any - -import temporalio.nexus.system -from temporalio.api.common.v1.message_pb2 import Payload -from temporalio.bridge._visitor_functions import ( - BoundedVisitorFunctions, - PayloadSequence, - VisitorFunctions, -) - - -class PayloadVisitor: - """A visitor for payloads. - Applies a function to every payload in a tree of messages. - """ - - def __init__( - self, - *, - skip_search_attributes: bool = False, - skip_headers: bool = False, - concurrency_limit: int = 1, - ): - """Creates a new payload visitor. - - Args: - skip_search_attributes: If True, search attributes are not visited. - skip_headers: If True, headers are not visited. - concurrency_limit: Maximum number of payload visits that may run - concurrently during a single call to visit(). Defaults to 1 - (sequential). - """ - if concurrency_limit < 1: - raise ValueError("concurrency_limit must be positive") - self.skip_search_attributes = skip_search_attributes - self.skip_headers = skip_headers - self._concurrency_limit = concurrency_limit - - async def visit(self, fs: VisitorFunctions, root: Any) -> None: - """Visits the given root message with the given function.""" - method_name = "_visit_" + root.DESCRIPTOR.full_name.replace(".", "_") - method = getattr(self, method_name, None) - if method is None: - raise ValueError(f"Unknown root message type: {root.DESCRIPTOR.full_name}") - if self._concurrency_limit == 1: - await method(fs, root) - return - - bounded = BoundedVisitorFunctions(fs, self._concurrency_limit) - try: - await method(bounded, root) - finally: - await bounded.drain() - - async def _visit_nexus_operation_input_payload( - self, - fs: VisitorFunctions, - endpoint: str, - payload: Payload, - ) -> None: - new_payload = await temporalio.nexus.system.maybe_visit_payload( - endpoint, - payload, - fs, - self.skip_search_attributes, - ) - if new_payload is None: - await self._visit_temporal_api_common_v1_Payload(fs, payload) - return - - if new_payload is not payload: - payload.CopyFrom(new_payload) - await fs.visit_system_nexus_envelope(payload) - - async def _visit_temporal_api_common_v1_Payload( - self, fs: VisitorFunctions, o: Payload - ): - await fs.visit_payload(o) - - async def _visit_temporal_api_common_v1_Payloads( - self, fs: VisitorFunctions, o: Any - ): - await fs.visit_payloads(o.payloads) - - async def _visit_payload_container(self, fs: VisitorFunctions, o: PayloadSequence): - await fs.visit_payloads(o) - - async def _visit_temporal_api_common_v1_Memo(self, fs: VisitorFunctions, o: Any): - for v in o.fields.values(): - await self._visit_temporal_api_common_v1_Payload(fs, v) - - async def _visit_temporal_api_common_v1_SearchAttributes( - self, fs: VisitorFunctions, o: Any - ): - if self.skip_search_attributes: - return - for v in o.indexed_fields.values(): - await self._visit_temporal_api_common_v1_Payload(fs, v) - - async def _visit_temporal_api_common_v1_Header(self, fs: VisitorFunctions, o: Any): - for v in o.fields.values(): - await self._visit_temporal_api_common_v1_Payload(fs, v) - - async def _visit_temporal_api_sdk_v1_UserMetadata( - self, fs: VisitorFunctions, o: Any - ): - if o.HasField("summary"): - await self._visit_temporal_api_common_v1_Payload(fs, o.summary) - if o.HasField("details"): - await self._visit_temporal_api_common_v1_Payload(fs, o.details) - - async def _visit_temporal_api_workflowservice_v1_SignalWithStartWorkflowExecutionRequest( - self, fs: VisitorFunctions, o: Any - ): - if o.HasField("input"): - await self._visit_temporal_api_common_v1_Payloads(fs, o.input) - if o.HasField("signal_input"): - await self._visit_temporal_api_common_v1_Payloads(fs, o.signal_input) - if o.HasField("memo"): - await self._visit_temporal_api_common_v1_Memo(fs, o.memo) - if o.HasField("search_attributes"): - await self._visit_temporal_api_common_v1_SearchAttributes( - fs, o.search_attributes - ) - if o.HasField("header"): - await self._visit_temporal_api_common_v1_Header(fs, o.header) - if o.HasField("user_metadata"): - await self._visit_temporal_api_sdk_v1_UserMetadata(fs, o.user_metadata) diff --git a/temporalio/runtime.py b/temporalio/runtime.py index 8fab68e9e..fc526ca2b 100644 --- a/temporalio/runtime.py +++ b/temporalio/runtime.py @@ -118,6 +118,7 @@ def __init__( *, telemetry: TelemetryConfig, worker_heartbeat_interval: timedelta | None = timedelta(seconds=60), + disable_environment_info: bool = False, ) -> None: """Create a runtime with the provided configuration. @@ -128,6 +129,8 @@ def __init__( ``runtime_options``. worker_heartbeat_interval: Interval for worker heartbeats. ``None`` disables heartbeating. Interval must be between 1s and 60s. + disable_environment_info: Whether to omit runtime, hosting, and + platform information from worker heartbeats. Raises: ValueError: If both ```runtime_options`` is a negative value. @@ -142,6 +145,7 @@ def __init__( runtime_options = temporalio.bridge.runtime.RuntimeOptions( telemetry=telemetry._to_bridge_config(), worker_heartbeat_interval_millis=heartbeat_millis, + disable_environment_info=disable_environment_info, ) self._core_runtime = temporalio.bridge.runtime.Runtime(options=runtime_options) @@ -180,6 +184,7 @@ def formatted(self) -> str: # We intentionally aren't using __str__ or __format__ so they can keep # their original dataclass impls targets = [ + "temporalio_common", "temporalio_sdk_core", "temporalio_client", "temporalio_sdk", @@ -335,6 +340,10 @@ class OpenTelemetryConfig: When enabled, the ``url`` should point to the HTTP endpoint (e.g. ``"http://localhost:4318/v1/metrics"``). Defaults to ``False`` (gRPC). + histogram_bucket_overrides: Override the default histogram bucket + boundaries for specific metrics. Keys are metric names and + values are sequences of bucket boundaries (e.g. + ``{"workflow_task_schedule_to_start_latency": [0.01, 0.05, 0.1, 0.5, 1.0, 5.0]}``). """ url: str @@ -345,6 +354,7 @@ class OpenTelemetryConfig: ) durations_as_seconds: bool = False http: bool = False + histogram_bucket_overrides: Mapping[str, Sequence[float]] | None = None def _to_bridge_config(self) -> temporalio.bridge.runtime.OpenTelemetryConfig: return temporalio.bridge.runtime.OpenTelemetryConfig( @@ -360,6 +370,7 @@ def _to_bridge_config(self) -> temporalio.bridge.runtime.OpenTelemetryConfig: ), durations_as_seconds=self.durations_as_seconds, http=self.http, + histogram_bucket_overrides=self.histogram_bucket_overrides, ) diff --git a/temporalio/service.py b/temporalio/service.py index 2d3829c08..130b5d295 100644 --- a/temporalio/service.py +++ b/temporalio/service.py @@ -24,7 +24,7 @@ import temporalio.runtime from temporalio.bridge.client import RPCError as BridgeRPCError -__version__ = "1.29.0" +__version__ = "1.31.0" ServiceRequest = TypeVar("ServiceRequest", bound=google.protobuf.message.Message) ServiceResponse = TypeVar("ServiceResponse", bound=google.protobuf.message.Message) @@ -43,7 +43,9 @@ class TLSConfig: """Root CA to validate the server certificate against.""" domain: str | None = None - """TLS domain.""" + """SNI host and HTTP/2 authority override, and the default name the server + certificate is verified against (see + :py:attr:`verification_server_name`).""" client_cert: bytes | None = None """Client certificate for mTLS. @@ -55,12 +57,26 @@ class TLSConfig: This must be combined with :py:attr:`client_cert`.""" + verification_server_name: str | None = None + """Name to verify the server certificate against, instead of the + :py:attr:`domain` / connected host. + + Unlike :py:attr:`domain`, this does not change the TLS SNI or HTTP/2 + authority values, which continue to follow the connected host (or + :py:attr:`domain` if set). Use this when the server's certificate does not + carry the name being dialed, e.g. when the connection traverses an + SNI-inspecting proxy that must be able to resolve the SNI value. + + Requires :py:attr:`server_root_ca_cert`; the system root store is not + consulted when this is set.""" + def _to_bridge_config(self) -> temporalio.bridge.client.ClientTlsConfig: return temporalio.bridge.client.ClientTlsConfig( server_root_ca_cert=self.server_root_ca_cert, domain=self.domain, client_cert=self.client_cert, client_private_key=self.client_private_key, + verification_server_name=self.verification_server_name, ) @@ -192,6 +208,20 @@ def _to_bridge_config(self) -> str: GrpcCompression.GZIP = _GzipGrpcCompression() +@dataclass(frozen=True) +class PayloadLimitsConfig: + """Warning thresholds for outbound payload/memo sizes.""" + + # Defaults mirror the Temporal server's `limit.blobSize.warn` (512 KiB) and `limit.memoSize.warn` + # (2 KiB) dynamic-config defaults, so the SDK warns at the same sizes the server would. + payloads_warn_size: int = 512 * 1024 + """Warning threshold, in bytes, for the size of an outbound payload-bearing field. Set to 0 to + disable.""" + + memo_warn_size: int = 2 * 1024 + """Warning threshold, in bytes, for outbound memo size. Set to 0 to disable.""" + + @dataclass class ConnectConfig: """Config for connecting to the server.""" @@ -208,6 +238,7 @@ class ConnectConfig: http_connect_proxy_config: HttpConnectProxyConfig | None = None dns_load_balancing_config: DnsLoadBalancingConfig | None = None grpc_compression: GrpcCompression = GrpcCompression.GZIP + payload_limits: PayloadLimitsConfig = field(default_factory=PayloadLimitsConfig) def __post_init__(self) -> None: """Set extra defaults on unset properties.""" @@ -271,6 +302,8 @@ def _to_bridge_config(self) -> temporalio.bridge.client.ClientConfig: else None ), grpc_compression=self.grpc_compression._to_bridge_config(), + payloads_warn_size=self.payload_limits.payloads_warn_size, + memo_warn_size=self.payload_limits.memo_warn_size, ) diff --git a/temporalio/testing/_workflow.py b/temporalio/testing/_workflow.py index 979222dea..3ab07e5aa 100644 --- a/temporalio/testing/_workflow.py +++ b/temporalio/testing/_workflow.py @@ -373,6 +373,27 @@ def client(self) -> temporalio.client.Client: """Client to this environment.""" return self._client + async def connect_client(self, **kwargs: Any) -> temporalio.client.Client: + """Create another client connected to this environment. + + Namespace and connection credentials from this environment's client are + used by default. + Keyword arguments are forwarded to :py:meth:`temporalio.client.Client.connect` + and override those defaults. + """ + config = self.client.service_client.config + connect_kwargs: dict[str, Any] = { + "namespace": self.client.namespace, + "api_key": config.api_key, + "tls": config.tls, + "rpc_metadata": config.rpc_metadata, + "runtime": config.runtime or temporalio.runtime.Runtime.default(), + } + connect_kwargs.update(kwargs) + return await temporalio.client.Client.connect( + config.target_host, **connect_kwargs + ) + async def shutdown(self) -> None: """Shut down this environment.""" pass diff --git a/temporalio/worker/__init__.py b/temporalio/worker/__init__.py index 55966b35d..4f6efe68c 100644 --- a/temporalio/worker/__init__.py +++ b/temporalio/worker/__init__.py @@ -58,6 +58,7 @@ WorkerDeploymentConfig, ) from ._workflow_instance import ( + PatchActivationInput, UnsandboxedWorkflowRunner, WorkflowInstance, WorkflowInstanceDetails, @@ -77,6 +78,7 @@ "PollerBehavior", "PollerBehaviorSimpleMaximum", "PollerBehaviorAutoscaling", + "PatchActivationInput", # Interceptor base classes "Interceptor", "ActivityInboundInterceptor", diff --git a/temporalio/worker/_activity.py b/temporalio/worker/_activity.py index 088ed0380..ded3047fc 100644 --- a/temporalio/worker/_activity.py +++ b/temporalio/worker/_activity.py @@ -32,7 +32,6 @@ import temporalio.client import temporalio.common import temporalio.converter -import temporalio.converter._payload_limits import temporalio.exceptions from temporalio.converter import ( StorageDriverActivityInfo, @@ -132,15 +131,8 @@ def __init__( else: self._dynamic_activity = defn - async def run( - self, - payload_error_limits: temporalio.converter._payload_limits._ServerPayloadErrorLimits - | None, - ) -> None: + async def run(self) -> None: """Continually poll for activity tasks and dispatch to handlers.""" - self._data_converter = self._data_converter._with_payload_error_limits( - payload_error_limits - ) async def raise_from_exception_queue() -> NoReturn: raise await self._fail_worker_exception_queue.get() @@ -363,136 +355,111 @@ async def _handle_start_activity_task( completion.result.completed.result.CopyFrom(payload) except BaseException as err: try: - try: - if isinstance(err, temporalio.activity._CompleteAsyncError): - temporalio.activity.logger.debug("Completing asynchronously") - completion.result.will_complete_async.SetInParent() - elif ( - isinstance( - err, - ( - asyncio.CancelledError, - temporalio.exceptions.CancelledError, - ), - ) - and running_activity.cancelled_due_to_heartbeat_error - ): - err = running_activity.cancelled_due_to_heartbeat_error - temporalio.activity.logger.warning( - f"Completing as failure during heartbeat with error of type {type(err)}: {err}", - ) - await data_converter.encode_failure( - err, completion.result.failed.failure - ) - elif ( - isinstance( - err, - ( - asyncio.CancelledError, - temporalio.exceptions.CancelledError, - ), - ) - and running_activity.cancellation_details.details - and running_activity.cancellation_details.details.paused - ): - temporalio.activity.logger.warning( - "Completing as failure due to unhandled cancel error produced by activity pause", - ) - await data_converter.encode_failure( - temporalio.exceptions.ApplicationError( - type="ActivityPause", - message="Unhandled activity cancel error produced by activity pause", - ), - completion.result.failed.failure, - ) - elif ( - isinstance( - err, - ( - asyncio.CancelledError, - temporalio.exceptions.CancelledError, - ), - ) - and running_activity.cancellation_details.details - and running_activity.cancellation_details.details.reset - ): - temporalio.activity.logger.warning( - "Completing as failure due to unhandled cancel error produced by activity reset", - ) - await data_converter.encode_failure( - temporalio.exceptions.ApplicationError( - type="ActivityReset", - message="Unhandled activity cancel error produced by activity reset", - ), - completion.result.failed.failure, - ) - elif ( + if isinstance(err, temporalio.activity._CompleteAsyncError): + temporalio.activity.logger.debug("Completing asynchronously") + completion.result.will_complete_async.SetInParent() + elif ( + isinstance( + err, + ( + asyncio.CancelledError, + temporalio.exceptions.CancelledError, + ), + ) + and running_activity.cancelled_due_to_heartbeat_error + ): + err = running_activity.cancelled_due_to_heartbeat_error + temporalio.activity.logger.warning( + f"Completing as failure during heartbeat with error of type {type(err)}: {err}", + ) + await data_converter.encode_failure( + err, completion.result.failed.failure + ) + elif ( + isinstance( + err, + ( + asyncio.CancelledError, + temporalio.exceptions.CancelledError, + ), + ) + and running_activity.cancellation_details.details + and running_activity.cancellation_details.details.paused + ): + temporalio.activity.logger.warning( + "Completing as failure due to unhandled cancel error produced by activity pause", + ) + await data_converter.encode_failure( + temporalio.exceptions.ApplicationError( + type="ActivityPause", + message="Unhandled activity cancel error produced by activity pause", + ), + completion.result.failed.failure, + ) + elif ( + isinstance( + err, + ( + asyncio.CancelledError, + temporalio.exceptions.CancelledError, + ), + ) + and running_activity.cancellation_details.details + and running_activity.cancellation_details.details.reset + ): + temporalio.activity.logger.warning( + "Completing as failure due to unhandled cancel error produced by activity reset", + ) + await data_converter.encode_failure( + temporalio.exceptions.ApplicationError( + type="ActivityReset", + message="Unhandled activity cancel error produced by activity reset", + ), + completion.result.failed.failure, + ) + elif ( + isinstance( + err, + ( + asyncio.CancelledError, + temporalio.exceptions.CancelledError, + ), + ) + and running_activity.cancelled_by_request + ): + temporalio.activity.logger.debug("Completing as cancelled") + await data_converter.encode_failure( + # TODO(cretz): Should use some other message? + temporalio.exceptions.CancelledError("Cancelled"), + completion.result.cancelled.failure, + ) + else: + if ( isinstance( err, - ( - asyncio.CancelledError, - temporalio.exceptions.CancelledError, - ), + temporalio.exceptions.ApplicationError, ) - and running_activity.cancelled_by_request + and err.category + == temporalio.exceptions.ApplicationErrorCategory.BENIGN ): - temporalio.activity.logger.debug("Completing as cancelled") - await data_converter.encode_failure( - # TODO(cretz): Should use some other message? - temporalio.exceptions.CancelledError("Cancelled"), - completion.result.cancelled.failure, - ) - elif isinstance( - err, - temporalio.converter._payload_limits._PayloadSizeError, - ): - temporalio.activity.logger.warning( - err.message, - extra={"__temporal_error_identifier": "PayloadSizeError"}, - ) - await data_converter.encode_failure( - err, completion.result.failed.failure + # Downgrade log level to DEBUG for BENIGN application errors. + temporalio.activity.logger.debug( + "Completing activity as failed", + exc_info=True, + extra={"__temporal_error_identifier": "ActivityFailure"}, ) else: - if ( - isinstance( - err, - temporalio.exceptions.ApplicationError, - ) - and err.category - == temporalio.exceptions.ApplicationErrorCategory.BENIGN - ): - # Downgrade log level to DEBUG for BENIGN application errors. - temporalio.activity.logger.debug( - "Completing activity as failed", - exc_info=True, - extra={ - "__temporal_error_identifier": "ActivityFailure" - }, - ) - else: - temporalio.activity.logger.warning( - "Completing activity as failed", - exc_info=True, - extra={ - "__temporal_error_identifier": "ActivityFailure" - }, - ) - await data_converter.encode_failure( - err, completion.result.failed.failure + temporalio.activity.logger.warning( + "Completing activity as failed", + exc_info=True, + extra={"__temporal_error_identifier": "ActivityFailure"}, ) - # For broken executors, we have to fail the entire worker - if isinstance(err, concurrent.futures.BrokenExecutor): - self._fail_worker_exception_queue.put_nowait(err) - # Handle PayloadSizeError from attempting to encode failure information - except ( - temporalio.converter._payload_limits._PayloadSizeError - ) as inner_err: - temporalio.activity.logger.exception(inner_err.message) - completion.result.Clear() await data_converter.encode_failure( - inner_err, completion.result.failed.failure + err, completion.result.failed.failure ) + # For broken executors, we have to fail the entire worker + if isinstance(err, concurrent.futures.BrokenExecutor): + self._fail_worker_exception_queue.put_nowait(err) except Exception as inner_err: temporalio.activity.logger.exception( f"Exception handling failed, original error: {err}" @@ -760,8 +727,7 @@ def cancel( ) # If not sync and there's a task, cancel it if not self.sync and self.task: - # TODO(cretz): Check that Python >= 3.9 and set msg? - self.task.cancel() + self.task.cancel("Activity cancelled") class _ThreadExceptionRaiser: @@ -775,6 +741,20 @@ def set_thread_id(self, thread_id: int) -> None: with self._lock: self._thread_id = thread_id + @contextmanager + def active_thread(self) -> Iterator[None]: + thread_id = threading.current_thread().ident + if thread_id is not None: + self.set_thread_id(thread_id) + try: + yield None + finally: + if thread_id is not None: + with self._lock: + if self._thread_id == thread_id: + self._thread_id = None + self._pending_exception = None + def raise_in_thread(self, exc_type: type[Exception]) -> None: with self._lock: self._pending_exception = exc_type @@ -939,10 +919,6 @@ def _execute_sync_activity( fn: Callable[..., Any], *args: Any, ) -> Any: - if cancel_thread_raiser: - thread_id = threading.current_thread().ident - if thread_id is not None: - cancel_thread_raiser.set_thread_id(thread_id) if isinstance(heartbeat, SharedHeartbeatSender): def heartbeat_fn(*details: Any) -> None: @@ -968,7 +944,11 @@ def heartbeat_fn(*details: Any) -> None: cancellation_details=cancellation_details, ) ) - return fn(*args) + if not cancel_thread_raiser: + return fn(*args) + else: + with cancel_thread_raiser.active_thread(): + return fn(*args) class SharedStateManager(ABC): diff --git a/temporalio/worker/_nexus.py b/temporalio/worker/_nexus.py index f35d10fd5..131e50862 100644 --- a/temporalio/worker/_nexus.py +++ b/temporalio/worker/_nexus.py @@ -6,7 +6,7 @@ import concurrent.futures import contextvars import threading -from collections.abc import Callable, Mapping, Sequence +from collections.abc import Awaitable, Callable, Mapping, Sequence from dataclasses import dataclass from datetime import datetime, timezone from functools import reduce @@ -29,8 +29,9 @@ import temporalio.client import temporalio.common import temporalio.converter -import temporalio.converter._payload_limits import temporalio.nexus +from temporalio.bridge._visitor import PayloadVisitor +from temporalio.bridge._visitor_functions import PayloadSequence, VisitorFunctions from temporalio.bridge.worker import PollShutdownError from temporalio.exceptions import ( ApplicationError, @@ -96,15 +97,8 @@ def __init__( self._fail_worker_exception_queue: asyncio.Queue[Exception] = asyncio.Queue() self._worker_shutdown_event: temporalio.common._CompositeEvent | None = None - async def run( - self, - payload_error_limits: temporalio.converter._payload_limits._ServerPayloadErrorLimits - | None, - ) -> None: + async def run(self) -> None: """Continually poll for Nexus tasks and dispatch to handlers.""" - self._data_converter = self._data_converter._with_payload_error_limits( - payload_error_limits - ) async def raise_from_exception_queue() -> NoReturn: raise await self._fail_worker_exception_queue.get() @@ -224,6 +218,19 @@ async def _complete_task( ): await asyncio.shield(self._bridge_worker().complete_nexus_task(completion)) + async def _encode_completion( + self, completion: temporalio.bridge.proto.nexus.NexusTaskCompletion + ) -> None: + """Apply the payload codec then external storage to the completion's payloads.""" + dc = self._data_converter + await PayloadVisitor(skip_search_attributes=True, skip_headers=True).visit( + _PayloadTransformVisitor(dc._encode_payload_sequence), completion + ) + await PayloadVisitor(skip_search_attributes=True).visit( + _PayloadTransformVisitor(dc._external_store_payload_sequence), + completion, + ) + # TODO(nexus-preview): stack trace pruning. See sdk-typescript NexusHandler.execute # "Any call up to this function and including this one will be trimmed out of stack traces."" @@ -268,6 +275,14 @@ async def _handle_cancel_operation_task( try: try: await self._handler.cancel_operation(ctx, request.operation_token) + completion = temporalio.bridge.proto.nexus.NexusTaskCompletion( + task_token=task_token, + completed=temporalio.api.nexus.v1.Response( + cancel_operation=temporalio.api.nexus.v1.CancelOperationResponse() + ), + ) + # No-op but keeps the cancel covered if it ever carries a payload. + await self._encode_completion(completion) except asyncio.CancelledError: completion = temporalio.bridge.proto.nexus.NexusTaskCompletion( task_token=task_token, @@ -279,16 +294,12 @@ async def _handle_cancel_operation_task( completion = temporalio.bridge.proto.nexus.NexusTaskCompletion( task_token=task_token, ) - await self._data_converter.encode_failure( - handler_error, completion.failure - ) - else: - completion = temporalio.bridge.proto.nexus.NexusTaskCompletion( - task_token=task_token, - completed=temporalio.api.nexus.v1.Response( - cancel_operation=temporalio.api.nexus.v1.CancelOperationResponse() - ), + self._data_converter.failure_converter.to_failure( + handler_error, + self._data_converter.payload_converter, + completion.failure, ) + await self._encode_completion(completion) await self._complete_task(completion) except Exception: logger.exception("Failed to send Nexus task completion") @@ -323,6 +334,13 @@ async def _handle_start_operation_task( request_deadline, endpoint, ) + completion = temporalio.bridge.proto.nexus.NexusTaskCompletion( + task_token=task_token, + completed=temporalio.api.nexus.v1.Response( + start_operation=start_response + ), + ) + await self._encode_completion(completion) except asyncio.CancelledError: completion = temporalio.bridge.proto.nexus.NexusTaskCompletion( task_token=task_token, @@ -334,19 +352,15 @@ async def _handle_start_operation_task( task_token=task_token, ) handler_error = _exception_to_handler_error(err) - await self._data_converter.encode_failure( - handler_error, completion.failure + self._data_converter.failure_converter.to_failure( + handler_error, + self._data_converter.payload_converter, + completion.failure, ) if isinstance(err, concurrent.futures.BrokenExecutor): self._fail_worker_exception_queue.put_nowait(err) - else: - completion = temporalio.bridge.proto.nexus.NexusTaskCompletion( - task_token=task_token, - completed=temporalio.api.nexus.v1.Response( - start_operation=start_response - ), - ) + await self._encode_completion(completion) await self._complete_task(completion) except Exception: @@ -425,7 +439,9 @@ async def _start_operation( ) ) elif isinstance(result, nexusrpc.handler.StartOperationResultSync): - [payload] = await self._data_converter.encode([result.value]) + [payload] = self._data_converter.payload_converter.to_payloads( + [result.value] + ) return temporalio.api.nexus.v1.StartOperationResponse( sync_success=temporalio.api.nexus.v1.StartOperationResponse.Sync( payload=payload, @@ -454,10 +470,41 @@ async def _start_operation( ) from err.__cause__ except FailureError as new_err: response = temporalio.api.nexus.v1.StartOperationResponse() - await self._data_converter.encode_failure(new_err, response.failure) + self._data_converter.failure_converter.to_failure( + new_err, + self._data_converter.payload_converter, + response.failure, + ) return response +class _PayloadTransformVisitor(VisitorFunctions): + """Adapts a payload-sequence transform for use with :class:`PayloadVisitor`.""" + + def __init__( + self, + f: Callable[ + [Sequence[temporalio.api.common.v1.Payload]], + Awaitable[list[temporalio.api.common.v1.Payload]], + ], + ) -> None: + self._f = f + + async def visit_payload(self, payload: temporalio.api.common.v1.Payload) -> None: + new_payload = (await self._f([payload]))[0] + if new_payload is not payload: + payload.CopyFrom(new_payload) + + async def visit_payloads(self, payloads: PayloadSequence) -> None: + if len(payloads) == 0: + return + new_payloads = await self._f(payloads) + if new_payloads is payloads: + return + del payloads[:] + payloads.extend(new_payloads) + + @dataclass class _DummyPayloadSerializer: data_converter: temporalio.converter.DataConverter @@ -473,18 +520,35 @@ async def deserialize( content: nexusrpc.Content, # type:ignore[reportUnusedParameter] as_type: type[Any] | None = None, ) -> Any: - payload = self.payload - if self.data_converter.payload_codec: - try: - [payload] = await self.data_converter.payload_codec.decode([payload]) - except Exception as err: - raise nexusrpc.HandlerError( - "Payload codec failed to decode Nexus operation input", - type=nexusrpc.HandlerErrorType.INTERNAL, - ) from err + dc = self.data_converter + # The visitor mutates in place, so work on a copy to leave the request + # payload untouched. + payload = temporalio.api.common.v1.Payload() + payload.CopyFrom(self.payload) + try: + await PayloadVisitor(skip_search_attributes=True).visit( + _PayloadTransformVisitor(dc._external_retrieve_payload_sequence), + payload, + ) + except Exception as err: + raise nexusrpc.HandlerError( + "Failed to retrieve Nexus operation input from external storage", + type=nexusrpc.HandlerErrorType.INTERNAL, + retryable_override=True, + ) from err + + try: + await PayloadVisitor(skip_search_attributes=True, skip_headers=True).visit( + _PayloadTransformVisitor(dc._decode_payload_sequence), payload + ) + except Exception as err: + raise nexusrpc.HandlerError( + "Payload codec failed to decode Nexus operation input", + type=nexusrpc.HandlerErrorType.INTERNAL, + ) from err try: - [input] = self.data_converter.payload_converter.from_payloads( + [input] = dc.payload_converter.from_payloads( [payload], type_hints=[as_type] if as_type else None, ) diff --git a/temporalio/worker/_replayer.py b/temporalio/worker/_replayer.py index 508d5f708..b3eb1a4d1 100644 --- a/temporalio/worker/_replayer.py +++ b/temporalio/worker/_replayer.py @@ -24,7 +24,12 @@ from ._interceptor import Interceptor from ._worker import load_default_build_id from ._workflow import _WorkflowWorker -from ._workflow_instance import UnsandboxedWorkflowRunner, WorkflowRunner +from ._workflow_instance import ( + _DEFAULT_ENABLED_WORKFLOW_LOGIC_FLAGS, + UnsandboxedWorkflowRunner, + WorkflowRunner, + _WorkflowLogicFlag, +) from .workflow_sandbox import SandboxedWorkflowRunner logger = logging.getLogger(__name__) @@ -83,6 +88,7 @@ def __init__( header_codec_behavior=header_codec_behavior, ) self._initial_config = self._config.copy() + self._default_workflow_logic_flags = set(_DEFAULT_ENABLED_WORKFLOW_LOGIC_FLAGS) # Apply plugin configuration self.plugins = plugins @@ -93,6 +99,14 @@ def __init__( if not self._config.get("workflows"): raise ValueError("At least one workflow must be specified") + def _set_default_workflow_logic_flag( + self, flag: _WorkflowLogicFlag, *, enabled: bool + ) -> None: + if enabled: + self._default_workflow_logic_flags.add(flag) + else: + self._default_workflow_logic_flags.discard(flag) + def config(self, *, active_config: bool = False) -> ReplayerConfig: """Config, as a dictionary, used to create this replayer. @@ -255,6 +269,7 @@ def on_eviction_hook( workflow_failure_exception_types=self._config.get( "workflow_failure_exception_types", [] ), + patch_activation_callback=None, debug_mode=self._config.get("debug_mode", False), metric_meter=runtime.metric_meter, on_eviction_hook=on_eviction_hook, @@ -269,6 +284,9 @@ def on_eviction_hook( ) != HeaderCodecBehavior.NO_CODEC, max_workflow_task_external_storage_concurrency=1, + default_workflow_logic_flags=frozenset( + self._default_workflow_logic_flags + ), ) external_storage = data_converter.external_storage storage_driver_types = ( @@ -307,6 +325,7 @@ def on_eviction_hook( ), nonsticky_to_sticky_poll_ratio=1, no_remote_activities=True, + disable_payload_error_limit=True, task_types=temporalio.bridge.worker.WorkerTaskTypes( enable_workflows=True, enable_local_activities=False, @@ -318,6 +337,7 @@ def on_eviction_hook( default_heartbeat_throttle_interval_millis=1000, max_activities_per_second=None, max_task_queue_activities_per_second=None, + max_eager_activity_reservations_per_workflow_task=3, graceful_shutdown_period_millis=0, versioning_strategy=temporalio.bridge.worker.WorkerVersioningStrategyNone( build_id_no_versioning=self._config.get("build_id") @@ -339,7 +359,7 @@ def on_eviction_hook( bridge_worker_scope = bridge_worker # Start worker - workflow_worker_task = asyncio.create_task(workflow_worker.run(None)) + workflow_worker_task = asyncio.create_task(workflow_worker.run()) # Yield iterator async def replay_iterator() -> AsyncIterator[WorkflowReplayResult]: diff --git a/temporalio/worker/_worker.py b/temporalio/worker/_worker.py index 2ad1d42c6..60f824c4d 100644 --- a/temporalio/worker/_worker.py +++ b/temporalio/worker/_worker.py @@ -29,7 +29,6 @@ VersioningBehavior, WorkerDeploymentVersion, ) -from temporalio.converter._payload_limits import _ServerPayloadErrorLimits from ._activity import SharedStateManager, _ActivityWorker from ._interceptor import Interceptor @@ -40,7 +39,12 @@ _DEFAULT_WORKFLOW_TASK_EXTERNAL_STORAGE_CONCURRENCY, _WorkflowWorker, ) -from ._workflow_instance import UnsandboxedWorkflowRunner, WorkflowRunner +from ._workflow_instance import ( + PatchActivationInput, + UnsandboxedWorkflowRunner, + WorkflowRunner, + _WorkflowLogicFlag, +) from .workflow_sandbox import SandboxedWorkflowRunner logger = logging.getLogger(__name__) @@ -126,6 +130,7 @@ def __init__( default_heartbeat_throttle_interval: timedelta = timedelta(seconds=30), max_activities_per_second: float | None = None, max_task_queue_activities_per_second: float | None = None, + max_eager_activity_reservations_per_workflow_task: int = 3, graceful_shutdown_timeout: timedelta = timedelta(), workflow_failure_exception_types: Sequence[type[BaseException]] = [], shared_state_manager: SharedStateManager | None = None, @@ -135,6 +140,7 @@ def __init__( use_worker_versioning: bool = False, disable_safe_workflow_eviction: bool = False, deployment_config: WorkerDeploymentConfig | None = None, + patch_activation_callback: Callable[[PatchActivationInput], bool] | None = None, workflow_task_poller_behavior: PollerBehavior = PollerBehaviorSimpleMaximum( maximum=5 ), @@ -263,6 +269,11 @@ def __init__( poll request. If multiple workers on the same queue have different values set, they will thrash with the last poller winning. + max_eager_activity_reservations_per_workflow_task: Maximum number of + activity slots that may be reserved for eager execution when + completing a workflow task. The default is 3 and the value must + be positive. To disable eager activity execution, set + ``disable_eager_activity_execution`` to ``True``. graceful_shutdown_timeout: Amount of time after shutdown is called that activities are given to complete before their tasks are cancelled. @@ -307,6 +318,12 @@ def __init__( deployment_config: Deployment config for the worker. Exclusive with ``build_id`` and ``use_worker_versioning``. WARNING: This is an experimental feature and may change in the future. + patch_activation_callback: Callback to decide whether the first non-replay + call to :py:func:`workflow.patched` for a + patch ID should activate that patch. The callback receives a + :py:class:`PatchActivationInput` and must return ``True`` to activate the + patch or ``False`` to leave it inactive. + WARNING: This is an experimental feature and may change in the future. workflow_task_poller_behavior: Specify the behavior of workflow task polling. Defaults to a 5-poller maximum. activity_task_poller_behavior: Specify the behavior of activity task polling. @@ -359,6 +376,7 @@ def __init__( default_heartbeat_throttle_interval=default_heartbeat_throttle_interval, max_activities_per_second=max_activities_per_second, max_task_queue_activities_per_second=max_task_queue_activities_per_second, + max_eager_activity_reservations_per_workflow_task=max_eager_activity_reservations_per_workflow_task, graceful_shutdown_timeout=graceful_shutdown_timeout, workflow_failure_exception_types=workflow_failure_exception_types, shared_state_manager=shared_state_manager, @@ -368,6 +386,7 @@ def __init__( use_worker_versioning=use_worker_versioning, disable_safe_workflow_eviction=disable_safe_workflow_eviction, deployment_config=deployment_config, + patch_activation_callback=patch_activation_callback, workflow_task_poller_behavior=workflow_task_poller_behavior, activity_task_poller_behavior=activity_task_poller_behavior, nexus_task_poller_behavior=nexus_task_poller_behavior, @@ -434,6 +453,11 @@ def _init_from_config(self, client: temporalio.client.Client, config: WorkerConf raise ValueError( "max_workflow_task_external_storage_concurrency must be positive" ) + if config.get("max_eager_activity_reservations_per_workflow_task", 3) < 1: + raise ValueError( + "max_eager_activity_reservations_per_workflow_task must be positive; " + "use disable_eager_activity_execution=True to disable eager activity execution" + ) # Prepend applicable client interceptors to the given ones client_config = config["client"].config(active_config=True) # type: ignore[reportTypedDictNotRequiredAccess] @@ -528,6 +552,7 @@ def check_activity(activity: str): workflow_failure_exception_types=config[ "workflow_failure_exception_types" ], # type: ignore[reportTypedDictNotRequiredAccess] + patch_activation_callback=config.get("patch_activation_callback"), debug_mode=config["debug_mode"], # type: ignore[reportTypedDictNotRequiredAccess] disable_eager_activity_execution=config[ "disable_eager_activity_execution" @@ -645,6 +670,9 @@ def check_activity(activity: str): max_task_queue_activities_per_second=config[ "max_task_queue_activities_per_second" ], # type: ignore[reportTypedDictNotRequiredAccess] + max_eager_activity_reservations_per_workflow_task=config[ + "max_eager_activity_reservations_per_workflow_task" + ], # type: ignore[reportTypedDictNotRequiredAccess] graceful_shutdown_period_millis=int( 1000 * config["graceful_shutdown_timeout"].total_seconds() # type: ignore[reportTypedDictNotRequiredAccess] ), @@ -666,6 +694,9 @@ def check_activity(activity: str): ]._to_bridge(), # type: ignore[reportTypedDictNotRequiredAccess,reportOptionalMemberAccess] plugins=deduped_plugin_names, storage_drivers=deduped_storage_driver_types, + disable_payload_error_limit=config.get( + "disable_payload_error_limit", False + ), ), ) @@ -720,6 +751,17 @@ def client(self, value: temporalio.client.Client) -> None: if self._nexus_worker: self._nexus_worker._client = value + def _set_default_workflow_logic_flag( + self, flag: _WorkflowLogicFlag, *, enabled: bool + ) -> None: + if self._started: + raise RuntimeError( + "Cannot set default workflow logic flags after the worker has started" + ) + if not self._workflow_worker: + raise RuntimeError("Cannot set workflow logic flags without workflows") + self._workflow_worker._set_default_workflow_logic_flag(flag, enabled=enabled) + @property def is_running(self) -> bool: """Whether the worker is running. @@ -767,17 +809,8 @@ def make_lambda(plugin: Plugin, next: Callable[[Worker], Awaitable[None]]): await next_function(self) async def _run(self): - # Eagerly validate which will do a namespace check in Core - namespace_info = await self._bridge_worker.validate() - payload_error_limits = ( - _ServerPayloadErrorLimits( - memo_size_error=namespace_info.limits.memo_size_limit_error, - payload_size_error=namespace_info.limits.blob_size_limit_error, - ) - if namespace_info.HasField("limits") - and not self._config.get("disable_payload_error_limit", False) - else None - ) + # Eagerly validate which will do a namespace check in Core. + await self._bridge_worker.validate() if self._started: raise RuntimeError("Already started") @@ -797,16 +830,14 @@ async def raise_on_shutdown(): # Create tasks for workers if self._activity_worker: tasks[self._activity_worker] = asyncio.create_task( - self._activity_worker.run(payload_error_limits) + self._activity_worker.run() ) if self._workflow_worker: tasks[self._workflow_worker] = asyncio.create_task( - self._workflow_worker.run(payload_error_limits) + self._workflow_worker.run() ) if self._nexus_worker: - tasks[self._nexus_worker] = asyncio.create_task( - self._nexus_worker.run(payload_error_limits) - ) + tasks[self._nexus_worker] = asyncio.create_task(self._nexus_worker.run()) # Wait for either worker or shutdown requested wait_task = asyncio.wait(tasks.values(), return_when=asyncio.FIRST_EXCEPTION) @@ -973,6 +1004,7 @@ class WorkerConfig(TypedDict, total=False): default_heartbeat_throttle_interval: timedelta max_activities_per_second: float | None max_task_queue_activities_per_second: float | None + max_eager_activity_reservations_per_workflow_task: int graceful_shutdown_timeout: timedelta workflow_failure_exception_types: Sequence[type[BaseException]] shared_state_manager: SharedStateManager | None @@ -982,6 +1014,7 @@ class WorkerConfig(TypedDict, total=False): use_worker_versioning: bool disable_safe_workflow_eviction: bool deployment_config: WorkerDeploymentConfig | None + patch_activation_callback: Callable[[PatchActivationInput], bool] | None workflow_task_poller_behavior: PollerBehavior activity_task_poller_behavior: PollerBehavior nexus_task_poller_behavior: PollerBehavior diff --git a/temporalio/worker/_workflow.py b/temporalio/worker/_workflow.py index 8e6ba2726..1b217b4a5 100644 --- a/temporalio/worker/_workflow.py +++ b/temporalio/worker/_workflow.py @@ -9,13 +9,13 @@ import os import sys import threading -import time from collections.abc import Awaitable, Callable, MutableMapping, Sequence from dataclasses import dataclass -from datetime import timedelta, timezone +from datetime import timezone from types import TracebackType import temporalio.api.common.v1 +import temporalio.bridge.proto.common import temporalio.bridge.proto.workflow_activation import temporalio.bridge.proto.workflow_completion import temporalio.bridge.runtime @@ -23,10 +23,8 @@ import temporalio.common import temporalio.converter import temporalio.converter._extstore -import temporalio.converter._payload_limits import temporalio.exceptions import temporalio.workflow -from temporalio.api.enums.v1 import WorkflowTaskFailedCause from temporalio.bridge.worker import PollShutdownError from temporalio.converter import StorageDriverStoreContext, StorageDriverWorkflowInfo from temporalio.worker.workflow_sandbox._runner import SandboxedWorkflowRunner @@ -42,10 +40,13 @@ WorkflowInterceptorClassInput, ) from ._workflow_instance import ( + _DEFAULT_ENABLED_WORKFLOW_LOGIC_FLAGS, + PatchActivationInput, WorkflowInstance, WorkflowInstanceDetails, WorkflowRunner, _WorkflowExternFunctions, + _WorkflowLogicFlag, ) logger = logging.getLogger(__name__) @@ -63,6 +64,17 @@ _DEFAULT_WORKFLOW_TASK_EXTERNAL_STORAGE_CONCURRENCY: int = 3 +def _set_external_storage_metrics( + target: temporalio.bridge.proto.common.ExternalStorageMetrics, + metrics: temporalio.converter._extstore.StorageOperationMetrics, +) -> None: + """Populate a proto ``ExternalStorageMetrics`` from measured storage metrics.""" + target.payload_count = metrics.payload_count + target.total_size_bytes = metrics.total_size + target.total_duration.FromTimedelta(metrics.total_duration) + target.driver_names.extend(sorted(metrics.driver_names)) + + class _WorkflowWorker: # type:ignore[reportUnusedClass] def __init__( self, @@ -78,6 +90,7 @@ def __init__( data_converter: temporalio.converter.DataConverter, interceptors: Sequence[Interceptor], workflow_failure_exception_types: Sequence[type[BaseException]], + patch_activation_callback: Callable[[PatchActivationInput], bool] | None, debug_mode: bool, disable_eager_activity_execution: bool, metric_meter: temporalio.common.MetricMeter, @@ -90,6 +103,7 @@ def __init__( assert_local_activity_valid: Callable[[str], None], encode_headers: bool, max_workflow_task_external_storage_concurrency: int, + default_workflow_logic_flags: frozenset[_WorkflowLogicFlag] | None = None, ) -> None: # Debug mode is enabled if specified or if the TEMPORAL_DEBUG env var is truthy debug_mode = debug_mode or bool(os.environ.get("TEMPORAL_DEBUG")) @@ -97,6 +111,11 @@ def __init__( self._bridge_worker = bridge_worker self._namespace = namespace self._task_queue = task_queue + self._default_workflow_logic_flags = set( + _DEFAULT_ENABLED_WORKFLOW_LOGIC_FLAGS + if default_workflow_logic_flags is None + else default_workflow_logic_flags + ) self._workflow_task_executor = ( workflow_task_executor or concurrent.futures.ThreadPoolExecutor( @@ -145,6 +164,7 @@ def __init__( ) self._workflow_failure_exception_types = workflow_failure_exception_types + self._patch_activation_callback = patch_activation_callback self._running_workflows: dict[str, _RunningWorkflow] = {} self._disable_eager_activity_execution = disable_eager_activity_execution self._on_eviction_hook = on_eviction_hook @@ -206,15 +226,7 @@ def __init__( else: self._dynamic_workflow = defn - async def run( - self, - payload_error_limits: temporalio.converter._payload_limits._ServerPayloadErrorLimits - | None, - ) -> None: - self._data_converter = self._data_converter._with_payload_error_limits( - payload_error_limits - ) - + async def run(self) -> None: # Continually poll for workflow work task_tag = object() try: @@ -324,7 +336,6 @@ async def _handle_activation( completion.successful.SetInParent() workflow = None data_converter = self._data_converter - task_start_time = time.monotonic() download_metrics = temporalio.converter._extstore.StorageOperationMetrics() try: if LOG_PROTOS: @@ -486,18 +497,12 @@ async def _handle_activation( upload_metrics = temporalio.converter._extstore.StorageOperationMetrics() try: - try: - upload_metrics = await temporalio.bridge.worker.encode_completion( - completion, - data_converter, - encode_headers=self._encode_headers, - storage_concurrency_limit=self._max_workflow_task_external_storage_concurrency, - ) - except temporalio.converter._payload_limits._PayloadSizeError as err: - logger.warning(err.message) - completion.failed.Clear() - await data_converter.encode_failure(err, completion.failed.failure) - completion.failed.force_cause = WorkflowTaskFailedCause.WORKFLOW_TASK_FAILED_CAUSE_PAYLOADS_TOO_LARGE + upload_metrics = await temporalio.bridge.worker.encode_completion( + completion, + data_converter, + encode_headers=self._encode_headers, + storage_concurrency_limit=self._max_workflow_task_external_storage_concurrency, + ) except Exception as err: logger.exception( "Failed encoding completion on workflow with run ID %s", act.run_id @@ -505,6 +510,17 @@ async def _handle_activation( completion.failed.Clear() completion.failed.failure.message = f"Failed encoding completion: {err}" + # Reported on the completion so core can include them in its workflow-task duration + # log; core measures the duration itself. + if download_metrics.payload_count > 0: + _set_external_storage_metrics( + completion.payload_download_metrics, download_metrics + ) + if upload_metrics.payload_count > 0: + _set_external_storage_metrics( + completion.payload_upload_metrics, upload_metrics + ) + # Send off completion if LOG_PROTOS: logger.debug("Sending workflow completion:\n%s", completion) @@ -516,84 +532,6 @@ async def _handle_activation( "Failed completing activation on workflow with run ID %s", act.run_id ) - # Log workflow task duration with external storage metrics - self._log_workflow_task_duration( - act, workflow, task_start_time, download_metrics, upload_metrics - ) - - def _log_workflow_task_duration( - self, - act: temporalio.bridge.proto.workflow_activation.WorkflowActivation, - workflow: _RunningWorkflow | None, - task_start_time: float, - download_metrics: temporalio.converter._extstore.StorageOperationMetrics, - upload_metrics: temporalio.converter._extstore.StorageOperationMetrics, - ) -> None: - task_duration = timedelta(seconds=time.monotonic() - task_start_time) - - def _fmt_duration(td: timedelta) -> str: - secs = td.total_seconds() - if secs >= 1: - return f"{secs:.3f}s" - return f"{secs * 1000:.3f}ms" - - completed_event_id = act.history_length + 1 - _info = workflow.get_info() if workflow is not None else None - attempt = _info.attempt if _info is not None else "unknown" - log_id = f"{act.run_id}:{completed_event_id}:{attempt}" - msg_details, extra = temporalio.workflow._build_log_context( - _info._logger_details() if _info is not None else None, - full_workflow_info=_info, - ) - msg_details["event_id"] = completed_event_id - msg_details["workflow_task_duration"] = _fmt_duration(task_duration) - msg_details["workflow_history_size"] = act.history_size_bytes - extra["event_id"] = completed_event_id - extra["workflow_task_duration"] = task_duration - extra["workflow_history_size"] = act.history_size_bytes - if download_metrics.payload_count > 0: - msg_details["payload_download_count"] = download_metrics.payload_count - msg_details["payload_download_size"] = download_metrics.total_size - msg_details["payload_download_duration"] = _fmt_duration( - download_metrics.total_duration - ) - msg_details["payload_download_drivers"] = sorted( - download_metrics.driver_names - ) - extra["payload_download_count"] = download_metrics.payload_count - extra["payload_download_size"] = download_metrics.total_size - extra["payload_download_duration"] = download_metrics.total_duration - extra["payload_download_drivers"] = sorted(download_metrics.driver_names) - if upload_metrics.payload_count > 0: - msg_details["payload_upload_count"] = upload_metrics.payload_count - msg_details["payload_upload_size"] = upload_metrics.total_size - msg_details["payload_upload_duration"] = _fmt_duration( - upload_metrics.total_duration - ) - msg_details["payload_upload_drivers"] = sorted(upload_metrics.driver_names) - extra["payload_upload_count"] = upload_metrics.payload_count - extra["payload_upload_size"] = upload_metrics.total_size - extra["payload_upload_duration"] = upload_metrics.total_duration - extra["payload_upload_drivers"] = sorted(upload_metrics.driver_names) - if task_duration.total_seconds() > 10: - logger.warning( - f"[TMPRL1104] {log_id} Workflow task exceeded 10 seconds (%s)", - msg_details, - extra=extra, - ) - elif task_duration.total_seconds() > 5: - logger.info( - f"[TMPRL1104] {log_id} Workflow task exceeded 5 seconds (%s)", - msg_details, - extra=extra, - ) - else: - logger.debug( - f"[TMPRL1104] {log_id} Workflow task duration information (%s)", - msg_details, - extra=extra, - ) - async def _handle_cache_eviction( self, act: temporalio.bridge.proto.workflow_activation.WorkflowActivation, @@ -789,7 +727,7 @@ def _create_workflow_instance( # Create instance from details det = WorkflowInstanceDetails( - payload_converter_class=self._data_converter.payload_converter_class, + payload_converter_factory=self._data_converter._new_payload_converter, failure_converter_class=self._data_converter.failure_converter_class, interceptor_classes=self._interceptor_classes, defn=defn, @@ -798,8 +736,10 @@ def _create_workflow_instance( extern_functions=self._extern_functions, disable_eager_activity_execution=self._disable_eager_activity_execution, worker_level_failure_exception_types=self._workflow_failure_exception_types, + patch_activation_callback=self._patch_activation_callback, last_completion_result=init.last_completion_result, last_failure=last_failure, + default_workflow_logic_flags=frozenset(self._default_workflow_logic_flags), ) if defn.sandboxed: return self._workflow_runner.create_instance(det) @@ -812,6 +752,14 @@ def nondeterminism_as_workflow_fail(self) -> bool: for typ in self._workflow_failure_exception_types ) + def _set_default_workflow_logic_flag( + self, flag: _WorkflowLogicFlag, *, enabled: bool + ) -> None: + if enabled: + self._default_workflow_logic_flags.add(flag) + else: + self._default_workflow_logic_flags.discard(flag) + def nondeterminism_as_workflow_fail_for_types(self) -> set[str]: return { k @@ -977,7 +925,6 @@ def create( payload_converter_class=workflow_context_dc.payload_converter_class, payload_codec=workflow_context_dc.payload_codec, failure_converter_class=workflow_context_dc.failure_converter_class, - payload_limits=workflow_context_dc.payload_limits, external_storage=workflow_context_dc.external_storage, _ca_instance=instance, _ca_context_free_dc=context_free_dc, @@ -1020,12 +967,6 @@ async def _decode_payload_sequence( ) -> list[temporalio.api.common.v1.Payload]: return await self._get_current_dc()._decode_payload_sequence(payloads) - def _validate_payload_limits( - self, - payloads: Sequence[temporalio.api.common.v1.Payload], - ) -> None: - self._get_current_dc()._validate_payload_limits(payloads) - class _InterruptDeadlockError(BaseException): pass diff --git a/temporalio/worker/_workflow_instance.py b/temporalio/worker/_workflow_instance.py index 74edc66b7..d0b10ccae 100644 --- a/temporalio/worker/_workflow_instance.py +++ b/temporalio/worker/_workflow_instance.py @@ -27,7 +27,7 @@ Sequence, ) from contextlib import contextmanager -from dataclasses import dataclass +from dataclasses import dataclass, field from datetime import timedelta from enum import IntEnum from typing import ( @@ -86,6 +86,17 @@ LOG_IGNORE_DURING_DELETE = False +async def _shield_await(fut: asyncio.Future[Any]) -> Any: + """Await a future without cancelling it if the awaiting task is cancelled. + + This behaves like ``asyncio.shield(fut)`` but avoids the spurious + "exception in shielded future" error log on Python 3.11+ when the + awaiting task is cancelled and the future eventually fails. + """ + await asyncio.wait([fut]) + return fut.result() + + class WorkflowRunner(ABC): """Abstract runner for workflows that creates workflow instances to run. @@ -131,11 +142,22 @@ def set_worker_level_failure_exception_types( pass +@dataclass(frozen=True) +class PatchActivationInput: + """Input for the worker patch activation callback.""" + + workflow_info: temporalio.workflow.Info + """Information about the workflow execution calling ``patched``.""" + + patch_id: str + """Patch ID passed to ``patched``.""" + + @dataclass(frozen=True) class WorkflowInstanceDetails: """Immutable details for creating a workflow instance.""" - payload_converter_class: type[temporalio.converter.PayloadConverter] + payload_converter_factory: Callable[[], temporalio.converter.PayloadConverter] failure_converter_class: type[temporalio.converter.FailureConverter] interceptor_classes: Sequence[type[WorkflowInboundInterceptor]] defn: temporalio.workflow._Definition @@ -144,8 +166,12 @@ class WorkflowInstanceDetails: extern_functions: Mapping[str, Callable] disable_eager_activity_execution: bool worker_level_failure_exception_types: Sequence[type[BaseException]] + patch_activation_callback: Callable[[PatchActivationInput], bool] | None last_completion_result: temporalio.api.common.v1.Payloads last_failure: Failure | None + default_workflow_logic_flags: frozenset[_WorkflowLogicFlag] = field( + default_factory=lambda: _DEFAULT_ENABLED_WORKFLOW_LOGIC_FLAGS + ) class WorkflowInstance(ABC): @@ -246,7 +272,7 @@ def __init__(self, det: WorkflowInstanceDetails) -> None: self._defn = det.defn self._workflow_input: ExecuteWorkflowInput | None = None self._info = det.info - self._context_free_payload_converter = det.payload_converter_class() + self._context_free_payload_converter = det.payload_converter_factory() self._context_free_failure_converter = det.failure_converter_class() workflow_context = temporalio.converter.WorkflowSerializationContext( namespace=det.info.namespace, @@ -264,7 +290,10 @@ def __init__(self, det: WorkflowInstanceDetails) -> None: self._worker_level_failure_exception_types = ( det.worker_level_failure_exception_types ) + self._patch_activation_callback = det.patch_activation_callback + self._default_workflow_logic_flags = det.default_workflow_logic_flags self._primary_task: asyncio.Task[None] | None = None + self._cancel_primary_task_pending = False self._time_ns = 0 self._cancel_reason: str | None = None self._deployment_version_for_current_task: None | ( @@ -435,6 +464,9 @@ def activate( self._is_replaying = act.is_replaying self._current_thread_id = threading.get_ident() self._current_internal_flags = act.available_internal_flags + self._single_batch_activation = self._workflow_logic_flag_enabled( + _WorkflowLogicFlag.PROCESS_WORKFLOW_ACTIVATION_JOBS_AS_SINGLE_BATCH + ) activation_err: Exception | None = None try: # Split into job sets with patches, then signals + updates, then @@ -455,21 +487,39 @@ def activate( else: job_sets[3].append(job) + # Core guarantees query-only activations. Fail the workflow task if violated. + assert not job_sets[3] or not any(job_sets[:3]), ( + "Query jobs must not share an activation with non-query jobs. " + "This is an SDK Core bug." + ) + if start_job: self._workflow_input = self._make_workflow_input(start_job) - # Apply every job set, running after each set - for index, job_set in enumerate(job_sets): - if not job_set: - continue - for job in job_set: - # Let errors bubble out of these to the caller to fail the task - self._apply(job) - - # Run one iteration of the loop. We do not allow conditions to - # be checked in patch jobs (first index) or query jobs (last - # index). - self._run_once(check_conditions=index == 1 or index == 2) + if self._single_batch_activation: + # Applying every job before giving workflow tasks a chance to + # run prevents their order in the activation from hiding state + # that arrived in the same workflow task. + for job_set in job_sets: + for job in job_set: + # Let errors bubble out of these to the caller to fail the task + self._apply(job) + if any(job_sets): + self._run_once(check_conditions=bool(job_sets[1] or job_sets[2])) + else: + # Preserve the legacy scheduling order for histories which do + # not contain the single-batch workflow logic flag. + for index, job_set in enumerate(job_sets): + if not job_set: + continue + for job in job_set: + # Let errors bubble out of these to the caller to fail the task + self._apply(job) + + # Run one iteration of the loop. We do not allow conditions to + # be checked in patch jobs (first index) or query jobs (last + # index). + self._run_once(check_conditions=index == 1 or index == 2) except Exception as err: # We want some errors during activation, like those that can happen # during payload conversion, to be able to fail the workflow not the @@ -604,6 +654,10 @@ def _apply_cancel_workflow( # workflow the ability to receive the cancellation, so we must defer # this cancellation to the next iteration of the event loop. self.call_soon(self._primary_task.cancel) + elif self._single_batch_activation: + # Initialization is the only job that creates the primary task, so + # retain a same-activation cancellation until that task exists. + self._cancel_primary_task_pending = True def _apply_do_update( self, job: temporalio.bridge.proto.workflow_activation.DoUpdate @@ -1097,6 +1151,9 @@ async def run_workflow(input: ExecuteWorkflowInput) -> None: self._run_top_level_workflow_function(run_workflow(self._workflow_input)), name="run", ) + if self._cancel_primary_task_pending: + self._cancel_primary_task_pending = False + self.call_soon(self._primary_task.cancel) def _apply_update_random_seed( self, job: temporalio.bridge.proto.workflow_activation.UpdateRandomSeed @@ -1363,7 +1420,20 @@ def workflow_patch(self, id: str, *, deprecated: bool) -> bool: if use_patch is not None: return use_patch - use_patch = not self._is_replaying or id in self._patches_notified + # Replay and history markers already determine the branch, and deprecation must + # keep existing patch semantics, so only a genuinely new patch consults the + # callback. + if deprecated or self._is_replaying or id in self._patches_notified: + use_patch = not self._is_replaying or id in self._patches_notified + elif self._patch_activation_callback is not None: + with self._as_read_only(in_query_or_validator=False): + use_patch = self._patch_activation_callback( + PatchActivationInput(workflow_info=self._info, patch_id=id) + ) + if type(use_patch) is not bool: + raise TypeError("Patch activation callback must return true or false") + else: + use_patch = True self._patches_memoized[id] = use_patch if use_patch: command = self._add_command() @@ -1795,6 +1865,19 @@ async def workflow_wait_condition( timeout_summary: str | None = None, ) -> None: self._assert_not_read_only("wait condition") + cancellation_requested_before = self._cancel_reason is not None + + # Some asyncio.wait_for implementations can prefer a ready condition + # result or timeout over task cancellation that becomes ready in the same + # event-loop turn. Only detect a new request so workflows can catch + # cancellation and keep going. + def cancellation_arrived() -> bool: + return ( + self._single_batch_activation + and not cancellation_requested_before + and self._cancel_reason is not None + ) + fut = self.create_future() self._conditions.append((fn, fut)) user_metadata = ( @@ -1812,7 +1895,14 @@ async def in_context(): _TimerOptionsCtxVar.set(_TimerOptions(user_metadata=user_metadata)) await asyncio.wait_for(fut, timeout) - await ctxvars.run(in_context) + try: + await ctxvars.run(in_context) + except asyncio.TimeoutError: + if cancellation_arrived(): + raise asyncio.CancelledError() + raise + if cancellation_arrived(): + raise asyncio.CancelledError() def workflow_get_current_details(self) -> str: return self._current_details @@ -1873,6 +1963,7 @@ def workflow_random_seed(self) -> int: def workflow_register_random_seed_callback( self, callback: Callable[[int], None] ) -> None: + self._assert_not_read_only("register random seed callback") self._seed_callbacks.append(callback) #### Calls from outbound impl #### @@ -1900,9 +1991,11 @@ async def run_activity() -> Any: # be marked as unstarted handle._started = True try: - # We have to shield because we don't want the underlying - # result future to be cancelled - return await asyncio.shield(handle._result_fut) + return await self._await_temporal_operation( + handle._result_fut, + lambda _err, command: handle._apply_cancel_command(command), + completed_cancellation_flag=_WorkflowLogicFlag.RAISE_ON_CANCELLING_COMPLETED_ACTIVITY, + ) except _ActivityDoBackoffError as err: # We have to sleep then reschedule. Note this sleep can be # cancelled like any other timer. @@ -1913,28 +2006,6 @@ async def run_activity() -> Any: # We have to put the handle back on the pending activity # dict with its new seq self._pending_activities[handle._seq] = handle - except asyncio.CancelledError: - # If an activity future completes at the same time as a cancellation is being processed, the cancellation would be swallowed - # _WorkflowLogicFlag.RAISE_ON_CANCELLING_COMPLETED_ACTIVITY will correctly reraise the exception - if handle._result_fut.done(): - if ( - not self._is_replaying - or _WorkflowLogicFlag.RAISE_ON_CANCELLING_COMPLETED_ACTIVITY - in self._current_internal_flags - ): - self._current_completion.successful.used_internal_flags.append( - _WorkflowLogicFlag.RAISE_ON_CANCELLING_COMPLETED_ACTIVITY - ) - raise - # Send a cancel request to the activity - handle._apply_cancel_command(self._add_command()) - # Clear the cancellation counter on Python 3.11+ so the - # next await does not immediately re-raise CancelledError - if ( - sys.version_info >= (3, 11) - and (t := asyncio.current_task()) is not None - ): - t.uncancel() # type: ignore[union-attr] # Create the handle and set as pending handle = _ActivityHandle(self, input, run_activity()) @@ -1991,11 +2062,13 @@ async def _outbound_start_child_workflow( handle: _ChildWorkflowHandle # Common code for handling cancel for start and run - def apply_child_cancel_error(err: asyncio.CancelledError) -> None: + def apply_child_cancel_error( + err: asyncio.CancelledError, + cancel_command: temporalio.bridge.proto.workflow_commands.WorkflowCommand, + ) -> None: # Send a cancel request to the child, forwarding the msg passed to # Task.cancel(msg) (if any) as the cancellation reason. reason = err.args[0] if err.args and isinstance(err.args[0], str) else "" - cancel_command = self._add_command() handle._apply_cancel_command(cancel_command, reason=reason) # If the cancel command is for external workflow, we # have to add a seq and mark it pending @@ -2014,20 +2087,9 @@ def apply_child_cancel_error(err: asyncio.CancelledError) -> None: # Function that runs in the handle async def run_child() -> Any: - while True: - try: - # We have to shield because we don't want the future itself - # to be cancelled - return await asyncio.shield(handle._result_fut) - except asyncio.CancelledError as err: - apply_child_cancel_error(err) - # Clear the cancellation counter on Python 3.11+ so the - # next await does not immediately re-raise CancelledError - if ( - sys.version_info >= (3, 11) - and (t := asyncio.current_task()) is not None - ): - t.uncancel() # type: ignore[union-attr] + return await self._await_temporal_operation( + handle._result_fut, apply_child_cancel_error + ) # Create the handle and set as pending handle = _ChildWorkflowHandle( @@ -2037,23 +2099,12 @@ async def run_child() -> Any: self._pending_child_workflows[handle._seq] = handle # Wait on start before returning - while True: - try: - # We have to shield because we don't want the future itself - # to be cancelled - await asyncio.shield(handle._start_fut) - return handle - except asyncio.CancelledError as err: - apply_child_cancel_error(err) - # Clear the cancellation counter on Python 3.11+ so the - # next await does not immediately re-raise CancelledError - if ( - sys.version_info >= (3, 11) - and (t := asyncio.current_task()) is not None - ): - t.uncancel() # type: ignore[union-attr] - if self._cancel_reason is not None or self._deleting: - raise + await self._await_temporal_operation( + handle._start_fut, + apply_child_cancel_error, + reraise_on_workflow_cancellation=True, + ) + return handle async def _outbound_start_nexus_operation( self, input: StartNexusOperationInput[Any, OutputT] @@ -2074,22 +2125,18 @@ async def _outbound_start_nexus_operation( handle: _NexusOperationHandle[OutputT] async def operation_handle_fn() -> OutputT: - while True: - try: - return cast(OutputT, await asyncio.shield(handle._result_fut)) - except asyncio.CancelledError: - cancel_command = self._add_command() - handle._apply_cancel_command(cancel_command) - # Clear the cancellation counter on Python 3.11+ so the - # next await does not immediately re-raise CancelledError - if ( - sys.version_info >= (3, 11) - and (t := asyncio.current_task()) is not None - ): - t.uncancel() # type: ignore[union-attr] + return cast( + OutputT, + await self._await_temporal_operation( + handle._result_fut, + lambda _err, command: handle._apply_cancel_command(command), + ), + ) payload_converter = ( - temporalio.nexus.system.get_payload_converter() + temporalio.nexus.system._get_payload_converter( + self._workflow_context_payload_converter + ) if temporalio.nexus.system.is_system_endpoint(input.endpoint) else self._context_free_payload_converter ) @@ -2103,22 +2150,12 @@ async def operation_handle_fn() -> OutputT: handle._apply_schedule_command() self._pending_nexus_operations[handle._seq] = handle - while True: - try: - await asyncio.shield(handle._start_fut) - return handle - except asyncio.CancelledError: - cancel_command = self._add_command() - handle._apply_cancel_command(cancel_command) - # Clear the cancellation counter on Python 3.11+ so the - # next await does not immediately re-raise CancelledError - if ( - sys.version_info >= (3, 11) - and (t := asyncio.current_task()) is not None - ): - t.uncancel() # type: ignore[union-attr] - if self._cancel_reason is not None or self._deleting: - raise + await self._await_temporal_operation( + handle._start_fut, + lambda _err, command: handle._apply_cancel_command(command), + reraise_on_workflow_cancellation=True, + ) + return handle #### Miscellaneous helpers #### # These are in alphabetical order. @@ -2127,6 +2164,14 @@ def _add_command(self) -> temporalio.bridge.proto.workflow_commands.WorkflowComm self._assert_not_read_only("add command") return self._current_completion.successful.commands.add() + def _workflow_logic_flag_enabled(self, flag: _WorkflowLogicFlag) -> bool: + if flag in self._current_internal_flags: + return True + if self._is_replaying or flag not in self._default_workflow_logic_flags: + return False + self._current_completion.successful.used_internal_flags.append(flag) + return True + @contextmanager def _as_read_only(self, *, in_query_or_validator: bool) -> Iterator[None]: prev_read_only = self._read_only @@ -2151,6 +2196,56 @@ def _assert_not_read_only( f"While in read-only function, action attempted: {action_attempted}" ) + async def _await_temporal_operation( + self, + fut: asyncio.Future[_T], + apply_cancel: Callable[ + [ + asyncio.CancelledError, + temporalio.bridge.proto.workflow_commands.WorkflowCommand, + ], + None, + ], + *, + completed_cancellation_flag: _WorkflowLogicFlag | None = None, + reraise_on_workflow_cancellation: bool = False, + ) -> _T: + while True: + try: + # Protect the operation's result from task cancellation so a + # Temporal cancellation command can decide its outcome. The + # custom shield also avoids spurious error logs on Python 3.11+. + return await _shield_await(fut) + except asyncio.CancelledError as err: + if fut.done(): + # Retrying the shield after both futures become ready would + # return the result and erase the task cancellation. + if self._single_batch_activation: + raise + if completed_cancellation_flag is not None and ( + not self._is_replaying + or completed_cancellation_flag in self._current_internal_flags + ): + self._current_completion.successful.used_internal_flags.append( + completed_cancellation_flag + ) + raise + + apply_cancel(err, self._add_command()) + + # Clear the cancellation counter on Python 3.11+ so the next + # await does not immediately re-raise CancelledError. + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) is not None + ): + task.uncancel() # type: ignore[union-attr] + + if reraise_on_workflow_cancellation and ( + self._cancel_reason is not None or self._deleting + ): + raise + async def _cancel_external_workflow( self, # Should not have seq set @@ -2641,22 +2736,14 @@ async def _signal_external_workflow( ) self._pending_external_signals[seq] = (done_fut, target_workflow_id) + def apply_cancel( + _err: asyncio.CancelledError, + command: temporalio.bridge.proto.workflow_commands.WorkflowCommand, + ) -> None: + command.cancel_signal_workflow.seq = seq + # Wait until completed or cancelled - while True: - try: - # We have to shield because we don't want the future itself - # to be cancelled - return await asyncio.shield(done_fut) - except asyncio.CancelledError: - cancel_command = self._add_command() - cancel_command.cancel_signal_workflow.seq = seq - # Clear the cancellation counter on Python 3.11+ so the - # next await does not immediately re-raise CancelledError - if ( - sys.version_info >= (3, 11) - and (t := asyncio.current_task()) is not None - ): - t.uncancel() # type: ignore[union-attr] + return await self._await_temporal_operation(done_fut, apply_cancel) def _stack_trace(self) -> str: stacks = [] @@ -3849,3 +3936,11 @@ class _WorkflowLogicFlag(IntEnum): """Flags that may be set on task/activation completion to differentiate new from old workflow behavior.""" RAISE_ON_CANCELLING_COMPLETED_ACTIVITY = 1 + PROCESS_WORKFLOW_ACTIVATION_JOBS_AS_SINGLE_BATCH = 2 + + +# TODO: Enable PROCESS_WORKFLOW_ACTIVATION_JOBS_AS_SINGLE_BATCH by default after +# two published SDK releases have recognized flag 2. When enabling it, remove the +# explicit overrides for this flag from tests/worker/test_workflow.py, then remove +# this reminder. +_DEFAULT_ENABLED_WORKFLOW_LOGIC_FLAGS: frozenset[_WorkflowLogicFlag] = frozenset() diff --git a/temporalio/worker/workflow_sandbox/_restrictions.py b/temporalio/worker/workflow_sandbox/_restrictions.py index 78b7a0363..23774fcd2 100644 --- a/temporalio/worker/workflow_sandbox/_restrictions.py +++ b/temporalio/worker/workflow_sandbox/_restrictions.py @@ -769,7 +769,9 @@ def _public_callables(parent: Any, *, exclude: set[str] = set()) -> set[str]: "urllib": SandboxMatcher( children={"request": SandboxMatcher.all_uses}, ), - "uuid": SandboxMatcher(use={"uuid1", "uuid4"}, only_runtime=True), + # uuid7 only exists in the stdlib on Python 3.14+; matching a + # nonexistent attribute is harmless on older versions + "uuid": SandboxMatcher(use={"uuid1", "uuid4", "uuid7"}, only_runtime=True), "webbrowser": SandboxMatcher.all_uses, "xmlrpc": SandboxMatcher.all_uses, "zipfile": SandboxMatcher( @@ -860,6 +862,8 @@ def set_on_proxy(self, v: _RestrictedProxy) -> None: class _RestrictedProxyLookup: + bind_func: Callable[[_RestrictedProxy, Any], Callable[..., Any]] | None + def __init__( self, access_func: Callable | None = None, @@ -951,12 +955,12 @@ def __init__( def bind_f(instance: _RestrictedProxy, obj: Any) -> Callable: def i_op(self: Any, other: Any) -> _RestrictedProxy: - f(self, other) # type: ignore + access_func(self, other) # type: ignore return instance return i_op.__get__(obj, type(obj)) # type: ignore - self.bind_f = bind_f + self.bind_func = bind_f _OpF = TypeVar("_OpF", bound=Callable[..., Any]) diff --git a/temporalio/worker/workflow_sandbox/_runner.py b/temporalio/worker/workflow_sandbox/_runner.py index b11c9b8c4..7f06bfcd6 100644 --- a/temporalio/worker/workflow_sandbox/_runner.py +++ b/temporalio/worker/workflow_sandbox/_runner.py @@ -79,7 +79,7 @@ def prepare_workflow(self, defn: temporalio.workflow._Definition) -> None: # Just create with fake info which validates self.create_instance( WorkflowInstanceDetails( - payload_converter_class=temporalio.converter.DataConverter.default.payload_converter_class, + payload_converter_factory=temporalio.converter.DataConverter.default._new_payload_converter, failure_converter_class=temporalio.converter.DataConverter.default.failure_converter_class, interceptor_classes=[], defn=defn, @@ -89,6 +89,7 @@ def prepare_workflow(self, defn: temporalio.workflow._Definition) -> None: extern_functions={}, disable_eager_activity_execution=False, worker_level_failure_exception_types=self._worker_level_failure_exception_types, + patch_activation_callback=None, last_completion_result=Payloads(), last_failure=Failure(), ), diff --git a/temporalio/workflow/__init__.py b/temporalio/workflow/__init__.py index 3d5a65c77..fa2681139 100644 --- a/temporalio/workflow/__init__.py +++ b/temporalio/workflow/__init__.py @@ -94,6 +94,7 @@ upsert_memo, upsert_search_attributes, uuid4, + uuid7, wait_condition, ) from ._definition import ( @@ -225,6 +226,7 @@ "upsert_memo", "upsert_search_attributes", "uuid4", + "uuid7", "wait_condition", "DynamicWorkflowConfig", "defn", diff --git a/temporalio/workflow/_context.py b/temporalio/workflow/_context.py index 23c943cd0..b33f83150 100644 --- a/temporalio/workflow/_context.py +++ b/temporalio/workflow/_context.py @@ -65,6 +65,7 @@ "upsert_memo", "upsert_search_attributes", "uuid4", + "uuid7", "wait_condition", ] @@ -901,6 +902,33 @@ def uuid4() -> uuid.UUID: return uuid.UUID(bytes=random().getrandbits(16 * 8).to_bytes(16, "big"), version=4) +def uuid7() -> uuid.UUID: + """Get a new, determinism-safe v7 UUID based on :py:func:`time_ns` and + :py:func:`random`. + + Per RFC 9562, the UUID's leading 48 bits are the current workflow time as + milliseconds since the epoch, so UUIDs from successive workflow tasks sort + by creation time. The remaining 74 bits are random. UUIDs generated within + the same workflow task share the same workflow time and are not guaranteed + to be monotonically ordered with respect to one another. + + Note, this UUID is not cryptographically safe and should not be used for + security purposes. + + Returns: + A deterministically-seeded v7 UUID. + """ + # uuid.UUID's version parameter only accepts 1-5 before Python 3.14, so + # the version and variant bits are set manually. + unix_ts_ms = (time_ns() // 1_000_000) & 0xFFFF_FFFF_FFFF + rand = random() + rand_a = rand.getrandbits(12) + rand_b = rand.getrandbits(62) + return uuid.UUID( + int=(unix_ts_ms << 80) | (0x7 << 76) | (rand_a << 64) | (0b10 << 62) | rand_b + ) + + async def sleep(duration: float | timedelta, *, summary: str | None = None) -> None: """Sleep for the given duration. @@ -928,6 +956,11 @@ async def wait_condition( This function returns when the callback returns true (invoked each loop iteration) or the timeout has been reached. + Importantly, using `None` value for ``timeout`` means that no Temporal timer is created. This + means changing from `None` to a positive value (or the inverse) constitutes a nondeterministic + change to workflow code. Using ``0`` will cause this function to immediately throw a timeout and + should not be used. + Args: fn: Non-async callback that accepts no parameters and returns a boolean. timeout: Optional number of seconds to wait until throwing diff --git a/temporalio/workflow/_definition.py b/temporalio/workflow/_definition.py index c1ce21169..a14e7640a 100644 --- a/temporalio/workflow/_definition.py +++ b/temporalio/workflow/_definition.py @@ -73,7 +73,7 @@ def defn( cannot be set if dynamic is set. sandboxed: Whether the workflow should run in a sandbox. Default is true. - dynamic: If true, this activity will be dynamic. Dynamic workflows have + dynamic: If true, this workflow will be dynamic. Dynamic workflows have to accept a single 'Sequence[RawValue]' parameter. This cannot be set to true if name is present. failure_exception_types: The types of exceptions that, if a diff --git a/temporalio/workflow/_sandbox.py b/temporalio/workflow/_sandbox.py index 6f1d4569a..32a053604 100644 --- a/temporalio/workflow/_sandbox.py +++ b/temporalio/workflow/_sandbox.py @@ -268,9 +268,9 @@ def process( else None, ) - kwargs["extra"] = {**extra, **(kwargs.get("extra") or {})} - if msg_extra: - msg = f"{msg} ({msg_extra})" + kwargs["extra"] = {**extra, **(kwargs.get("extra") or {})} + if msg_extra: + msg = f"{msg} ({msg_extra})" return msg, kwargs def log( diff --git a/tests/__init__.py b/tests/__init__.py index 4725d3a7e..af97849fe 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +1 @@ -DEV_SERVER_DOWNLOAD_VERSION = "v1.7.1-system-nexus-operations" +DEV_SERVER_DOWNLOAD_VERSION = "v1.7.4-standalone-nexus-operations" diff --git a/tests/conftest.py b/tests/conftest.py index e01773e7e..a9c6abb89 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,6 +10,7 @@ from opentelemetry.util._once import Once from temporalio.client import Client +from temporalio.envconfig import ClientConfigProfile from temporalio.testing import WorkflowEnvironment from temporalio.worker import SharedStateManager from tests.helpers.worker import ExternalPythonWorker, ExternalWorker @@ -58,10 +59,43 @@ def pytest_addoption(parser): # type: ignore[reportMissingParameterType] "-E", "--workflow-environment", default="local", - help="Which workflow environment to use ('local', 'time-skipping', or ip:port for existing server)", + help="Which workflow environment to use ('local', 'time-skipping', 'envconfig', or ip:port for existing server)", ) +def _uses_envconfig_server(env_type: str) -> bool: + return env_type == "envconfig" + + +def pytest_configure(config: pytest.Config) -> None: + config.addinivalue_line( + "markers", + "requires_local_server: test requires local-server-only behavior and cannot run against an envconfig server", + ) + + +def pytest_collection_modifyitems( + config: pytest.Config, items: list[pytest.Item] +) -> None: + if not _uses_envconfig_server(config.getoption("--workflow-environment")): + return + skip_local_only = pytest.mark.skip( + reason="requires a local Temporal server, not the configured envconfig server" + ) + for item in items: + if item.get_closest_marker("requires_local_server"): + item.add_marker(skip_local_only) + + +async def _create_env_from_envconfig() -> WorkflowEnvironment: + config = ClientConfigProfile.load().to_client_connect_config() + if not config.get("target_host"): + raise ValueError( + "An envconfig workflow environment requires TEMPORAL_ADDRESS or an envconfig profile with an address" + ) + return WorkflowEnvironment.from_client(await Client.connect(**config)) + + @pytest.fixture(scope="session") def event_loop(): loop = asyncio.get_event_loop_policy().new_event_loop() # type: ignore[reportDeprecated] @@ -100,7 +134,9 @@ def env_type(request: pytest.FixtureRequest) -> str: @pytest_asyncio.fixture(scope="session") # type: ignore[reportUntypedFunctionDecorator] async def env(env_type: str) -> AsyncGenerator[WorkflowEnvironment, None]: - if env_type == "local": + if _uses_envconfig_server(env_type): + env = await _create_env_from_envconfig() + elif env_type == "local": env = await WorkflowEnvironment.start_local( dev_server_extra_args=[ "--dynamic-config-value", @@ -132,7 +168,7 @@ async def env(env_type: str) -> AsyncGenerator[WorkflowEnvironment, None]: "--dynamic-config-value", "history.enableTransitionHistory=true", "--dynamic-config-value", - "history.enableChasmCallbacks=true", + "history.enableCHASMCallbacks=true", "--dynamic-config-value", "history.enableCHASMSignalBacklinks=true", "--dynamic-config-value", @@ -141,6 +177,10 @@ async def env(env_type: str) -> AsyncGenerator[WorkflowEnvironment, None]: 'system.system.refreshNexusEndpointsMinWait="0s"', "--dynamic-config-value", "history.enableSignalWithStartFromWorkflow=true", + "--dynamic-config-value", + "history.enableUpdateCallbacks=true", + "--dynamic-config-value", + "activity.enableCallbacks=true", ], dev_server_download_version=DEV_SERVER_DOWNLOAD_VERSION, ) diff --git a/tests/contrib/aws/s3driver/test_s3driver.py b/tests/contrib/aws/s3driver/test_s3driver.py index 19b3419f8..05628a7f2 100644 --- a/tests/contrib/aws/s3driver/test_s3driver.py +++ b/tests/contrib/aws/s3driver/test_s3driver.py @@ -336,6 +336,88 @@ async def test_key_urlencodes_namespace( == f"v0/ns/my%2Fns%231/wt/null/wi/wf1/ri/null/d/sha256/{expected_hash}" ) + async def test_key_preserves_s3_safe_special_chars( + self, driver_client: S3StorageDriverClient + ) -> None: + """S3's safe special characters are left unescaped per the key spec.""" + driver = S3StorageDriver(client=driver_client, bucket=BUCKET) + payload = make_payload() + # Every special character S3 lists as safe: ! - _ . * ' ( ) + ctx = make_workflow_context(namespace="ns1", workflow_id="!-_.*'()") + [claim] = await driver.store(ctx, [payload]) + expected_hash = hashlib.sha256(payload.SerializeToString()).hexdigest() + assert ( + claim.claim_data["key"] + == f"v0/ns/ns1/wt/null/wi/!-_.*'()/ri/null/d/sha256/{expected_hash}" + ) + + async def test_key_escapes_tilde( + self, driver_client: S3StorageDriverClient + ) -> None: + """Tilde is not in S3's safe set and must be percent-encoded (%7E).""" + driver = S3StorageDriver(client=driver_client, bucket=BUCKET) + payload = make_payload() + ctx = make_workflow_context(namespace="ns1", workflow_id="wf~1") + [claim] = await driver.store(ctx, [payload]) + expected_hash = hashlib.sha256(payload.SerializeToString()).hexdigest() + assert ( + claim.claim_data["key"] + == f"v0/ns/ns1/wt/null/wi/wf%7E1/ri/null/d/sha256/{expected_hash}" + ) + + async def test_key_escapes_non_ascii_as_utf8_bytes( + self, driver_client: S3StorageDriverClient + ) -> None: + """Non-ASCII characters are percent-encoded byte by byte from UTF-8.""" + driver = S3StorageDriver(client=driver_client, bucket=BUCKET) + payload = make_payload() + # "é" is U+00E9, which is 0xC3 0xA9 in UTF-8. + ctx = make_workflow_context(namespace="ns1", workflow_id="café") + [claim] = await driver.store(ctx, [payload]) + expected_hash = hashlib.sha256(payload.SerializeToString()).hexdigest() + assert ( + claim.claim_data["key"] + == f"v0/ns/ns1/wt/null/wi/caf%C3%A9/ri/null/d/sha256/{expected_hash}" + ) + + async def test_key_matches_spec_workflow_example( + self, driver_client: S3StorageDriverClient + ) -> None: + """Reproduces the workflow key example from the S3 key spec.""" + driver = S3StorageDriver(client=driver_client, bucket=BUCKET) + payload = make_payload() + ctx = make_workflow_context( + namespace="payments prod", + workflow_type="ChargeWorkflow", + workflow_id="order+123=abc", + run_id="3f1d6c7a-8b2e-4f7a-9d0a-87a6f95e4d31", + ) + [claim] = await driver.store(ctx, [payload]) + expected_hash = hashlib.sha256(payload.SerializeToString()).hexdigest() + assert claim.claim_data["key"] == ( + "v0/ns/payments%20prod/wt/ChargeWorkflow/wi/order%2B123%3Dabc" + f"/ri/3f1d6c7a-8b2e-4f7a-9d0a-87a6f95e4d31/d/sha256/{expected_hash}" + ) + + async def test_key_matches_spec_activity_example( + self, driver_client: S3StorageDriverClient + ) -> None: + """Reproduces the activity key example from the S3 key spec.""" + driver = S3StorageDriver(client=driver_client, bucket=BUCKET) + payload = make_payload() + ctx = make_activity_context( + namespace="payments prod", + activity_type="Capture/Charge", + activity_id="activity id+42", + run_id="9e1d1fd9-2f8a-4c40-93e2-731f31b9268b", + ) + [claim] = await driver.store(ctx, [payload]) + expected_hash = hashlib.sha256(payload.SerializeToString()).hexdigest() + assert claim.claim_data["key"] == ( + "v0/ns/payments%20prod/at/Capture%2FCharge/ai/activity%20id%2B42" + f"/ri/9e1d1fd9-2f8a-4c40-93e2-731f31b9268b/d/sha256/{expected_hash}" + ) + async def test_key_urlencoded_roundtrip( self, driver_client: S3StorageDriverClient ) -> None: diff --git a/tests/contrib/aws/s3driver/test_s3driver_worker.py b/tests/contrib/aws/s3driver/test_s3driver_worker.py index 61729535f..fcf17fc16 100644 --- a/tests/contrib/aws/s3driver/test_s3driver_worker.py +++ b/tests/contrib/aws/s3driver/test_s3driver_worker.py @@ -62,9 +62,7 @@ async def tmprl_client( ) -> AsyncIterator[Client]: """Temporal client wired with ExternalStorage backed by the moto S3 server.""" driver = S3StorageDriver(client=new_aioboto3_client(aioboto3_client), bucket=BUCKET) - yield await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + yield await env.connect_client( data_converter=dataclasses.replace( temporalio.converter.default(), external_storage=ExternalStorage( @@ -112,7 +110,8 @@ async def test_s3_driver_workflow_input_key( # worker stores activity input with ri=run_id — same bytes, two S3 objects. assert len(keys) == 2 assert all( - f"/ns/default/wt/LargeIOWorkflow/wi/{workflow_id}/ri/" in k for k in keys + f"/ns/{tmprl_client.namespace}/wt/LargeIOWorkflow/wi/{workflow_id}/ri/" in k + for k in keys ) # Client-side store: ri=null because run ID is not yet known. assert sum(1 for k in keys if "/ri/null/" in k) == 1 @@ -138,7 +137,10 @@ async def test_s3_driver_workflow_output_key( keys = await _list_keys(aioboto3_client) # Activity result and workflow result dedup to same key assert len(keys) == 1 - assert f"/ns/default/wt/LargeIOWorkflow/wi/{workflow_id}/ri/" in keys[0] + assert ( + f"/ns/{tmprl_client.namespace}/wt/LargeIOWorkflow/wi/{workflow_id}/ri/" + in keys[0] + ) # Run ID is known for both activity completion and workflow completion assert "/ri/null/" not in keys[0] @@ -162,7 +164,8 @@ async def test_s3_driver_workflow_activity_input_key( assert len(keys) == 2 # Both keys are under the workflow wi/ri prefix, not the activity. assert all( - f"/ns/default/wt/LargeIOWorkflow/wi/{workflow_id}/ri/" in k for k in keys + f"/ns/{tmprl_client.namespace}/wt/LargeIOWorkflow/wi/{workflow_id}/ri/" in k + for k in keys ) # Activity input is keyed under the scheduling workflow, not the activity. assert all("/ai/" not in k for k in keys) @@ -185,7 +188,10 @@ async def test_s3_driver_workflow_activity_output_key( keys = await _list_keys(aioboto3_client) # Activity result and workflow result are both LARGE so they deduplicate to one object. assert len(keys) == 1 - assert f"/ns/default/wt/LargeIOWorkflow/wi/{workflow_id}/ri/" in keys[0] + assert ( + f"/ns/{tmprl_client.namespace}/wt/LargeIOWorkflow/wi/{workflow_id}/ri/" + in keys[0] + ) # ri=run_id for both stores (run ID is known by the time the activity completes). assert "/ri/null/" not in keys[0] @@ -214,7 +220,8 @@ async def test_s3_driver_standalone_activity_input_key( assert len(keys) == 2 # Both keyed under the activity, not a workflow. assert all( - f"/ns/default/at/large_io_activity/ai/{activity_id}/ri/" in k for k in keys + f"/ns/{tmprl_client.namespace}/at/large_io_activity/ai/{activity_id}/ri/" in k + for k in keys ) assert all("/wt/" not in k for k in keys) # Client-side store does not have run ID information @@ -244,7 +251,10 @@ async def test_s3_driver_standalone_activity_output_key( keys = await _list_keys(aioboto3_client) # Only the output is large; keyed under the activity. assert len(keys) == 1 - assert f"/ns/default/at/large_output_activity/ai/{activity_id}/ri/" in keys[0] + assert ( + f"/ns/{tmprl_client.namespace}/at/large_output_activity/ai/{activity_id}/ri/" + in keys[0] + ) assert "/ri/null/" not in keys[0] assert "/wt/" not in keys[0] @@ -337,7 +347,10 @@ async def test_s3_driver_child_workflow_input_key( # Child input is the only large payload — stored under the child's wi/ri. assert len(keys) == 1 # Keyed under the child: child input is stored in the child's context. - assert f"/ns/default/wt/ChildWorkflow/wi/{child_workflow_id}/ri/" in keys[0] + assert ( + f"/ns/{tmprl_client.namespace}/wt/ChildWorkflow/wi/{child_workflow_id}/ri/" + in keys[0] + ) async def test_s3_driver_identified_casing( @@ -359,7 +372,8 @@ async def test_s3_driver_identified_casing( assert len(keys) == 2 # Workflow ID is percent-encoded but casing is preserved verbatim. assert all( - f"/ns/default/wt/LargeIOWorkflow/wi/{workflow_id}/ri/" in k for k in keys + f"/ns/{tmprl_client.namespace}/wt/LargeIOWorkflow/wi/{workflow_id}/ri/" in k + for k in keys ), "Workflow ID should preserve original case in the key" @@ -386,7 +400,8 @@ async def test_s3_driver_content_dedup( assert len(keys) == 2 # Both are under the same workflow wi/ri prefix despite crossing activity boundaries. assert all( - f"/ns/default/wt/DocumentIngestionWorkflow/wi/{workflow_id}/ri/" in k + f"/ns/{tmprl_client.namespace}/wt/DocumentIngestionWorkflow/wi/{workflow_id}/ri/" + in k for k in keys ) # The two keys differ by content hash only. @@ -469,9 +484,7 @@ async def test_s3_store_failure_surfaces_in_workflow_history( aws_secret_access_key="testing", ) as client: driver = S3StorageDriver(client=new_aioboto3_client(client), bucket=bad_bucket) - bad_client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + bad_client = await env.connect_client( data_converter=dataclasses.replace( temporalio.converter.default(), external_storage=ExternalStorage( diff --git a/tests/contrib/deepagents/__init__.py b/tests/contrib/deepagents/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/contrib/deepagents/helpers.py b/tests/contrib/deepagents/helpers.py new file mode 100644 index 000000000..eb5ba295b --- /dev/null +++ b/tests/contrib/deepagents/helpers.py @@ -0,0 +1,16 @@ +"""Shared helpers for the Deep Agents plugin test suite.""" + +from collections import Counter + +from temporalio.api.enums.v1 import EventType +from temporalio.client import WorkflowHandle + + +async def count_scheduled_activities(handle: WorkflowHandle) -> Counter: + """Count ``ActivityTaskScheduled`` events by activity-type name.""" + counts: Counter = Counter() + async for event in handle.fetch_history_events(): + if event.event_type == EventType.EVENT_TYPE_ACTIVITY_TASK_SCHEDULED: + name = event.activity_task_scheduled_event_attributes.activity_type.name + counts[name] += 1 + return counts diff --git a/tests/contrib/deepagents/test_backends.py b/tests/contrib/deepagents/test_backends.py new file mode 100644 index 000000000..b32a6f12b --- /dev/null +++ b/tests/contrib/deepagents/test_backends.py @@ -0,0 +1,315 @@ +"""``TemporalBackend`` routes real-I/O backend ops through activities. + +A backend that touches disk or a shell must not run its operations from workflow +code. ``TemporalBackend`` wraps such a backend so each op becomes a +``deepagents.backend_op`` activity. The wrapped backend here is a plain object +(no LangChain / deepagents needed), so this boots a real server and proves the +op crosses the activity boundary. + +A state-only backend needs no wrapping — that path is covered against the real +``deepagents.StateBackend`` when it is importable. +""" + +from __future__ import annotations + +import gc +import sys +import uuid +from datetime import timedelta +from pathlib import Path + +import pytest + +from temporalio.testing import WorkflowEnvironment + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 11), reason="deepagents requires Python >= 3.11" +) +pytest.importorskip("deepagents") +pytest.importorskip("langchain_core") + +from temporalio import workflow +from temporalio.contrib.deepagents import DeepAgentsPlugin, TemporalBackend +from temporalio.contrib.deepagents._tools import ( + register_backend, + registered_backends, +) +from temporalio.worker import Worker +from tests.contrib.deepagents.helpers import count_scheduled_activities + +BACKEND_OP = "deepagents.backend_op" + + +class RecordingBackend: + """A minimal backend doing 'real' work off-workflow, exposing both halves + of the deepagents backend protocol: a sync op (``read``) and its async + twin (``aread``). The async twin is the regression-critical case — + deepagents' filesystem middleware calls ``aread``/``awrite``/…, and an + earlier op list intercepted only sync names, so agent-driven file tools + ran their I/O in-workflow.""" + + def read(self, file_path: str) -> str: + return f"contents of {file_path}" + + async def aread(self, file_path: str) -> str: + return f"acontents of {file_path}" + + +@workflow.defn +class BackendWorkflow: + @workflow.run + async def run(self, path: str) -> str: + backend = TemporalBackend( + RecordingBackend(), + activity_options={"start_to_close_timeout": timedelta(seconds=10)}, + ) + sync_out = await backend.read(path) + async_out = await backend.aread(path) + return f"{sync_out}|{async_out}" + + +# Bind deepagents symbols off the module importorskip returns: a static +# `from deepagents import ...` cannot resolve on Python 3.10 (deepagents +# needs >= 3.11), and with the package absent the type checkers mis-resolve +# the name against this same-named test directory. +_deepagents_mod = pytest.importorskip("deepagents") +_backends_mod = pytest.importorskip("deepagents.backends") +create_deep_agent = _deepagents_mod.create_deep_agent +FilesystemBackend = _backends_mod.FilesystemBackend +StateBackend = _backends_mod.StateBackend + + +# A state-only backend is pure workflow state and must NOT schedule an activity. +@workflow.defn +class StateBackendWorkflow: + @workflow.run + async def run(self) -> str: + backend = StateBackend() + # Merely holding a StateBackend schedules no activity; it is not + # wrapped. Return the class provenance so the assertion is on a real + # runtime property rather than a statically-decidable comparison. + return type(backend).__module__ + + +# The full agent-level seam: a REAL Deep Agent whose BUILT-IN file tools drive a +# REAL FilesystemBackend through TemporalBackend. This is the path a fake-backend +# test cannot cover: deepagents' filesystem middleware calls the ASYNC protocol +# (`awrite` / `aread`), and the ops return protocol dataclasses (WriteResult / +# ReadResult) that must survive the activity boundary as real objects — the +# middleware reads their attributes in-workflow. +@workflow.defn +class FilesystemAgentWorkflow: + @workflow.run + async def run(self, root_dir: str) -> str: + backend = TemporalBackend( + FilesystemBackend(root_dir=root_dir, virtual_mode=True), + activity_options={"start_to_close_timeout": timedelta(seconds=10)}, + ) + agent = create_deep_agent( + model="anthropic:claude-sonnet-4-5", + backend=backend, + system_prompt="Write the note, read it back, then report it.", + ) + result = await agent.ainvoke( + {"messages": [{"role": "user", "content": "Note 'hello' down."}]} + ) + return str(result["messages"][-1].content) + + +@pytest.mark.asyncio +async def test_temporal_backend_op_activity(env: WorkflowEnvironment) -> None: + plugin = DeepAgentsPlugin() + async with Worker( + env.client, + task_queue="da-backend", + workflows=[BackendWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + BackendWorkflow.run, + "notes.txt", + id=f"da-backend-{uuid.uuid4()}", + task_queue="da-backend", + ) + out = await handle.result() + + assert out == "contents of notes.txt|acontents of notes.txt" + counts = await count_scheduled_activities(handle) + # One activity per op — the sync read AND the async aread both cross. + assert counts[BACKEND_OP] == 2, counts + + +def test_temporal_backend_unregisters_on_gc() -> None: + # A wrapper is typically constructed per workflow run; its registry entry + # must not outlive it, or a long-lived worker leaks one entry per run. + inner = RecordingBackend() + before = set(registered_backends()) + wrapper = TemporalBackend(inner) + (ref,) = set(registered_backends()) - before + assert registered_backends()[ref] is inner + del wrapper + gc.collect() + assert ref not in registered_backends() + + +def test_temporal_backend_gc_keeps_reregistered_ref() -> None: + # Refs are deterministic per run: after a cache eviction, a replay + # re-registers the SAME ref with a fresh inner backend. The evicted + # wrapper's GC cleanup must leave that live registration alone. + before = set(registered_backends()) + wrapper = TemporalBackend(RecordingBackend()) + (ref,) = set(registered_backends()) - before + replacement = RecordingBackend() + register_backend(ref, replacement) + del wrapper + gc.collect() + assert registered_backends().get(ref) is replacement + registered_backends().pop(ref, None) + + +@pytest.mark.asyncio +async def test_state_backend_in_workflow(env: WorkflowEnvironment) -> None: + # A state-only backend is pure workflow state and must NOT schedule an + # activity. Exercised against the real StateBackend when deepagents is present. + plugin = DeepAgentsPlugin() + async with Worker( + env.client, + task_queue="da-state-backend", + workflows=[StateBackendWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + StateBackendWorkflow.run, + id=f"da-state-backend-{uuid.uuid4()}", + task_queue="da-state-backend", + ) + assert (await handle.result()).startswith("deepagents") + counts = await count_scheduled_activities(handle) + assert counts[BACKEND_OP] == 0, counts + + +@pytest.mark.asyncio +async def test_agent_builtin_file_tools_route_backend_ops( + env: WorkflowEnvironment, tmp_path: Path +) -> None: + """An unmodified agent's built-in write_file/read_file tools cross the + activity boundary when the backend is TemporalBackend-wrapped — under + ``max_cached_workflows=0``, so every workflow task replays from history. + + Regression: an earlier op list intercepted only sync method names, so the + middleware's async calls (`awrite`/`aread`) forwarded to the inner backend + and ran real disk I/O in-workflow. This test fails if that recurs, if the + protocol result dataclasses stop surviving the activity boundary, or if + replay diverges. + """ + from langchain_core.messages import AIMessage # real lib; guarded above + + write_turn = AIMessage( + content="", + tool_calls=[ + { + "name": "write_file", + "args": {"file_path": "/notes.txt", "content": "hello"}, + "id": "call-write", + } + ], + ) + read_turn = AIMessage( + content="", + tool_calls=[ + { + "name": "read_file", + "args": {"file_path": "/notes.txt"}, + "id": "call-read", + } + ], + ) + final = AIMessage(content="The note says: hello") + from temporalio.contrib.deepagents.testing import mock_model_provider + + plugin = DeepAgentsPlugin( + model_provider=mock_model_provider([write_turn, read_turn, final]), + ) + async with Worker( + env.client, + task_queue="da-fs-agent", + workflows=[FilesystemAgentWorkflow], + plugins=[plugin], + max_cached_workflows=0, + ): + handle = await env.client.start_workflow( + FilesystemAgentWorkflow.run, + str(tmp_path), + id=f"da-fs-agent-{uuid.uuid4()}", + task_queue="da-fs-agent", + ) + out = await handle.result() + + assert "hello" in out + # The write really happened on disk — in the activity, not the workflow. + assert (tmp_path / "notes.txt").read_text() == "hello" + counts = await count_scheduled_activities(handle) + # Exactly one backend_op per file tool call (awrite + aread), three model turns. + assert counts[BACKEND_OP] == 2, counts + assert counts["deepagents.invoke_model"] == 3, counts + + +@workflow.defn +class DefaultBackendGrepWorkflow: + @workflow.run + async def run(self, prompt: str) -> str: + # No backend argument: deepagents uses its default state-only backend. + agent = create_deep_agent(model="anthropic:claude-sonnet-4-5") + result = await agent.ainvoke( + {"messages": [{"role": "user", "content": prompt}]} + ) + return str(result["messages"][-1].content) + + +@pytest.mark.asyncio +async def test_builtin_tool_on_default_backend_runs_in_workflow( + env: WorkflowEnvironment, +) -> None: + """A built-in tool call (grep) on the DEFAULT state backend runs inline + in the workflow — no activity, no thread hop — under + ``max_cached_workflows=0`` so every task replays from history. + + Regression: ``BackendProtocol``'s async defaults wrap their sync twins in + ``asyncio.to_thread``, which the deterministic workflow event loop + rejects with ``NotImplementedError``. A real model's first spontaneous + ``grep``/``read_file`` call crashed the workflow task; scripted tests + that never invoked built-ins on the default backend sailed past it. The + plugin now runs the sync twin inline when ``workflow.in_workflow()``. + """ + from langchain_core.messages import AIMessage + + from temporalio.contrib.deepagents.testing import mock_model_provider + + grep_turn = AIMessage( + content="", + tool_calls=[{"name": "grep", "args": {"pattern": "hello"}, "id": "call-grep"}], + ) + final = AIMessage(content="No matches found; done.") + plugin = DeepAgentsPlugin( + model_provider=mock_model_provider([grep_turn, final]), + ) + async with Worker( + env.client, + task_queue="da-default-backend", + workflows=[DefaultBackendGrepWorkflow], + plugins=[plugin], + max_cached_workflows=0, + ): + handle = await env.client.start_workflow( + DefaultBackendGrepWorkflow.run, + "Grep the workspace for 'hello'.", + id=f"da-default-backend-{uuid.uuid4()}", + task_queue="da-default-backend", + ) + out = await handle.result() + + assert "done" in out + counts = await count_scheduled_activities(handle) + # The state-backend op stays in-workflow: model turns are the ONLY activities. + assert counts[BACKEND_OP] == 0, counts + assert counts["deepagents.invoke_model"] == 2, counts diff --git a/tests/contrib/deepagents/test_checkpointer.py b/tests/contrib/deepagents/test_checkpointer.py new file mode 100644 index 000000000..851bbb018 --- /dev/null +++ b/tests/contrib/deepagents/test_checkpointer.py @@ -0,0 +1,98 @@ +"""Checkpointing follows the ``contrib.langgraph`` precedent. + +The zero-config default is an in-workflow ``InMemorySaver`` rehydrated by +deterministic replay; no bespoke checkpointer adapter ships. A user-supplied +durable checkpointer would run its own I/O from workflow code, which is not +replay-safe, so the plugin warns (respecting the choice rather than hard-failing) +and points at the snapshot + continue-as-new path. +""" + +from __future__ import annotations + +import sys +import uuid +import warnings + +import pytest + +from temporalio.testing import WorkflowEnvironment + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 11), reason="deepagents requires Python >= 3.11" +) +pytest.importorskip("deepagents") +pytest.importorskip("langchain_core") + +from temporalio import workflow +from temporalio.contrib.deepagents.workflow import warn_durable_checkpointer + +# Bind deepagents symbols off the module importorskip returns: a static +# `from deepagents import ...` cannot resolve on Python 3.10 (deepagents +# needs >= 3.11), and with the package absent the type checkers mis-resolve +# the name against this same-named test directory. +create_deep_agent = pytest.importorskip("deepagents").create_deep_agent + + +class _DurableSaver: + """Stand-in for a checkpointer that does its own database I/O.""" + + +class InMemorySaver: + """Same class name LangGraph's in-workflow saver uses.""" + + +@workflow.defn +class CheckpointWorkflow: + @workflow.run + async def run(self, prompt: str) -> str: + agent = create_deep_agent(model="fake:model") + result = await agent.ainvoke( + {"messages": [{"role": "user", "content": prompt}]} + ) + return str(result["messages"][-1].content) + + +def test_durable_checkpointer_warns() -> None: + # None and in-workflow savers are silent... + with warnings.catch_warnings(record=True) as records: + warnings.simplefilter("always") + warn_durable_checkpointer(None) + warn_durable_checkpointer(InMemorySaver()) + assert not records, [str(r.message) for r in records] + + # ...a durable saver warns (respect the choice, do not hard-fail). + with pytest.warns(UserWarning, match="durable checkpointer"): + warn_durable_checkpointer(_DurableSaver()) + + +@pytest.mark.asyncio +async def test_default_saver_rehydrates(env: WorkflowEnvironment) -> None: + # Against the real deepagents default (in-workflow InMemorySaver): the agent + # runs and its recorded history replays cleanly, proving replay rehydrates + # the in-workflow checkpoint state with no external checkpointer. + pytest.importorskip("deepagents") + pytest.importorskip("langchain_core") + + from temporalio.contrib.deepagents import DeepAgentsPlugin + from temporalio.contrib.deepagents.testing import mock_model_provider + from temporalio.worker import Replayer, Worker + + plugin = DeepAgentsPlugin(model_provider=mock_model_provider(["Checkpointed."])) + async with Worker( + env.client, + task_queue="da-ckpt", + workflows=[CheckpointWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + CheckpointWorkflow.run, + "hello", + id=f"da-ckpt-{uuid.uuid4()}", + task_queue="da-ckpt", + ) + await handle.result() + history = await handle.fetch_history() + + await Replayer( + workflows=[CheckpointWorkflow], plugins=[DeepAgentsPlugin()] + ).replay_workflow(history) diff --git a/tests/contrib/deepagents/test_continue_as_new.py b/tests/contrib/deepagents/test_continue_as_new.py new file mode 100644 index 000000000..c5add7cf7 --- /dev/null +++ b/tests/contrib/deepagents/test_continue_as_new.py @@ -0,0 +1,167 @@ +"""Continue-as-new state carry for long-running Deep Agents. + +``run_deep_agent(continue_as_new_after=...)`` keeps a long conversation from +bloating workflow history: once the current turn finishes past the threshold and +there is still pending work, it snapshots the accumulated messages plus the +model/tool result cache and continues into a fresh run. These tests use a plain +fake agent (no LangChain needed) so they boot a real Temporal server and exercise +the continue-as-new machinery end to end. +""" + +from __future__ import annotations + +import sys +import uuid +from typing import Any + +import pytest + +from temporalio.testing import WorkflowEnvironment + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 11), reason="deepagents requires Python >= 3.11" +) +from temporalio import workflow +from temporalio.contrib.deepagents import DeepAgentsPlugin, _serde, run_deep_agent +from temporalio.worker import Worker + + +class FakeAgent: + """A stand-in compiled agent that appends a step and reports a todo. + + It is *not* a LangChain object — it just satisfies the ``ainvoke`` shape + ``run_deep_agent`` drives, so the continue-as-new path can be tested without + a model provider or the LangChain import tree. + """ + + async def ainvoke(self, input: Any) -> dict: + messages = list(input.get("messages", [])) if isinstance(input, dict) else [] + messages = [*messages, "step"] + done = len(messages) >= 3 + return { + "messages": messages, + "todos": [ + {"content": "work", "status": "completed" if done else "pending"} + ], + } + + +@workflow.defn +class ContinueAsNewWorkflow: + @workflow.run + async def run(self, input: dict, state_snapshot: dict | None = None) -> dict: + # Threshold of 1 means: continue-as-new as soon as there is pending work, + # which the fake agent reports until the conversation reaches 3 messages. + return await run_deep_agent( + FakeAgent(), + input, + continue_as_new_after=1, + state_snapshot=state_snapshot, + ) + + +@pytest.mark.asyncio +async def test_can_threshold_and_cache(env: WorkflowEnvironment) -> None: + plugin = DeepAgentsPlugin() + async with Worker( + env.client, + task_queue="da-can", + workflows=[ContinueAsNewWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + ContinueAsNewWorkflow.run, + {"messages": ["start"]}, + id=f"da-can-{uuid.uuid4()}", + task_queue="da-can", + ) + result = await handle.result() + + # The only way the conversation reaches >= 3 messages is if the snapshot from + # the pre-continue-as-new run was carried into the continued run and merged. + assert len(result["messages"]) >= 3, result + assert result["todos"][0]["status"] == "completed" + + +def test_state_snapshot_roundtrip() -> None: + # The result cache carried in a snapshot rehydrates to the same hits, so work + # done before a continue-as-new is reused, not recomputed, afterwards. + _serde.set_result_cache({}) + key = _serde.cache_key("model", "fake:model", [["m"], []]) + _serde.cache_put(key, {"dumped": "message"}) + snapshot = _serde.result_cache_snapshot() + assert snapshot and key in snapshot + + # Simulate the continued run: a fresh cache seeded from the snapshot. + _serde.set_result_cache(dict(snapshot)) + hit, value = _serde.cache_lookup(key) + assert hit and value == {"dumped": "message"} + + +class SlowFakeAgent: + """Like ``FakeAgent`` but each turn burns timers so a single run's history + grows past the dev server's continue-as-new suggestion threshold (the test + env pins ``limit.historyCount.suggestContinueAsNew`` low).""" + + async def ainvoke(self, input: Any) -> dict: + for _ in range(20): + await workflow.sleep(0.001) + messages = list(input.get("messages", [])) if isinstance(input, dict) else [] + messages = [*messages, "step"] + done = len(messages) >= 3 + return { + "messages": messages, + "todos": [ + {"content": "work", "status": "completed" if done else "pending"} + ], + } + + +@workflow.defn +class SuggestedCanWorkflow: + @workflow.run + async def run(self, input: dict, state_snapshot: dict | None = None) -> dict: + # No continue_as_new_after: the default follows the server's own + # is_continue_as_new_suggested() signal. + return await run_deep_agent( + SlowFakeAgent(), + input, + state_snapshot=state_snapshot, + ) + + +@pytest.mark.asyncio +async def test_can_defaults_to_server_suggestion( + env: WorkflowEnvironment, env_type: str +) -> None: + """With ``continue_as_new_after`` unset, the driver continues-as-new when + the SERVER suggests it (history count/size), not on a fixed threshold.""" + if env_type != "local": + pytest.skip("needs the local dev server's low suggestContinueAsNew threshold") + plugin = DeepAgentsPlugin() + async with Worker( + env.client, + task_queue="da-can-suggested", + workflows=[SuggestedCanWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + SuggestedCanWorkflow.run, + {"messages": ["start"]}, + id=f"da-can-suggested-{uuid.uuid4()}", + task_queue="da-can-suggested", + ) + result = await handle.result() + + # Carry across the suggested continue-as-new: the conversation only reaches + # 3 messages if snapshots crossed run boundaries. + assert len(result["messages"]) >= 3, result + assert result["todos"][0]["status"] == "completed" + # The first run really did continue-as-new (not complete). + first = env.client.get_workflow_handle( + handle.id, run_id=handle.first_execution_run_id + ) + desc = await first.describe() + assert desc.status is not None and desc.status.name == "CONTINUED_AS_NEW", ( + desc.status + ) diff --git a/tests/contrib/deepagents/test_failures.py b/tests/contrib/deepagents/test_failures.py new file mode 100644 index 000000000..6cfe6e5ff --- /dev/null +++ b/tests/contrib/deepagents/test_failures.py @@ -0,0 +1,119 @@ +"""Error handling: retry classification and the workflow-failure type. + +Two dep-free paths run against a real server: the HTTP-error → Temporal retry +translation, and that a raised :class:`DeepAgentsWorkflowError` surfaces to the +client as a non-retryable failure with a stable ``ApplicationError.type`` (never +a stringified peer exception). The model-instance validation error needs +LangChain and guards on its import. +""" + +from __future__ import annotations + +import sys +import uuid +from datetime import timedelta + +import pytest + +from temporalio.testing import WorkflowEnvironment + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 11), reason="deepagents requires Python >= 3.11" +) +from temporalio import workflow +from temporalio.client import WorkflowFailureError +from temporalio.contrib.deepagents import DeepAgentsPlugin, DeepAgentsWorkflowError +from temporalio.contrib.deepagents._activity import _translate_api_error +from temporalio.exceptions import ApplicationError +from temporalio.worker import Worker + + +class _FakeResponse: + def __init__(self, headers: dict) -> None: + self.headers = headers + + +class _FakeHTTPError(Exception): + def __init__(self, status_code: int, headers: dict | None = None) -> None: + super().__init__(f"HTTP {status_code}") + self.status_code = status_code + self.response = _FakeResponse(headers or {}) + + +def test_error_classification() -> None: + # 429 is retryable and honors the upstream Retry-After. + err = _translate_api_error(_FakeHTTPError(429, {"retry-after": "7"})) + assert isinstance(err, ApplicationError) + assert err.non_retryable is False + assert err.next_retry_delay == timedelta(seconds=7) + + # 400 is a client error: non-retryable. + e400 = _translate_api_error(_FakeHTTPError(400)) + assert isinstance(e400, ApplicationError) + assert e400.non_retryable is True + + # 503 is retryable by default... + e503 = _translate_api_error(_FakeHTTPError(503)) + assert isinstance(e503, ApplicationError) + assert e503.non_retryable is False + # ...unless the server explicitly says not to. + forced = _translate_api_error(_FakeHTTPError(503, {"x-should-retry": "false"})) + assert isinstance(forced, ApplicationError) + assert forced.non_retryable is True + + # retry-after-ms wins over retry-after when both are present. + ms = _translate_api_error( + _FakeHTTPError(429, {"retry-after-ms": "250", "retry-after": "7"}) + ) + assert isinstance(ms, ApplicationError) + assert ms.next_retry_delay == timedelta(milliseconds=250) + + # A non-HTTP exception is not recognized, so the caller falls through. + assert _translate_api_error(ValueError("nope")) is None + + +@workflow.defn +class FailingWorkflow: + @workflow.run + async def run(self) -> None: + raise DeepAgentsWorkflowError("deliberate non-retryable failure") + + +@pytest.mark.asyncio +async def test_workflow_failure_type(env: WorkflowEnvironment) -> None: + plugin = DeepAgentsPlugin() + async with Worker( + env.client, + task_queue="da-fail", + workflows=[FailingWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + FailingWorkflow.run, + id=f"da-fail-{uuid.uuid4()}", + task_queue="da-fail", + ) + with pytest.raises(WorkflowFailureError) as excinfo: + await handle.result() + + cause = excinfo.value.cause + assert isinstance(cause, ApplicationError) + assert cause.type == "deepagents.DeepAgentsWorkflowError" + assert cause.non_retryable is True + + +@pytest.mark.asyncio +async def test_unwrappable_model_instance() -> None: + pytest.importorskip("langchain_core") + from langchain_core.language_models.fake_chat_models import FakeListChatModel + + # A string is auto-wrapped; a TemporalModel passes through; a live model + # instance is rejected at the workflow boundary with the typed failure. + from temporalio.contrib.deepagents import TemporalModel + from temporalio.contrib.deepagents._model import _wrap_model_arg + + assert isinstance(_wrap_model_arg("anthropic:claude"), TemporalModel) + tm = TemporalModel(model="anthropic:claude") + assert _wrap_model_arg(tm) is tm + with pytest.raises(DeepAgentsWorkflowError): + _wrap_model_arg(FakeListChatModel(responses=["hi"])) diff --git a/tests/contrib/deepagents/test_hitl.py b/tests/contrib/deepagents/test_hitl.py new file mode 100644 index 000000000..6124d41f1 --- /dev/null +++ b/tests/contrib/deepagents/test_hitl.py @@ -0,0 +1,139 @@ +"""Human-in-the-loop: SDK-native interrupt mapped to Query + Update. + +``interrupt_on={...}`` makes Deep Agents pause before a guarded tool runs. With a +checkpointer configured, LangGraph does *not* raise out of ``ainvoke`` — it +returns the current state with an ``__interrupt__`` entry describing the pending +approval (verified against deepagents 0.6.12 / langchain 1.x). Because the loop +runs in the workflow, that pause surfaces directly in workflow code. The plugin's +recommended mapping — used here — is: detect the returned ``__interrupt__``, +expose its payload via a Query, and resume with a Workflow Update carrying the +human's decision through ``Command(resume=...)``. No shim exception is invented; +the native LangGraph resume protocol (``{"decisions": [{"type": ...}]}``) is used +as-is. + +State lives on the workflow instance (per-execution), which is the idiomatic +Temporal pattern for state shared between the run method and its handlers. +""" + +from __future__ import annotations + +import asyncio +import sys +import uuid +from datetime import timedelta +from typing import Any + +import pytest + +from temporalio.testing import WorkflowEnvironment + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 11), reason="deepagents requires Python >= 3.11" +) + +pytest.importorskip("deepagents") +pytest.importorskip("langchain_core") +pytest.importorskip("langgraph") + +from temporalio import workflow # noqa: E402 +from temporalio.worker import Worker # noqa: E402 + +# Bind deepagents symbols off the module importorskip returns: a static +# `from deepagents import ...` cannot resolve on Python 3.10 (deepagents +# needs >= 3.11), and with the package absent the type checkers mis-resolve +# the name against this same-named test directory. +create_deep_agent = pytest.importorskip("deepagents").create_deep_agent + +with workflow.unsafe.imports_passed_through(): + from langchain_core.messages import AIMessage + from langchain_core.runnables import RunnableConfig + from langgraph.checkpoint.memory import InMemorySaver + from langgraph.types import Command + + from temporalio.contrib.deepagents import DeepAgentsPlugin, tool_as_activity + from temporalio.contrib.deepagents.testing import mock_model_provider + + +@workflow.defn +class HitlWorkflow: + def __init__(self) -> None: + self._interrupt: str | None = None + self._resume_value: str | None = None + self._resumed = False + + @workflow.run + async def run(self, city: str) -> str: + def book_trip(city: str) -> str: + """Book a trip to a city (requires human approval).""" + return f"Booked a trip to {city}." + + trip_tool = tool_as_activity( + book_trip, start_to_close_timeout=timedelta(seconds=30) + ) + agent = create_deep_agent( + model="anthropic:claude-sonnet-4-5", + tools=[trip_tool], + interrupt_on={"book_trip": True}, + checkpointer=InMemorySaver(), + ) + config: RunnableConfig = { + "configurable": {"thread_id": workflow.info().workflow_id} + } + payload: Any = {"messages": [{"role": "user", "content": f"Book {city}."}]} + result = await agent.ainvoke(payload, config=config) + # LangGraph returns (not raises) the pending approval under __interrupt__. + pending = result.get("__interrupt__") + if pending: + self._interrupt = str(getattr(pending[0], "value", pending[0])) + await workflow.wait_condition(lambda: self._resumed) + result = await agent.ainvoke( + Command(resume={"decisions": [{"type": self._resume_value}]}), + config=config, + ) + return result["messages"][-1].content + + @workflow.query + def pending_interrupt(self) -> str | None: + return self._interrupt + + @workflow.update + async def resume(self, decision: str) -> None: + self._resume_value = decision + self._resumed = True + + +@pytest.mark.asyncio +async def test_interrupt_query_then_resume(env: WorkflowEnvironment) -> None: + approve = AIMessage( + content="", + tool_calls=[{"name": "book_trip", "args": {"city": "Rome"}, "id": "c1"}], + ) + done = AIMessage(content="Booked a trip to Rome.") + plugin = DeepAgentsPlugin(model_provider=mock_model_provider([approve, done])) + async with Worker( + env.client, + task_queue="da-hitl", + workflows=[HitlWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + HitlWorkflow.run, + "Rome", + id=f"da-hitl-{uuid.uuid4()}", + task_queue="da-hitl", + ) + + # Wait for the agent to hit the interrupt (surfaced via the Query), then + # approve via an Update. A bounded poll with a sleep, so a regression that + # never raises the interrupt fails fast instead of busy-spinning. + for _ in range(100): + if await handle.query(HitlWorkflow.pending_interrupt) is not None: + break + await asyncio.sleep(0.1) + else: + pytest.fail("workflow never surfaced the HITL interrupt via the query") + + await handle.execute_update(HitlWorkflow.resume, "approve") + out = await handle.result() + + assert "Rome" in out diff --git a/tests/contrib/deepagents/test_model_activity.py b/tests/contrib/deepagents/test_model_activity.py new file mode 100644 index 000000000..37a98ef00 --- /dev/null +++ b/tests/contrib/deepagents/test_model_activity.py @@ -0,0 +1,103 @@ +"""The model seam: every ``TemporalModel`` generation is one activity. + +These exercise the seam directly through ``TemporalModel`` (no full agent +needed), so they depend only on LangChain, not on deepagents. +""" + +from __future__ import annotations + +import sys +import uuid +from datetime import timedelta + +import pytest + +from temporalio.testing import WorkflowEnvironment + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 11), reason="deepagents requires Python >= 3.11" +) + +pytest.importorskip("langchain_core") + +from temporalio import workflow # noqa: E402 +from temporalio.worker import Worker # noqa: E402 +from tests.contrib.deepagents.helpers import count_scheduled_activities # noqa: E402 + +with workflow.unsafe.imports_passed_through(): + from langchain_core.messages import HumanMessage + + from temporalio.contrib.deepagents import DeepAgentsPlugin, TemporalModel + from temporalio.contrib.deepagents.testing import mock_model_provider + +INVOKE_MODEL = "deepagents.invoke_model" + + +@workflow.defn +class ModelWorkflow: + @workflow.run + async def run(self, prompt: str) -> str: + model = TemporalModel(model="fake:model") + message = await model.ainvoke([HumanMessage(content=prompt)]) + return str(message.content) + + +@workflow.defn +class ExplicitTimeoutWorkflow: + @workflow.run + async def run(self, prompt: str) -> str: + model = TemporalModel( + model="fake:model", + activity_options={"start_to_close_timeout": timedelta(seconds=20)}, + ) + message = await model.ainvoke([HumanMessage(content=prompt)]) + return str(message.content) + + +@pytest.mark.asyncio +async def test_model_call_is_activity(env: WorkflowEnvironment) -> None: + plugin = DeepAgentsPlugin( + model_provider=mock_model_provider(["The capital of France is Paris."]), + model_activity_options={"start_to_close_timeout": timedelta(seconds=30)}, + ) + async with Worker( + env.client, + task_queue="da-model", + workflows=[ModelWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + ModelWorkflow.run, + "What is the capital of France?", + id=f"da-model-{uuid.uuid4()}", + task_queue="da-model", + ) + out = await handle.result() + assert "Paris" in out + counts = await count_scheduled_activities(handle) + assert counts[INVOKE_MODEL] == 1, counts + + +@pytest.mark.asyncio +async def test_temporal_model_explicit(env: WorkflowEnvironment) -> None: + # The explicit escape hatch routes through the same activity, with a + # per-model timeout override rather than the plugin default. + plugin = DeepAgentsPlugin( + model_provider=mock_model_provider(["Bonjour."]), + ) + async with Worker( + env.client, + task_queue="da-model-explicit", + workflows=[ExplicitTimeoutWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + ExplicitTimeoutWorkflow.run, + "hi", + id=f"da-model-x-{uuid.uuid4()}", + task_queue="da-model-explicit", + ) + out = await handle.result() + assert out == "Bonjour." + counts = await count_scheduled_activities(handle) + assert counts[INVOKE_MODEL] == 1, counts diff --git a/tests/contrib/deepagents/test_native_e2e.py b/tests/contrib/deepagents/test_native_e2e.py new file mode 100644 index 000000000..29b373da6 --- /dev/null +++ b/tests/contrib/deepagents/test_native_e2e.py @@ -0,0 +1,141 @@ +"""Cardinal end-to-end test: unmodified ``deepagents`` code, made durable. + +Builds a real Deep Agent with ``create_deep_agent(...)`` and drives it with +``agent.ainvoke(...)`` inside a ``@workflow.defn`` — the only addition is +``plugins=[DeepAgentsPlugin(...)]``. No user call to +``workflow.execute_activity``; the plugin routes model and tool calls to +activities under the hood. + +Guards on the ``deepagents`` / ``langchain_core`` imports (capability detection), +because they are the plugin's own runtime dependency and may be absent on a +docs-only checkout — there is no env-var gate. +""" + +from __future__ import annotations + +import sys +import uuid +from datetime import timedelta + +import pytest + +from temporalio.testing import WorkflowEnvironment + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 11), reason="deepagents requires Python >= 3.11" +) + +pytest.importorskip("deepagents") +pytest.importorskip("langchain_core") + +from temporalio import workflow # noqa: E402 +from temporalio.worker import Worker # noqa: E402 +from tests.contrib.deepagents.helpers import count_scheduled_activities # noqa: E402 + +# Bind deepagents symbols off the module importorskip returns: a static +# `from deepagents import ...` cannot resolve on Python 3.10 (deepagents +# needs >= 3.11), and with the package absent the type checkers mis-resolve +# the name against this same-named test directory. +create_deep_agent = pytest.importorskip("deepagents").create_deep_agent + +with workflow.unsafe.imports_passed_through(): + from langchain_core.messages import AIMessage + + from temporalio.contrib.deepagents import DeepAgentsPlugin, tool_as_activity + from temporalio.contrib.deepagents.testing import mock_model_provider + +INVOKE_MODEL = "deepagents.invoke_model" +INVOKE_TOOL = "deepagents.invoke_tool" + + +@workflow.defn +class DeepAgentWorkflow: + @workflow.run + async def run(self, question: str) -> str: + agent = create_deep_agent( + model="anthropic:claude-sonnet-4-5", + system_prompt="You are a helpful assistant.", + ) + result = await agent.ainvoke( + {"messages": [{"role": "user", "content": question}]} + ) + return result["messages"][-1].content + + +@workflow.defn +class ToolLoopWorkflow: + @workflow.run + async def run(self, city: str) -> str: + def get_weather(city: str) -> str: + """Return the weather for a city.""" + return f"It is sunny in {city}." + + weather_tool = tool_as_activity( + get_weather, start_to_close_timeout=timedelta(seconds=30) + ) + agent = create_deep_agent( + model="anthropic:claude-sonnet-4-5", + tools=[weather_tool], + system_prompt="Use the weather tool to answer.", + ) + result = await agent.ainvoke( + {"messages": [{"role": "user", "content": f"Weather in {city}?"}]} + ) + return result["messages"][-1].content + + +@pytest.mark.asyncio +async def test_deep_agent_runs_via_plugin(env: WorkflowEnvironment) -> None: + plugin = DeepAgentsPlugin( + model_provider=mock_model_provider(["The answer is 42."]), + ) + async with Worker( + env.client, + task_queue="da-native", + workflows=[DeepAgentWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + DeepAgentWorkflow.run, + "What is the meaning of life?", + id=f"da-native-{uuid.uuid4()}", + task_queue="da-native", + ) + out = await handle.result() + + assert "42" in out + counts = await count_scheduled_activities(handle) + # The model call was made durable as an activity, with no user wiring. + assert counts[INVOKE_MODEL] >= 1, counts + + +@pytest.mark.asyncio +async def test_agent_tool_loop_routes_to_activities(env: WorkflowEnvironment) -> None: + # Script the model: first turn asks for the tool, second turn answers. This + # forces model -> tool -> model, proving each call routes through an activity. + tool_call = AIMessage( + content="", + tool_calls=[{"name": "get_weather", "args": {"city": "Paris"}, "id": "call-1"}], + ) + final = AIMessage(content="It is sunny in Paris.") + plugin = DeepAgentsPlugin( + model_provider=mock_model_provider([tool_call, final]), + ) + async with Worker( + env.client, + task_queue="da-tool-loop", + workflows=[ToolLoopWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + ToolLoopWorkflow.run, + "Paris", + id=f"da-tool-loop-{uuid.uuid4()}", + task_queue="da-tool-loop", + ) + out = await handle.result() + + assert "Paris" in out + counts = await count_scheduled_activities(handle) + assert counts[INVOKE_MODEL] == 2, counts + assert counts[INVOKE_TOOL] == 1, counts diff --git a/tests/contrib/deepagents/test_readme.py b/tests/contrib/deepagents/test_readme.py new file mode 100644 index 000000000..7aa8ddd7a --- /dev/null +++ b/tests/contrib/deepagents/test_readme.py @@ -0,0 +1,36 @@ +"""The README's code blocks must stay valid Python. + +A copy-paste example that does not even parse is worse than no example. This +compiles every ```python fenced block in the README so a stale snippet fails the +suite. Compilation (not execution) keeps the check dependency-free. +""" + +from __future__ import annotations + +import re +from pathlib import Path + +README = ( + Path(__file__).resolve().parents[3] + / "temporalio" + / "contrib" + / "deepagents" + / "README.md" +) + +_BLOCK = re.compile(r"```python\n(.*?)```", re.DOTALL) + + +def _python_blocks() -> list[str]: + return _BLOCK.findall(README.read_text(encoding="utf-8")) + + +def test_readme_has_python_blocks() -> None: + blocks = _python_blocks() + assert len(blocks) >= 3, "expected the hello-world and composition examples" + + +def test_readme_hello_world_constructs() -> None: + # Every documented snippet must compile as written. + for i, block in enumerate(_python_blocks()): + compile(block, f"", "exec") diff --git a/tests/contrib/deepagents/test_replay.py b/tests/contrib/deepagents/test_replay.py new file mode 100644 index 000000000..ac43a4034 --- /dev/null +++ b/tests/contrib/deepagents/test_replay.py @@ -0,0 +1,65 @@ +"""Recorded histories replay cleanly through the plugin. + +The Deep Agents control loop runs in the workflow, so replay determinism is the +core safety property. This records the history of a real run (a plain fake agent +driven by ``run_deep_agent``, no LangChain needed) and feeds it back through a +``Replayer`` configured with the plugin. A nondeterministic seam would raise on +replay; a clean pass proves the in-workflow dispatch is deterministic. +""" + +from __future__ import annotations + +import sys +import uuid +from typing import Any + +import pytest + +from temporalio.testing import WorkflowEnvironment + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 11), reason="deepagents requires Python >= 3.11" +) +from temporalio import workflow +from temporalio.contrib.deepagents import DeepAgentsPlugin, run_deep_agent +from temporalio.worker import Replayer, Worker + + +class FakeAgent: + async def ainvoke(self, input: Any) -> dict: + messages = list(input.get("messages", [])) if isinstance(input, dict) else [] + return {"messages": [*messages, "answered"], "todos": []} + + +@workflow.defn +class ReplayWorkflow: + @workflow.run + async def run(self, input: dict) -> dict: + return await run_deep_agent(FakeAgent(), input) + + +@pytest.mark.asyncio +async def test_replay_with_plugin(env: WorkflowEnvironment) -> None: + plugin = DeepAgentsPlugin() + async with Worker( + env.client, + task_queue="da-replay", + workflows=[ReplayWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + ReplayWorkflow.run, + {"messages": ["question"]}, + id=f"da-replay-{uuid.uuid4()}", + task_queue="da-replay", + ) + await handle.result() + history = await handle.fetch_history() + + # A fresh replayer (new worker identity) must replay the recorded history + # without a nondeterminism error. + replayer = Replayer( + workflows=[ReplayWorkflow], + plugins=[DeepAgentsPlugin()], + ) + await replayer.replay_workflow(history) diff --git a/tests/contrib/deepagents/test_sandbox_passthrough.py b/tests/contrib/deepagents/test_sandbox_passthrough.py new file mode 100644 index 000000000..26a5ce2e6 --- /dev/null +++ b/tests/contrib/deepagents/test_sandbox_passthrough.py @@ -0,0 +1,89 @@ +"""The plugin's sandbox passthrough makes explicit import guards unnecessary. + +This module imports ``deepagents`` at the top WITHOUT +``workflow.unsafe.imports_passed_through()``. The workflow sandbox re-imports +the defining module of every workflow, so if the plugin's passthrough +configuration did not cover deepagents' import tree, worker registration +below would fail. This is the executable proof behind the README's +guard-free examples. + +Also the e2e for ``create_temporal_deep_agent``: the explicit construction +path that scopes ``activity_options`` to one agent. +""" + +from __future__ import annotations + +import sys +import uuid +from datetime import timedelta + +import pytest + +from temporalio.testing import WorkflowEnvironment + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 11), reason="deepagents requires Python >= 3.11" +) +pytest.importorskip("deepagents") +pytest.importorskip("langchain_core") + +import deepagents # noqa: F401 # pyright: ignore[reportUnusedImport, reportImplicitRelativeImport] + +from temporalio import workflow +from temporalio.contrib.deepagents import ( + DeepAgentsPlugin, + create_temporal_deep_agent, +) +from temporalio.worker import Worker +from tests.contrib.deepagents.helpers import count_scheduled_activities + + +@workflow.defn +class GuardFreeAgentWorkflow: + @workflow.run + async def run(self, question: str) -> str: + agent = create_temporal_deep_agent( + model="anthropic:claude-sonnet-4-5", + activity_options={"start_to_close_timeout": timedelta(minutes=2)}, + ) + result = await agent.ainvoke( + {"messages": [{"role": "user", "content": question}]} + ) + return str(result["messages"][-1].content) + + +@pytest.mark.asyncio +async def test_guard_free_import_and_explicit_agent( + env: WorkflowEnvironment, +) -> None: + from temporalio.contrib.deepagents.testing import mock_model_provider + + plugin = DeepAgentsPlugin( + model_provider=mock_model_provider(["The answer is 42."]), + ) + async with Worker( + env.client, + task_queue="da-guard-free", + workflows=[GuardFreeAgentWorkflow], + plugins=[plugin], + max_cached_workflows=0, + ): + handle = await env.client.start_workflow( + GuardFreeAgentWorkflow.run, + "What is the meaning of life?", + id=f"da-guard-free-{uuid.uuid4()}", + task_queue="da-guard-free", + ) + out = await handle.result() + + assert "42" in out + counts = await count_scheduled_activities(handle) + assert counts["deepagents.invoke_model"] == 1, counts + + +def test_wrapper_rejects_options_without_wrappable_model() -> None: + with pytest.raises(ValueError, match="activity_options requires"): + create_temporal_deep_agent( + model=object(), + activity_options={"start_to_close_timeout": timedelta(minutes=1)}, + ) diff --git a/tests/contrib/deepagents/test_side_effects.py b/tests/contrib/deepagents/test_side_effects.py new file mode 100644 index 000000000..2b32b9dee --- /dev/null +++ b/tests/contrib/deepagents/test_side_effects.py @@ -0,0 +1,74 @@ +"""Determinism: no unexpected side effects and a bounded activity count. + +Running the worker with ``max_cached_workflows=0`` forces the workflow to be +replayed from history on every activation. If the in-workflow dispatch did +anything nondeterministic, replay would diverge and the workflow would fail. A +clean completion plus an exact ``backend_op`` schedule count proves the seam +schedules one activity per op and nothing more. +""" + +from __future__ import annotations + +import sys +import uuid +from datetime import timedelta + +import pytest + +from temporalio.testing import WorkflowEnvironment + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 11), reason="deepagents requires Python >= 3.11" +) +from temporalio import workflow +from temporalio.contrib.deepagents import DeepAgentsPlugin, TemporalBackend +from temporalio.worker import Worker +from tests.contrib.deepagents.helpers import count_scheduled_activities + +BACKEND_OP = "deepagents.backend_op" + + +class TwoOpBackend: + """Protocol-named ops: one async (``awrite``, as the filesystem + middleware calls it) and one sync (``read``).""" + + async def awrite(self, file_path: str, _content: str) -> str: + return f"wrote:{file_path}" + + def read(self, file_path: str) -> str: + return f"read:{file_path}" + + +@workflow.defn +class TwoOpWorkflow: + @workflow.run + async def run(self) -> str: + backend = TemporalBackend( + TwoOpBackend(), + activity_options={"start_to_close_timeout": timedelta(seconds=10)}, + ) + await backend.awrite("a.txt", "hello") + return await backend.read("a.txt") + + +@pytest.mark.asyncio +async def test_activity_schedule_counts(env: WorkflowEnvironment) -> None: + plugin = DeepAgentsPlugin() + async with Worker( + env.client, + task_queue="da-side-effects", + workflows=[TwoOpWorkflow], + plugins=[plugin], + max_cached_workflows=0, + ): + handle = await env.client.start_workflow( + TwoOpWorkflow.run, + id=f"da-side-effects-{uuid.uuid4()}", + task_queue="da-side-effects", + ) + out = await handle.result() + + assert out == "read:a.txt" + counts = await count_scheduled_activities(handle) + # Exactly the two backend ops, each scheduled once — no hidden replays of work. + assert counts[BACKEND_OP] == 2, counts diff --git a/tests/contrib/deepagents/test_streaming.py b/tests/contrib/deepagents/test_streaming.py new file mode 100644 index 000000000..6b65aa0af --- /dev/null +++ b/tests/contrib/deepagents/test_streaming.py @@ -0,0 +1,107 @@ +"""Streaming: model calls route through the streaming activity when a topic is set. + +Setting ``streaming_topic`` flips model dispatch from ``invoke_model`` to +``invoke_model_streaming``, which streams chunks out of the activity and returns +the aggregated final message to the workflow (so the durable result matches the +non-streaming path). These assert the observable effects: which activity is +scheduled, the aggregated content, and that a custom batch interval is threaded +into the streaming activity. +""" + +from __future__ import annotations + +import sys +import uuid +from datetime import timedelta + +import pytest + +from temporalio.testing import WorkflowEnvironment + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 11), reason="deepagents requires Python >= 3.11" +) + +pytest.importorskip("langchain_core") + +from temporalio import workflow # noqa: E402 +from temporalio.worker import Worker # noqa: E402 +from tests.contrib.deepagents.helpers import count_scheduled_activities # noqa: E402 + +with workflow.unsafe.imports_passed_through(): + from langchain_core.messages import HumanMessage + + from temporalio.contrib.deepagents import DeepAgentsPlugin, TemporalModel + from temporalio.contrib.deepagents.testing import mock_model_provider + +INVOKE_MODEL = "deepagents.invoke_model" +INVOKE_MODEL_STREAMING = "deepagents.invoke_model_streaming" + + +@workflow.defn +class StreamWorkflow: + @workflow.run + async def run(self, prompt: str) -> str: + model = TemporalModel(model="fake:model") + parts: list[str] = [] + async for chunk in model.astream([HumanMessage(content=prompt)]): + parts.append(str(chunk.content)) + return "".join(parts) + + +@pytest.mark.asyncio +async def test_stream_chunks_published(env: WorkflowEnvironment) -> None: + plugin = DeepAgentsPlugin( + model_provider=mock_model_provider(["Streamed answer."]), + streaming_topic="da-stream-topic", + model_activity_options={"start_to_close_timeout": timedelta(seconds=30)}, + ) + async with Worker( + env.client, + task_queue="da-stream", + workflows=[StreamWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + StreamWorkflow.run, + "stream please", + id=f"da-stream-{uuid.uuid4()}", + task_queue="da-stream", + ) + out = await handle.result() + + assert "Streamed answer." in out + counts = await count_scheduled_activities(handle) + # The topic is set, so dispatch used the streaming activity, not invoke_model. + assert counts[INVOKE_MODEL_STREAMING] == 1, counts + assert counts[INVOKE_MODEL] == 0, counts + + +@pytest.mark.asyncio +async def test_batch_interval_coalesces(env: WorkflowEnvironment) -> None: + # A custom batch interval is threaded into the streaming activity that + # coalesces chunks; streaming still returns the aggregated message. + plugin = DeepAgentsPlugin( + model_provider=mock_model_provider(["Batched."]), + streaming_topic="da-batch-topic", + streaming_batch_interval=timedelta(milliseconds=500), + ) + assert plugin._activities._streaming_batch_interval == timedelta(milliseconds=500) + + async with Worker( + env.client, + task_queue="da-batch", + workflows=[StreamWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + StreamWorkflow.run, + "batch please", + id=f"da-batch-{uuid.uuid4()}", + task_queue="da-batch", + ) + out = await handle.result() + + assert "Batched." in out + counts = await count_scheduled_activities(handle) + assert counts[INVOKE_MODEL_STREAMING] == 1, counts diff --git a/tests/contrib/deepagents/test_subagents.py b/tests/contrib/deepagents/test_subagents.py new file mode 100644 index 000000000..49a407e9d --- /dev/null +++ b/tests/contrib/deepagents/test_subagents.py @@ -0,0 +1,90 @@ +"""Sub-agents inherit the activity seams. + +Deep Agents builds sub-agents as separate graphs, but they inherit the parent's +``model`` object and tools by default. Because the plugin substitutes the model +*object*, every sub-agent's model call routes through an activity without any +per-sub-agent wiring. This runs a real agent configured with a sub-agent and +asserts model calls still land on the activity seam. + +The exact number of hops depends on ``deepagents`` internals we do not pin, so +the assertion is the robust invariant: the configured agent runs and at least one +model call went through an activity. +""" + +from __future__ import annotations + +import sys +import uuid + +import pytest + +from temporalio.testing import WorkflowEnvironment + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 11), reason="deepagents requires Python >= 3.11" +) + +pytest.importorskip("deepagents") +pytest.importorskip("langchain_core") + +from temporalio import workflow # noqa: E402 +from temporalio.worker import Worker # noqa: E402 +from tests.contrib.deepagents.helpers import count_scheduled_activities # noqa: E402 + +# Bind deepagents symbols off the module importorskip returns: a static +# `from deepagents import ...` cannot resolve on Python 3.10 (deepagents +# needs >= 3.11), and with the package absent the type checkers mis-resolve +# the name against this same-named test directory. +create_deep_agent = pytest.importorskip("deepagents").create_deep_agent + +with workflow.unsafe.imports_passed_through(): + from temporalio.contrib.deepagents import DeepAgentsPlugin + from temporalio.contrib.deepagents.testing import mock_model_provider + +INVOKE_MODEL = "deepagents.invoke_model" + + +@workflow.defn +class SubAgentWorkflow: + @workflow.run + async def run(self, question: str) -> str: + agent = create_deep_agent( + model="anthropic:claude-sonnet-4-5", + system_prompt="You coordinate research.", + subagents=[ + { + "name": "researcher", + "description": "Researches a topic in depth.", + "system_prompt": "You research topics.", + } + ], + ) + result = await agent.ainvoke( + {"messages": [{"role": "user", "content": question}]} + ) + return result["messages"][-1].content + + +@pytest.mark.asyncio +async def test_subagent_calls_route_to_activities(env: WorkflowEnvironment) -> None: + plugin = DeepAgentsPlugin( + model_provider=mock_model_provider(["Coordinated answer."]), + ) + async with Worker( + env.client, + task_queue="da-subagent", + workflows=[SubAgentWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + SubAgentWorkflow.run, + "Investigate the topic.", + id=f"da-subagent-{uuid.uuid4()}", + task_queue="da-subagent", + ) + out = await handle.result() + + assert out + counts = await count_scheduled_activities(handle) + # A model instance shared with the sub-agent means model calls are activities. + assert counts[INVOKE_MODEL] >= 1, counts diff --git a/tests/contrib/deepagents/test_tools.py b/tests/contrib/deepagents/test_tools.py new file mode 100644 index 000000000..1fc1ff212 --- /dev/null +++ b/tests/contrib/deepagents/test_tools.py @@ -0,0 +1,238 @@ +"""The tool seam: existing activities and wrapped tools route to activities. + +These need LangChain (a tool is a ``BaseTool``) and guard on its import. Each +runs a real workflow that invokes the wrapped tool and asserts it scheduled the +expected activity. +""" + +from __future__ import annotations + +import sys +import uuid +from collections.abc import Sequence +from datetime import timedelta +from types import SimpleNamespace +from typing import Any + +import pytest + +from temporalio.testing import WorkflowEnvironment + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 11), reason="deepagents requires Python >= 3.11" +) + +pytest.importorskip("langchain_core") + +from temporalio import activity, workflow # noqa: E402 +from temporalio.worker import Worker # noqa: E402 +from tests.contrib.deepagents.helpers import count_scheduled_activities # noqa: E402 + +with workflow.unsafe.imports_passed_through(): + from temporalio.contrib.deepagents import ( # noqa: E402 + DeepAgentsPlugin, + activity_as_tool, + tool_as_activity, + ) + from temporalio.contrib.deepagents._tools import warn_unwrapped_tools # noqa: E402 + +INVOKE_TOOL = "deepagents.invoke_tool" + +# Bind deepagents symbols off the module importorskip returns: a static +# `from deepagents import ...` cannot resolve on Python 3.10 (deepagents +# needs >= 3.11), and with the package absent the type checkers mis-resolve +# the name against this same-named test directory. +create_deep_agent = pytest.importorskip("deepagents").create_deep_agent + + +def pairing_weather(city: str) -> str: + """Return the weather for a city.""" + return f"weather:{city}" + + +@activity.defn +async def echo_activity(text: str) -> str: + return f"echo:{text}" + + +@workflow.defn +class ActivityAsToolWorkflow: + @workflow.run + async def run(self, text: str) -> str: + tool = activity_as_tool( + echo_activity, start_to_close_timeout=timedelta(seconds=10) + ) + return await tool.ainvoke({"text": text}) + + +@workflow.defn +class ToolAsActivityWorkflow: + @workflow.run + async def run(self, city: str) -> str: + def get_weather(city: str) -> str: + """Look up the weather for a city.""" + return f"sunny in {city}" + + tool = tool_as_activity( + get_weather, start_to_close_timeout=timedelta(seconds=10) + ) + # The wrapper returns plain CONTENT (the tool node stamps the model's + # tool_call_id onto it) — not a pre-built ToolMessage. + return str(await tool.ainvoke({"city": city})) + + +@pytest.mark.asyncio +async def test_activity_as_tool(env: WorkflowEnvironment) -> None: + plugin = DeepAgentsPlugin() + async with Worker( + env.client, + task_queue="da-act-tool", + workflows=[ActivityAsToolWorkflow], + activities=[echo_activity], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + ActivityAsToolWorkflow.run, + "hi", + id=f"da-act-tool-{uuid.uuid4()}", + task_queue="da-act-tool", + ) + out = await handle.result() + + assert out == "echo:hi" + counts = await count_scheduled_activities(handle) + assert counts["echo_activity"] == 1, counts + + +@pytest.mark.asyncio +async def test_tool_as_activity(env: WorkflowEnvironment) -> None: + plugin = DeepAgentsPlugin() + async with Worker( + env.client, + task_queue="da-tool-act", + workflows=[ToolAsActivityWorkflow], + plugins=[plugin], + ): + handle = await env.client.start_workflow( + ToolAsActivityWorkflow.run, + "Paris", + id=f"da-tool-act-{uuid.uuid4()}", + task_queue="da-tool-act", + ) + out = await handle.result() + + assert "sunny in Paris" in out + counts = await count_scheduled_activities(handle) + assert counts[INVOKE_TOOL] == 1, counts + + +def test_builtin_tool_in_workflow(recwarn: pytest.WarningsRecorder) -> None: + # Built-in tool names never warn (they are pure, in-workflow); an unwrapped + # user tool does warn so the Workflow-vs-Activity choice is conscious. + warn_unwrapped_tools([SimpleNamespace(name="write_todos")]) + assert len(recwarn) == 0 + + warn_unwrapped_tools([SimpleNamespace(name="scrape_website")]) + assert any("scrape_website" in str(w.message) for w in recwarn) + + +# Turn-2 requests observed by the fake model, captured activity-side. Module +# state is shared with the in-process worker, same as the tool registries. +_captured_requests: list[list] = [] + + +@workflow.defn +class ToolCallIdPairingWorkflow: + @workflow.run + async def run(self, city: str) -> str: + weather_tool = tool_as_activity( + pairing_weather, start_to_close_timeout=timedelta(seconds=10) + ) + agent = create_deep_agent( + model="anthropic:claude-sonnet-4-5", + tools=[weather_tool], + system_prompt="Use the weather tool.", + ) + result = await agent.ainvoke( + {"messages": [{"role": "user", "content": f"Weather in {city}?"}]} + ) + return str(result["messages"][-1].content) + + +@pytest.mark.asyncio +async def test_wrapped_tool_result_pairs_with_model_tool_call_id( + env: WorkflowEnvironment, +) -> None: + """The tool_result the model sees on turn 2 must carry the model's OWN + tool_call_id. Regression: ``tool_as_activity`` returned the activity-built + ``ToolMessage`` whose workflow-generated id a real provider (Anthropic) + rejects as an unpaired ``tool_result`` — offline fakes never validate the + pairing, so only a live-model run surfaced it. The wrapper now returns + plain content and the tool node stamps the correct id. + """ + from langchain_core.messages import AIMessage, ToolMessage + + from temporalio.contrib.deepagents.testing import FakeModel + + _captured_requests.clear() + + class RecordingModel(FakeModel): + def __init__(self, responses: Sequence[Any]) -> None: + super().__init__(responses) + + async def _agenerate( + self, + messages: list[Any], + stop: list[str] | None = None, + run_manager: Any = None, + **kwargs: Any, + ) -> Any: + _captured_requests.append(list(messages)) + return await super()._agenerate( + messages, stop=stop, run_manager=run_manager, **kwargs + ) + + tool_turn = AIMessage( + content="", + tool_calls=[ + { + "name": "pairing_weather", + "args": {"city": "Paris"}, + "id": "toolu_scripted_pairing_id", + } + ], + ) + final = AIMessage(content="It is sunny in Paris.") + responses = [tool_turn, final] + cursor = {"i": 0} + + def provider(_model_name: str) -> RecordingModel: + reply = responses[cursor["i"] % len(responses)] + cursor["i"] += 1 + return RecordingModel([reply]) + + plugin = DeepAgentsPlugin(model_provider=provider) + async with Worker( + env.client, + task_queue="da-toolcall-pairing", + workflows=[ToolCallIdPairingWorkflow], + plugins=[plugin], + max_cached_workflows=0, + ): + handle = await env.client.start_workflow( + ToolCallIdPairingWorkflow.run, + "Paris", + id=f"da-toolcall-pairing-{uuid.uuid4()}", + task_queue="da-toolcall-pairing", + ) + out = await handle.result() + + assert "sunny" in out.lower() + # Turn 2's request must contain the tool result under the MODEL's id. + assert len(_captured_requests) >= 2, len(_captured_requests) + tool_messages = [m for m in _captured_requests[1] if isinstance(m, ToolMessage)] + assert tool_messages, _captured_requests[1] + assert tool_messages[0].tool_call_id == "toolu_scripted_pairing_id", tool_messages[ + 0 + ].tool_call_id + assert "weather:Paris" in str(tool_messages[0].content) diff --git a/tests/contrib/google_adk_agents/__init__.py b/tests/contrib/google_adk_agents/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/contrib/google_adk_agents/test_adk_tool_context.py b/tests/contrib/google_adk_agents/test_adk_tool_context.py new file mode 100644 index 000000000..2f5d340d8 --- /dev/null +++ b/tests/contrib/google_adk_agents/test_adk_tool_context.py @@ -0,0 +1,352 @@ +"""Tests for ToolContextSnapshot injection into activity-backed tools. + +Covers https://github.com/temporalio/sdk-python/issues/1470: activities +wrapped with activity_as_tool can read the serializable subset of the ADK +ToolContext (session state and function-call id) without the live, +non-serializable ToolContext ever crossing the activity boundary, and +without the parameter leaking into the LLM-facing tool schema. +""" + +import uuid +from collections.abc import AsyncGenerator +from datetime import timedelta +from typing import Any, Optional # pyright: ignore[reportDeprecated] + +import pytest +from google.adk import Agent +from google.adk.features import FeatureName, override_feature_enabled +from google.adk.models import BaseLlm, LLMRegistry +from google.adk.models.llm_request import LlmRequest +from google.adk.models.llm_response import LlmResponse +from google.adk.runners import InMemoryRunner +from google.adk.tools.function_tool import FunctionTool +from google.adk.tools.tool_context import ToolContext +from google.adk.utils.context_utils import Aclosing +from google.genai import types +from google.genai.types import Content, FunctionCall, Part + +from temporalio import activity, workflow +from temporalio.client import Client +from temporalio.contrib.google_adk_agents import GoogleAdkPlugin, TemporalModel +from temporalio.contrib.google_adk_agents.workflow import ( + ToolContextSnapshot, + activity_as_tool, +) +from temporalio.worker import Worker + +TASK_QUEUE = "adk-tool-context-task-queue" + +SESSION_STATE: dict[str, Any] = { + "db_url": "postgres://config", + "retries": 3, + "regions": {"primary": "us-east1", "replicas": ["eu-west1"]}, +} + + +@activity.defn +async def lookup_weather( + city: str, tool_context: ToolContextSnapshot, units: str = "celsius" +) -> str: + """Activity that reads tool configuration from session state. + + Mirrors the shape reported in issue #1470, with tool_context deliberately + in the middle of the parameter list to prove positional slotting. The + returned string also records whether the code ran inside a real activity, + so tests can tell the activity boundary was actually crossed (or not). + """ + db_url = tool_context.state.get("db_url", "") + retries = tool_context.state.get("retries", -1) + region = tool_context.state.get("regions", {}).get("primary", "") + has_function_call_id = "yes" if tool_context.function_call_id else "no" + in_act = "yes" if activity.in_activity() else "no" + return f"{city}|{units}|{db_url}|{retries}|{region}|fc={has_function_call_id}|act={in_act}" + + +def weather_agent(model_name: str) -> Agent: + return Agent( + name="state_agent", + model=TemporalModel(model_name), + tools=[ + activity_as_tool( + lookup_weather, start_to_close_timeout=timedelta(seconds=30) + ) + ], + ) + + +class StateToolModel(BaseLlm): + """Scripted model: call lookup_weather once, then echo its response.""" + + async def generate_content_async( + self, llm_request: LlmRequest, stream: bool = False + ) -> AsyncGenerator[LlmResponse, None]: + tool_response = None + for content in llm_request.contents: + for part in content.parts or []: + if ( + part.function_response is not None + and part.function_response.name == "lookup_weather" + ): + tool_response = part.function_response + if tool_response is None: + yield LlmResponse( + content=Content( + role="model", + parts=[ + Part( + function_call=FunctionCall( + name="lookup_weather", args={"city": "NYC"} + ) + ) + ], + ) + ) + else: + yield LlmResponse( + content=Content( + role="model", + parts=[Part(text=f"done:{tool_response.response}")], + ) + ) + + @classmethod + def supported_models(cls) -> list[str]: + return ["state_tool_model"] + + +async def run_state_agent(model_name: str) -> str: + """Runs the agent against a session seeded with state; returns final text.""" + runner = InMemoryRunner(agent=weather_agent(model_name), app_name="test_app") + session = await runner.session_service.create_session( + app_name="test_app", + user_id="test", + state=SESSION_STATE, + ) + final_text = "" + async with Aclosing( + runner.run_async( + user_id="test", + session_id=session.id, + new_message=types.Content( + role="user", parts=[types.Part(text="weather in NYC?")] + ), + ) + ) as agen: + async for event in agen: + if event.content and event.content.parts and event.content.parts[0].text: + final_text = event.content.parts[0].text + return final_text + + +@workflow.defn +class StateToolWorkflow: + @workflow.run + async def run(self, model_name: str) -> str: + return await run_state_agent(model_name) + + +@pytest.mark.asyncio +async def test_activity_as_tool_receives_tool_context_snapshot(client: Client): + new_config = client.config() + new_config["plugins"] = [GoogleAdkPlugin()] + client = Client(**new_config) + + async with Worker( + client, + task_queue=TASK_QUEUE, + activities=[lookup_weather], + workflows=[StateToolWorkflow], + max_cached_workflows=0, + ): + LLMRegistry.register(StateToolModel) + result = await client.execute_workflow( + StateToolWorkflow.run, + "state_tool_model", + id=f"tool-context-{uuid.uuid4()}", + task_queue=TASK_QUEUE, + execution_timeout=timedelta(seconds=30), + ) + # The activity saw mixed-type session state (string, int, nested dict), + # the default parameter value, and a populated function-call id — none of + # which came from the LLM — and act=yes proves the snapshot crossed a + # real activity boundary rather than running inline in the workflow. + assert "NYC|celsius|postgres://config|3|us-east1|fc=yes|act=yes" in result + + +@pytest.mark.asyncio +async def test_activity_as_tool_snapshot_outside_workflow(): + """Local ADK runs (no Temporal) receive the same snapshot.""" + LLMRegistry.register(StateToolModel) + result = await run_state_agent("state_tool_model") + assert "NYC|celsius|postgres://config|3|us-east1|fc=yes|act=no" in result + + +def _declared_properties(tool: FunctionTool) -> dict[str, Any]: + """Property names in the LLM-facing declaration, across schema styles.""" + declaration = tool._get_declaration() + assert declaration is not None + if declaration.parameters_json_schema is not None: + return declaration.parameters_json_schema.get("properties", {}) + assert declaration.parameters is not None + return declaration.parameters.properties or {} + + +def test_tool_schema_excludes_tool_context(): + """The tool_context parameter never appears in the LLM-facing schema.""" + tool = FunctionTool( + func=activity_as_tool( + lookup_weather, start_to_close_timeout=timedelta(seconds=30) + ) + ) + properties = _declared_properties(tool) + assert "city" in properties + assert "units" in properties + assert "tool_context" not in properties + + +def test_tool_schema_excludes_tool_context_legacy_declaration(): + """Exclusion also holds on the legacy (non-JSON-schema) declaration path.""" + override_feature_enabled(FeatureName.JSON_SCHEMA_FOR_FUNC_DECL, False) + try: + tool = FunctionTool( + func=activity_as_tool( + lookup_weather, start_to_close_timeout=timedelta(seconds=30) + ) + ) + declaration = tool._get_declaration() + assert declaration is not None + assert declaration.parameters is not None + properties = declaration.parameters.properties or {} + assert "city" in properties + assert "units" in properties + assert "tool_context" not in properties + finally: + # The flag is default-on across the supported google-adk range. + override_feature_enabled(FeatureName.JSON_SCHEMA_FOR_FUNC_DECL, True) + + +def test_tool_schema_context_only_parameter(): + """A tool whose only parameter is tool_context exposes no LLM arguments.""" + + @activity.defn + async def ctx_only_tool(tool_context: ToolContextSnapshot) -> str: + return str(tool_context.state) + + tool = FunctionTool( + func=activity_as_tool( + ctx_only_tool, start_to_close_timeout=timedelta(seconds=30) + ) + ) + declaration = tool._get_declaration() + if declaration is not None: + json_properties = (declaration.parameters_json_schema or {}).get( + "properties", {} + ) + legacy_properties = ( + (declaration.parameters.properties or {}) if declaration.parameters else {} + ) + assert not json_properties + assert not legacy_properties + + +def test_activity_as_tool_accepts_optional_snapshot_annotation(): + """ToolContextSnapshot | None is accepted and still excluded from the schema.""" + + @activity.defn + async def optional_tool( + query: str, + tool_context: ToolContextSnapshot | None = None, # pyright: ignore[reportUnusedParameter] + ) -> str: + return query + + tool = FunctionTool( + func=activity_as_tool( + optional_tool, start_to_close_timeout=timedelta(seconds=30) + ) + ) + properties = _declared_properties(tool) + assert set(properties) == {"query"} + + +def test_activity_as_tool_rejects_adk_tool_context_annotation(): + """Annotating with the live ADK ToolContext gives an actionable error.""" + + @activity.defn + async def bad_tool(query: str, tool_context: ToolContext) -> str: # pyright: ignore[reportUnusedParameter] + return query + + with pytest.raises(ValueError, match="ToolContextSnapshot"): + activity_as_tool(bad_tool, start_to_close_timeout=timedelta(seconds=30)) + + +def test_activity_as_tool_rejects_optional_adk_tool_context_annotation(): + """Optional[ToolContext] is rejected with the ADK-specific message.""" + + @activity.defn + async def optional_bad_tool( + query: str, + tool_context: Optional[ToolContext] = None, # pyright: ignore[reportUnusedParameter, reportDeprecated] + ) -> str: + return query + + with pytest.raises(ValueError, match="not serializable"): + activity_as_tool( + optional_bad_tool, start_to_close_timeout=timedelta(seconds=30) + ) + + +def test_activity_as_tool_rejects_adk_context_under_any_name(): + """ADK injects into any param annotated with a context type, so all are rejected.""" + + @activity.defn + async def sneaky_tool(query: str, ctx: ToolContext) -> str: # pyright: ignore[reportUnusedParameter] + return query + + with pytest.raises(ValueError, match="not serializable"): + activity_as_tool(sneaky_tool, start_to_close_timeout=timedelta(seconds=30)) + + +def test_activity_as_tool_rejects_snapshot_under_other_name(): + """ToolContextSnapshot on a differently-named param would leak into the schema.""" + + @activity.defn + async def misnamed_tool(query: str, snap: ToolContextSnapshot) -> str: # pyright: ignore[reportUnusedParameter] + return query + + with pytest.raises(ValueError, match="named 'tool_context'"): + activity_as_tool(misnamed_tool, start_to_close_timeout=timedelta(seconds=30)) + + +def test_activity_as_tool_rejects_unannotated_tool_context(): + """The reserved name without an annotation gives an actionable error.""" + + @activity.defn + async def untyped_tool(query: str, tool_context) -> str: # type: ignore[no-untyped-def] # pyright: ignore[reportUnusedParameter, reportMissingParameterType] + return query + + with pytest.raises(ValueError, match="unannotated 'tool_context'"): + activity_as_tool(untyped_tool, start_to_close_timeout=timedelta(seconds=30)) + + +def test_activity_as_tool_rejects_other_tool_context_annotation(): + """The reserved name with an unrelated annotation gives an actionable error.""" + + @activity.defn + async def confused_tool(query: str, tool_context: dict[str, Any]) -> str: # pyright: ignore[reportUnusedParameter] + return query + + with pytest.raises(ValueError, match="reserved by ADK"): + activity_as_tool(confused_tool, start_to_close_timeout=timedelta(seconds=30)) + + +def test_activity_as_tool_without_tool_context_unchanged(): + """Activities without a tool_context parameter keep their exact schema.""" + + @activity.defn + async def plain_tool(query: str) -> str: + return query + + tool = FunctionTool( + func=activity_as_tool(plain_tool, start_to_close_timeout=timedelta(seconds=30)) + ) + assert set(_declared_properties(tool)) == {"query"} diff --git a/tests/contrib/google_adk_agents/test_google_adk_agents.py b/tests/contrib/google_adk_agents/test_google_adk_agents.py index 2bea29efd..2a1cf6aa1 100644 --- a/tests/contrib/google_adk_agents/test_google_adk_agents.py +++ b/tests/contrib/google_adk_agents/test_google_adk_agents.py @@ -14,6 +14,7 @@ """Integration tests for ADK Temporal support.""" +import inspect import json import logging import os @@ -68,7 +69,7 @@ async def get_weather(city: str) -> str: # type: ignore[reportUnusedParameter] def weather_agent(model_name: str) -> Agent: # Wraps 'get_weather' activity as a Tool - weather_tool = temporalio.contrib.google_adk_agents.workflow.activity_tool( + weather_tool = temporalio.contrib.google_adk_agents.workflow.activity_as_tool( get_weather, start_to_close_timeout=timedelta(seconds=60) ) @@ -285,40 +286,60 @@ async def test_single_agent(client: Client, use_local_model: bool): class ResearchModel(TestModel): - def responses(self) -> list[LlmResponse]: - return [ - LlmResponse( - content=Content( - role="model", - parts=[ - Part( - function_call=FunctionCall( - args={"agent_name": "researcher"}, - name="transfer_to_agent", - ) + """Scripted coordinator -> researcher -> writer flow. + + Responses are keyed off which agent is calling (via its instruction text in + the request's system instruction) rather than deduped against conversation + history, since the ADK rewrites cross-agent history between versions (e.g. + 2.4 converts prior transfer calls into "For context:" text parts). + """ + + _responses_by_instruction = { + "You are a coordinator": LlmResponse( + content=Content( + role="model", + parts=[ + Part( + function_call=FunctionCall( + args={"agent_name": "researcher"}, + name="transfer_to_agent", ) - ], - ) - ), - LlmResponse( - content=Content( - role="model", - parts=[ - Part( - function_call=FunctionCall( - args={"agent_name": "writer"}, name="transfer_to_agent" - ) + ) + ], + ) + ), + "You are a researcher": LlmResponse( + content=Content( + role="model", + parts=[ + Part( + function_call=FunctionCall( + args={"agent_name": "writer"}, name="transfer_to_agent" ) - ], - ) - ), - LlmResponse( - content=Content( - role="model", - parts=[Part(text="haiku")], - ) - ), - ] + ) + ], + ) + ), + "You are a poet": LlmResponse( + content=Content( + role="model", + parts=[Part(text="haiku")], + ) + ), + } + + def responses(self) -> list[LlmResponse]: + return list(self._responses_by_instruction.values()) + + async def generate_content_async( + self, llm_request: LlmRequest, stream: bool = False + ) -> AsyncGenerator[LlmResponse, None]: + instruction = str(llm_request.config.system_instruction or "") + for phrase, response in self._responses_by_instruction.items(): + if phrase in instruction: + yield response + return + raise ValueError(f"No scripted response for instruction: {instruction!r}") @classmethod def supported_models(cls) -> list[str]: @@ -679,7 +700,7 @@ def test_summary_and_summary_fn_raises(): @pytest.mark.asyncio async def test_agent_outside_workflow(): - """Test that an agent using TemporalModel and activity_tool works outside a Temporal workflow.""" + """Test that an agent using TemporalModel and activity_as_tool works outside a Temporal workflow.""" LLMRegistry.register(WeatherModel) agent = weather_agent("weather_model") @@ -806,13 +827,13 @@ async def run(self, prompt: str, model_name: str) -> str: name="complex_input_agent", model=TemporalModel(model_name), tools=[ - temporalio.contrib.google_adk_agents.workflow.activity_tool( + temporalio.contrib.google_adk_agents.workflow.activity_as_tool( book_trip, start_to_close_timeout=timedelta(seconds=60) ), - temporalio.contrib.google_adk_agents.workflow.activity_tool( + temporalio.contrib.google_adk_agents.workflow.activity_as_tool( summarize_payload, start_to_close_timeout=timedelta(seconds=60) ), - temporalio.contrib.google_adk_agents.workflow.activity_tool( + temporalio.contrib.google_adk_agents.workflow.activity_as_tool( method_holder.annotate_trip, start_to_close_timeout=timedelta(seconds=60), ), @@ -913,7 +934,7 @@ def supported_models(cls) -> list[str]: @pytest.mark.asyncio -async def test_activity_tool_supports_complex_inputs_via_adk(client: Client): +async def test_activity_as_tool_supports_complex_inputs_via_adk(client: Client): new_config = client.config() new_config["plugins"] = [GoogleAdkPlugin()] client = Client(**new_config) @@ -1099,3 +1120,41 @@ def test_explicitly_set_none_preserved() -> None: assert "cache_config" in serialized, "Explicitly-set None should be preserved" assert serialized["cache_config"] is None + + +def test_activity_as_tool_preserves_metadata() -> None: + """activity_as_tool wrapper preserves the original function's metadata. + + This ensures ADK's tool schema generation can inspect __annotations__ + and __module__ on the wrapper, which are needed by + ``_handle_params_as_deferred_annotations`` to resolve type hints. + """ + + @activity.defn + async def my_activity(city: str, count: int = 1) -> str: + """Get info for a city.""" + return f"{city}: {count}" + + tool = temporalio.contrib.google_adk_agents.workflow.activity_as_tool( + my_activity, start_to_close_timeout=timedelta(seconds=30) + ) + + # __name__ and __doc__ + assert tool.__name__ == "my_activity" + assert tool.__doc__ == "Get info for a city." + + # __annotations__ — critical for ADK type introspection + assert "city" in tool.__annotations__ + assert tool.__annotations__["city"] is str + assert tool.__annotations__["count"] is int + assert tool.__annotations__["return"] is str + + # __module__ — needed by _handle_params_as_deferred_annotations + assert tool.__module__ == my_activity.__module__ + + # __signature__ — must match the original, not *args/**kw + sig = inspect.signature(tool) + params = list(sig.parameters.keys()) + assert params == ["city", "count"] + assert sig.parameters["city"].annotation is str + assert sig.parameters["count"].default == 1 diff --git a/tests/contrib/google_adk_agents/test_mcp.py b/tests/contrib/google_adk_agents/test_mcp.py new file mode 100644 index 000000000..38303ffee --- /dev/null +++ b/tests/contrib/google_adk_agents/test_mcp.py @@ -0,0 +1,228 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for the stateless MCP toolset support in +``temporalio.contrib.google_adk_agents._mcp``. + +These exercise ``TemporalMcpToolSetProvider``'s generated activities directly +against a fake ``McpToolset``, so they don't require a real MCP server, a +Temporal test server, or a full ``Worker``. They are targeted regression tests +for the connection-leak fix (issue #1663): every call builds its own toolset, +honors that call's ``factory_argument``, and always closes the toolset. +""" + +from typing import Any, cast + +import pytest +from google.adk.events import EventActions +from google.adk.tools.mcp_tool import McpToolset + +from temporalio.contrib.google_adk_agents._mcp import ( + TemporalMcpToolSetProvider, + TemporalToolContext, + _CallToolArguments, + _GetToolsArguments, +) + + +class _FakeTool: + def __init__(self, name: str, *, fail_run: bool = False) -> None: + self.name = name + self.description = "a fake tool" + self.is_long_running = False + self.custom_metadata: dict[str, Any] | None = None + self._fail_run = fail_run + + def _get_declaration(self) -> None: + return None + + async def run_async( + self, + *, + args: dict[str, Any], + tool_context: Any, # pyright: ignore[reportUnusedParameter] + ) -> Any: + if self._fail_run: + raise RuntimeError("tool call failed") + return {"echo": args} + + +class _FakeToolset: + """Stands in for ``google.adk.tools.mcp_tool.McpToolset``. + + Tracks whether ``close()`` was called so tests can assert the stateless + path always tears the toolset down, and records the ``factory_argument`` + it was created with so tests can prove each call routes with its own + argument. + """ + + def __init__( + self, + factory_argument: Any = None, + *, + fail_get_tools: bool = False, + fail_run: bool = False, + ) -> None: + self.factory_argument = factory_argument + self.closed = False + self._fail_get_tools = fail_get_tools + self._fail_run = fail_run + + async def get_tools(self) -> list[_FakeTool]: + if self._fail_get_tools: + raise RuntimeError("get_tools failed") + return [_FakeTool("echo", fail_run=self._fail_run)] + + async def close(self) -> None: + self.closed = True + + +def _tool_context() -> TemporalToolContext: + return TemporalToolContext( + tool_confirmation=None, + function_call_id=None, + event_actions=EventActions(), + ) + + +def _call_tool_args(factory_argument: Any = None) -> _CallToolArguments: + return _CallToolArguments( + factory_argument=factory_argument, + name="echo", + arguments={"x": 1}, + tool_context=_tool_context(), + ) + + +async def test_call_tool_creates_and_closes_fresh_toolset_each_call(): + """Each call_tool builds its own toolset and closes it, every time.""" + created: list[_FakeToolset] = [] + + def factory(arg: Any) -> McpToolset: + toolset = _FakeToolset(arg) + created.append(toolset) + return cast(McpToolset, cast(object, toolset)) + + provider = TemporalMcpToolSetProvider("stateless_reuse", factory) + _, call_tool = provider._get_activities() + + for _ in range(5): + result = await call_tool(_call_tool_args()) + assert result.result == {"echo": {"x": 1}} + + # One fresh toolset per call, and every one was closed. + assert len(created) == 5 + assert all(t.closed for t in created) + + +async def test_get_tools_creates_and_closes_fresh_toolset(): + created: list[_FakeToolset] = [] + + def factory(arg: Any) -> McpToolset: + toolset = _FakeToolset(arg) + created.append(toolset) + return cast(McpToolset, cast(object, toolset)) + + provider = TemporalMcpToolSetProvider("stateless_list", factory) + get_tools, _ = provider._get_activities() + + tools = await get_tools(_GetToolsArguments(factory_argument=None)) + assert [t.name for t in tools] == ["echo"] + + assert len(created) == 1 + assert created[0].closed + + +async def test_factory_argument_honored_on_every_call(): + """The bug the reviewer flagged: a later call with a different + ``factory_argument`` must route with *its own* argument, never silently + reuse a connection opened for an earlier argument. + """ + created: list[_FakeToolset] = [] + + def factory(arg: Any) -> McpToolset: + toolset = _FakeToolset(arg) + created.append(toolset) + return cast(McpToolset, cast(object, toolset)) + + provider = TemporalMcpToolSetProvider("stateless_routing", factory) + _, call_tool = provider._get_activities() + + await call_tool(_call_tool_args(factory_argument="tenant-a")) + await call_tool(_call_tool_args(factory_argument="tenant-b")) + + assert [t.factory_argument for t in created] == ["tenant-a", "tenant-b"] + assert all(t.closed for t in created) + + +async def test_call_tool_closes_toolset_on_error(): + """A failure mid-call still closes the toolset (no leak on the error path).""" + created: list[_FakeToolset] = [] + + def factory(arg: Any) -> McpToolset: + toolset = _FakeToolset(arg, fail_run=True) + created.append(toolset) + return cast(McpToolset, cast(object, toolset)) + + provider = TemporalMcpToolSetProvider("stateless_run_error", factory) + _, call_tool = provider._get_activities() + + with pytest.raises(RuntimeError, match="tool call failed"): + await call_tool(_call_tool_args()) + + assert len(created) == 1 + assert created[0].closed + + +async def test_get_tools_closes_toolset_on_error(): + created: list[_FakeToolset] = [] + + def factory(arg: Any) -> McpToolset: + toolset = _FakeToolset(arg, fail_get_tools=True) + created.append(toolset) + return cast(McpToolset, cast(object, toolset)) + + provider = TemporalMcpToolSetProvider("stateless_list_error", factory) + get_tools, _ = provider._get_activities() + + with pytest.raises(RuntimeError, match="get_tools failed"): + await get_tools(_GetToolsArguments(factory_argument=None)) + + assert len(created) == 1 + assert created[0].closed + + +async def test_call_tool_no_matching_tool_still_closes(): + """A business-logic ApplicationError still closes the fresh toolset.""" + created: list[_FakeToolset] = [] + + def factory(arg: Any) -> McpToolset: + toolset = _FakeToolset(arg) + created.append(toolset) + return cast(McpToolset, cast(object, toolset)) + + provider = TemporalMcpToolSetProvider("stateless_no_match", factory) + _, call_tool = provider._get_activities() + + args = _CallToolArguments( + factory_argument=None, + name="does_not_exist", + arguments={}, + tool_context=_tool_context(), + ) + with pytest.raises(Exception, match="Unable to find matching mcp tool"): + await call_tool(args) + + assert len(created) == 1 + assert created[0].closed diff --git a/tests/contrib/google_adk_agents/test_stateful_mcp.py b/tests/contrib/google_adk_agents/test_stateful_mcp.py new file mode 100644 index 000000000..994598df9 --- /dev/null +++ b/tests/contrib/google_adk_agents/test_stateful_mcp.py @@ -0,0 +1,194 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Integration tests for the stateful (pooled) MCP toolset support in +``temporalio.contrib.google_adk_agents._mcp``. + +These drive the real ``connect`` -> dedicated-worker -> ``get_tools`` path +through a Temporal workflow, but against an in-memory fake ``McpToolset`` (no +subprocess, no real MCP server) so they run in CI. They prove the properties +the reviewer asked for: one toolset is built per workflow run, ``factory_argument`` +is consumed exactly once per run, distinct runs never share a toolset, and the +toolset is torn down when the workflow completes. +""" + +import uuid +from datetime import timedelta +from typing import Any, cast + +from google.adk.tools.mcp_tool import McpToolset + +from temporalio import workflow +from temporalio.client import Client +from temporalio.worker import Worker + +with workflow.unsafe.imports_passed_through(): + from temporalio.contrib.google_adk_agents import ( + GoogleAdkPlugin, + TemporalStatefulMcpToolSet, + TemporalStatefulMcpToolSetProvider, + ) + +# Populated inside the activity worker process (same process as the test) each +# time the toolset factory runs, so tests can inspect what was built. +CREATED: list["_FakeToolset"] = [] + + +class _FakeTool: + def __init__(self, name: str) -> None: + self.name = name + self.description = "a fake tool" + self.is_long_running = False + self.custom_metadata: dict[str, Any] | None = None + + def _get_declaration(self) -> None: + return None + + async def run_async( + self, + *, + args: dict[str, Any], + tool_context: Any, # pyright: ignore[reportUnusedParameter] + ) -> Any: + return {"echo": args} + + +class _FakeToolset: + """In-memory stand-in for ``google.adk.tools.mcp_tool.McpToolset``.""" + + def __init__(self, factory_argument: Any = None) -> None: + self.factory_argument = factory_argument + self.get_tools_calls = 0 + self.closed = False + + async def get_tools(self) -> list[_FakeTool]: + self.get_tools_calls += 1 + return [_FakeTool("echo")] + + async def close(self) -> None: + self.closed = True + + +def _factory(arg: Any) -> McpToolset: + toolset = _FakeToolset(arg) + CREATED.append(toolset) + return cast(McpToolset, cast(object, toolset)) + + +@workflow.defn +class StatefulMcpWorkflow: + @workflow.run + async def run(self, factory_argument: Any | None) -> list[str]: + async with TemporalStatefulMcpToolSet( + "stateful_set", + factory_argument=factory_argument, + ) as toolset: + # Two calls against the same persistent toolset -- exercises reuse. + tools_1 = await toolset.get_tools() + tools_2 = await toolset.get_tools() + return [t.name for t in tools_1] + [t.name for t in tools_2] + + +def _make_client( + client: Client, provider: TemporalStatefulMcpToolSetProvider +) -> Client: + new_config = client.config() + new_config["plugins"] = [GoogleAdkPlugin(toolset_providers=[provider])] + return Client(**new_config) + + +async def test_stateful_one_toolset_per_run_and_teardown(client: Client): + CREATED.clear() + provider = TemporalStatefulMcpToolSetProvider("stateful_set", _factory) + client = _make_client(client, provider) + + async with Worker( + client, + task_queue="adk-stateful-mcp", + workflows=[StatefulMcpWorkflow], + max_cached_workflows=0, + ): + result = await client.execute_workflow( + StatefulMcpWorkflow.run, + None, + id=f"stateful-mcp-{uuid.uuid4()}", + task_queue="adk-stateful-mcp", + execution_timeout=timedelta(seconds=60), + ) + + assert result == ["echo", "echo"] + # Exactly one toolset built for the whole run... + assert len(CREATED) == 1 + # ...reused across both get_tools calls (state maintained, not per-call)... + assert CREATED[0].get_tools_calls == 2 + # ...and closed when the workflow completed (no leak). + assert CREATED[0].closed + # The dedicated-worker toolset registry is empty after teardown. + assert provider._toolsets == {} + + +async def test_stateful_factory_argument_consumed_once(client: Client): + CREATED.clear() + provider = TemporalStatefulMcpToolSetProvider("stateful_set", _factory) + client = _make_client(client, provider) + + async with Worker( + client, + task_queue="adk-stateful-mcp-arg", + workflows=[StatefulMcpWorkflow], + max_cached_workflows=0, + ): + await client.execute_workflow( + StatefulMcpWorkflow.run, + {"tenant": "acme"}, + id=f"stateful-mcp-{uuid.uuid4()}", + task_queue="adk-stateful-mcp-arg", + execution_timeout=timedelta(seconds=60), + ) + + assert len(CREATED) == 1 + assert CREATED[0].factory_argument == {"tenant": "acme"} + + +async def test_stateful_no_cross_run_sharing(client: Client): + CREATED.clear() + provider = TemporalStatefulMcpToolSetProvider("stateful_set", _factory) + client = _make_client(client, provider) + + async with Worker( + client, + task_queue="adk-stateful-mcp-iso", + workflows=[StatefulMcpWorkflow], + max_cached_workflows=0, + ): + await client.execute_workflow( + StatefulMcpWorkflow.run, + "tenant-a", + id=f"stateful-mcp-a-{uuid.uuid4()}", + task_queue="adk-stateful-mcp-iso", + execution_timeout=timedelta(seconds=60), + ) + await client.execute_workflow( + StatefulMcpWorkflow.run, + "tenant-b", + id=f"stateful-mcp-b-{uuid.uuid4()}", + task_queue="adk-stateful-mcp-iso", + execution_timeout=timedelta(seconds=60), + ) + + # Two distinct runs -> two distinct toolsets, each with its own argument. + assert len(CREATED) == 2 + assert {t.factory_argument for t in CREATED} == {"tenant-a", "tenant-b"} + assert all(t.closed for t in CREATED) + assert provider._toolsets == {} diff --git a/tests/contrib/google_genai/__init__.py b/tests/contrib/google_genai/__init__.py new file mode 100644 index 000000000..26a790b56 --- /dev/null +++ b/tests/contrib/google_genai/__init__.py @@ -0,0 +1 @@ +"""Tests for the `google-genai` SDK Temporal integration.""" diff --git a/tests/contrib/google_genai/echo_mcp_server.py b/tests/contrib/google_genai/echo_mcp_server.py new file mode 100644 index 000000000..a69ac7b22 --- /dev/null +++ b/tests/contrib/google_genai/echo_mcp_server.py @@ -0,0 +1,15 @@ +"""A minimal stdio MCP server used by the google_genai MCP tests.""" + +from mcp.server.fastmcp import FastMCP + +mcp = FastMCP("echo-server") + + +@mcp.tool() +def echo(message: str) -> str: + """Return the input message unchanged.""" + return message + + +if __name__ == "__main__": + mcp.run() diff --git a/tests/contrib/google_genai/test_gemini.py b/tests/contrib/google_genai/test_gemini.py new file mode 100644 index 000000000..0881f63b3 --- /dev/null +++ b/tests/contrib/google_genai/test_gemini.py @@ -0,0 +1,2052 @@ +"""Integration tests for the Google Gemini SDK Temporal integration. + +Tests cover: +- Basic generate_content through workflow +- Tool calling via activity_as_tool (single arg, multi arg, class method) +- Workflow method as a plain tool (runs in-workflow, not as an activity) +- Tool failure propagation +- Multiple sequential tool calls with arg verification +- Batched streaming via generate_content_stream +- Per-request http_options propagation +- File upload (str path + io.BytesIO) and download via TemporalAsyncFiles +- File search store upload via TemporalAsyncFileSearchStores +- Multi-turn chat via client.chats +- TemporalAsyncClient wiring (files, file_search_stores) +- _TemporalApiClient edge cases (sync raises) +- activity_as_tool validation and metadata preservation +- TemporalAsyncClient configuration +- Interactions API (create, batched streaming, get, cancel, delete) +- Managed agents (create, get, list, delete); webhooks unsupported +""" + +import inspect +import io +import json +import uuid +from datetime import timedelta +from typing import Any, Callable, cast +from unittest.mock import AsyncMock, MagicMock + +import pytest +from google.genai import Client as GeminiClient +from google.genai import types +from google.genai.interactions import ( + Agent, # pyright: ignore[reportPrivateImportUsage] + InteractionSSEEvent, +) +from google.genai.types import HttpResponse as SdkHttpResponse + +from temporalio import activity, workflow +from temporalio.client import Client, WorkflowFailureError +from temporalio.common import RetryPolicy +from temporalio.contrib.google_genai import ( + GoogleGenAIError, + GoogleGenAIPlugin, + activity_as_tool, +) +from temporalio.contrib.google_genai._compat import Interaction +from temporalio.contrib.google_genai._models import ( + _GeminiApiRequest, + _GeminiApiResponse, + _GeminiApiStreamedResponse, + _GeminiDownloadFileRequest, + _GeminiInteractionIdRequest, + _GeminiInteractionRequest, + _GeminiInteractionStreamedResponse, + _GeminiUploadFileRequest, + _GeminiUploadToFileSearchStoreRequest, +) +from temporalio.contrib.google_genai._temporal_api_client import ( + _TemporalApiClient, +) +from temporalio.contrib.google_genai._temporal_async_client import ( + TemporalAsyncClient, + _closure_if_bound_method, + _wrap_bound_method_tools, +) +from temporalio.contrib.google_genai._temporal_file_search_stores import ( + TemporalAsyncFileSearchStores, +) +from temporalio.contrib.google_genai._temporal_files import ( + TemporalAsyncFiles, +) +from temporalio.contrib.google_genai._temporal_interactions import _deserialize +from temporalio.exceptions import ApplicationError +from temporalio.worker import Replayer +from temporalio.workflow import ActivityConfig +from tests.helpers import new_worker + +# --------------------------------------------------------------------------- +# Mock response helpers +# --------------------------------------------------------------------------- + + +def make_text_response(text: str) -> str: + """Build a JSON body string for a simple text response.""" + return json.dumps( + { + "candidates": [ + { + "content": { + "role": "model", + "parts": [{"text": text}], + }, + "finishReason": "STOP", + } + ], + "usageMetadata": { + "promptTokenCount": 5, + "candidatesTokenCount": 10, + }, + } + ) + + +def make_function_call_response(fn_name: str, args: dict) -> str: + """Build a JSON body string for a function-call response.""" + return json.dumps( + { + "candidates": [ + { + "content": { + "role": "model", + "parts": [{"functionCall": {"name": fn_name, "args": args}}], + }, + "finishReason": "STOP", + } + ], + "usageMetadata": { + "promptTokenCount": 10, + "candidatesTokenCount": 15, + }, + } + ) + + +INTERACTION_ID = "interactions/test-123" + + +def make_interaction_dict(status: str = "completed") -> dict[str, Any]: + """Build a minimal Interaction dict as the API would return it.""" + return {"id": INTERACTION_ID, "object": "interaction", "status": status} + + +def make_interaction_sse_events() -> list[dict[str, Any]]: + """Build a small SSE event sequence for a streamed interaction. + + Includes a sparse ``interaction.created`` payload (just ``id`` and + ``object``) to exercise the lenient ``_deserialize`` rehydration. + """ + return [ + { + "event_type": "interaction.created", + "interaction": {"id": INTERACTION_ID, "object": "interaction"}, + }, + { + "event_type": "step.delta", + "index": 0, + "delta": {"type": "text", "text": "Hello "}, + }, + { + "event_type": "step.delta", + "index": 0, + "delta": {"type": "text", "text": "world"}, + }, + {"event_type": "interaction.completed", "interaction": make_interaction_dict()}, + ] + + +def make_agent_dict(agent_id: str = "test-agent") -> dict[str, Any]: + """Build a minimal managed-agent dict as the API would return it.""" + return {"id": agent_id, "system_instruction": "Be helpful."} + + +# --------------------------------------------------------------------------- +# Tool call tracker — records every tool invocation for assertion +# --------------------------------------------------------------------------- + + +class ToolCallTracker: + """Tracks tool invocations across activities and workflow methods. + + Each tool appends (name, args_dict) to ``calls`` so tests can assert + exactly which tools were called, in what order, with what arguments. + """ + + def __init__(self) -> None: + self.calls: list[tuple[str, dict]] = [] + + @activity.defn + async def get_weather(self, city: str) -> str: + """Get the weather for a given city.""" + self.calls.append(("get_weather", {"city": city})) + return f"Weather in {city}: Sunny, 20C" + + @activity.defn + async def get_weather_country(self, city: str, country: str) -> str: + """Get the weather for a given city in a country.""" + self.calls.append(("get_weather_country", {"city": city, "country": country})) + return f"Weather in {city}, {country}: Rainy, 15C" + + @activity.defn + async def get_weather_failure(self, city: str) -> str: + """Activity that always fails.""" + self.calls.append(("get_weather_failure", {"city": city})) + raise ApplicationError("Weather service unavailable", non_retryable=True) + + +# --------------------------------------------------------------------------- +# Test helper: tracking gemini_api_client_async_request activity +# --------------------------------------------------------------------------- + + +class GeminiApiCallTracker: + """A test replacement for the gemini_api_client activities. + + Records every ``_GeminiApiRequest`` received and returns canned + ``_GeminiApiResponse`` bodies in order. After the workflow completes, + inspect ``requests`` to verify exactly what the integration sent. + + For streamed requests, the mock response is split into per-line chunks + to simulate multiple streamed chunks. + + The real ``GoogleGenAIPlugin`` is still used for its data converter, sandbox + passthrough, and workflow runner configuration — only its activity + registration is suppressed so this tracker can take its place. + """ + + def __init__(self, mock_responses: list[str]) -> None: + self._mock_responses = mock_responses + self.requests: list[_GeminiApiRequest] = [] + self.file_upload_requests: list[_GeminiUploadFileRequest] = [] + self.file_download_requests: list[_GeminiDownloadFileRequest] = [] + self.file_search_store_upload_requests: list[ + _GeminiUploadToFileSearchStoreRequest + ] = [] + self.interaction_requests: list[_GeminiInteractionRequest] = [] + self.interaction_id_requests: list[_GeminiInteractionIdRequest] = [] + self._call_index = 0 + + def _next_response(self, req: _GeminiApiRequest) -> str: + self.requests.append(req) + idx = self._call_index + self._call_index += 1 + if idx >= len(self._mock_responses): + raise ApplicationError( + f"No more mock responses (called {idx + 1} times, " + f"have {len(self._mock_responses)})", + non_retryable=True, + ) + return self._mock_responses[idx] + + @activity.defn + async def gemini_api_client_async_request( + self, req: _GeminiApiRequest + ) -> _GeminiApiResponse: + return _GeminiApiResponse( + headers={"content-type": "application/json"}, + body=self._next_response(req), + ) + + @activity.defn + async def gemini_api_client_async_request_streamed( + self, req: _GeminiApiRequest + ) -> _GeminiApiStreamedResponse: + body = self._next_response(req) + # Split the response text into word-level chunks so tests can + # verify that multiple chunks are yielded back to the workflow. + parsed = json.loads(body) + full_text = ( + parsed.get("candidates", [{}])[0] + .get("content", {}) + .get("parts", [{}])[0] + .get("text", "") + ) + words = full_text.split() + chunks = [] + for word in words: + chunks.append( + _GeminiApiResponse( + headers={"content-type": "application/json"}, + body=make_text_response(word), + ) + ) + return _GeminiApiStreamedResponse(chunks=chunks) + + @activity.defn + async def gemini_files_upload(self, req: _GeminiUploadFileRequest) -> types.File: + self.file_upload_requests.append(req) + return types.File( + name="files/test-uploaded-file", + uri="https://fake.uri/files/test-uploaded-file", + size_bytes=len(req.file_bytes) if req.file_bytes else 0, + ) + + @activity.defn + async def gemini_files_download(self, req: _GeminiDownloadFileRequest) -> bytes: + self.file_download_requests.append(req) + return b"fake file content" + + @activity.defn + async def gemini_file_search_stores_upload( + self, req: _GeminiUploadToFileSearchStoreRequest + ) -> types.UploadToFileSearchStoreOperation: + self.file_search_store_upload_requests.append(req) + return types.UploadToFileSearchStoreOperation.model_construct( + name="operations/test-op", + ) + + @activity.defn + async def gemini_interactions_create( + self, req: _GeminiInteractionRequest + ) -> dict[str, Any]: + self.interaction_requests.append(req) + return make_interaction_dict() + + @activity.defn + async def gemini_interactions_create_streamed( + self, req: _GeminiInteractionRequest + ) -> _GeminiInteractionStreamedResponse: + self.interaction_requests.append(req) + return _GeminiInteractionStreamedResponse(events=make_interaction_sse_events()) + + @activity.defn + async def gemini_interactions_get( + self, req: _GeminiInteractionIdRequest + ) -> dict[str, Any]: + self.interaction_id_requests.append(req) + return make_interaction_dict() + + @activity.defn + async def gemini_interactions_get_streamed( + self, req: _GeminiInteractionIdRequest + ) -> _GeminiInteractionStreamedResponse: + self.interaction_id_requests.append(req) + return _GeminiInteractionStreamedResponse(events=make_interaction_sse_events()) + + @activity.defn + async def gemini_interactions_delete(self, req: _GeminiInteractionIdRequest) -> Any: + self.interaction_id_requests.append(req) + return {"deleted": True} + + @activity.defn + async def gemini_interactions_cancel( + self, req: _GeminiInteractionIdRequest + ) -> dict[str, Any]: + self.interaction_id_requests.append(req) + return make_interaction_dict(status="cancelled") + + @activity.defn + async def gemini_agents_create( + self, req: _GeminiInteractionRequest + ) -> dict[str, Any]: + self.interaction_requests.append(req) + return make_agent_dict(req.params.get("id", "test-agent")) + + @activity.defn + async def gemini_agents_list( + self, req: _GeminiInteractionRequest + ) -> dict[str, Any]: + self.interaction_requests.append(req) + return {"agents": [make_agent_dict()], "next_page_token": "next-tok"} + + @activity.defn + async def gemini_agents_get( + self, req: _GeminiInteractionIdRequest + ) -> dict[str, Any]: + self.interaction_id_requests.append(req) + return make_agent_dict(req.id) + + @activity.defn + async def gemini_agents_delete( + self, req: _GeminiInteractionIdRequest + ) -> dict[str, Any]: + self.interaction_id_requests.append(req) + return {"id": req.id, "deleted": True} + + +def apply_plugin( + client: Client, mock_responses: list[str] +) -> tuple[Client, GeminiApiCallTracker]: + """Create a real GoogleGenAIPlugin whose activities include a tracking fake. + + Monkey-patches ``GeminiApiCaller.activities`` so that when the plugin + constructs itself, it registers our tracking activity instead of + the real ones. Everything else — data converter, sandbox passthrough, + workflow runner — is the real plugin code. + + Returns the configured Temporal client and the tracker. + """ + from temporalio.contrib.google_genai._gemini_activity import GeminiApiCaller + + tracker = GeminiApiCallTracker(mock_responses) + original_activities = GeminiApiCaller.activities + GeminiApiCaller.activities = lambda self: [ # type: ignore[method-assign] + tracker.gemini_api_client_async_request, + tracker.gemini_api_client_async_request_streamed, + tracker.gemini_files_upload, + tracker.gemini_files_download, + tracker.gemini_file_search_stores_upload, + tracker.gemini_interactions_create, + tracker.gemini_interactions_create_streamed, + tracker.gemini_interactions_get, + tracker.gemini_interactions_get_streamed, + tracker.gemini_interactions_delete, + tracker.gemini_interactions_cancel, + tracker.gemini_agents_create, + tracker.gemini_agents_list, + tracker.gemini_agents_get, + tracker.gemini_agents_delete, + ] + try: + gemini = GeminiClient(api_key="fake-test-key") + plugin = GoogleGenAIPlugin(gemini) + finally: + GeminiApiCaller.activities = original_activities # type: ignore[method-assign] + + config = client.config() + config["plugins"] = [plugin] + return Client(**config), tracker + + +# --------------------------------------------------------------------------- +# Workflows +# --------------------------------------------------------------------------- + + +@workflow.defn +class SimpleGenerateWorkflow: + """Workflow that does a simple generate_content call.""" + + @workflow.run + async def run(self, prompt: str) -> str: + client = TemporalAsyncClient() + response = await client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + ) + return response.text or "" + + +@workflow.defn +class SingleArgToolWorkflow: + """Workflow that uses activity_as_tool for a single-arg tool.""" + + @workflow.run + async def run(self, prompt: str) -> str: + client = TemporalAsyncClient() + response = await client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + config=types.GenerateContentConfig( + tools=[ + activity_as_tool( + ToolCallTracker.get_weather, + activity_config=ActivityConfig( + start_to_close_timeout=timedelta(seconds=10), + ), + ), + ], + ), + ) + return response.text or "" + + +@workflow.defn +class MultiArgToolWorkflow: + """Workflow with multi-arg tool.""" + + @workflow.run + async def run(self, prompt: str) -> str: + client = TemporalAsyncClient() + response = await client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + config=types.GenerateContentConfig( + tools=[ + activity_as_tool( + ToolCallTracker.get_weather_country, + activity_config=ActivityConfig( + start_to_close_timeout=timedelta(seconds=10), + ), + ), + ], + ), + ) + return response.text or "" + + +@workflow.defn +class ToolFailureWorkflow: + """Workflow with a tool that always fails.""" + + @workflow.run + async def run(self, prompt: str) -> str: + client = TemporalAsyncClient() + response = await client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + config=types.GenerateContentConfig( + tools=[ + activity_as_tool( + ToolCallTracker.get_weather_failure, + activity_config=ActivityConfig( + start_to_close_timeout=timedelta(seconds=10), + retry_policy=RetryPolicy(maximum_attempts=1), + ), + ), + ], + ), + ) + return response.text or "" + + +@workflow.defn +class MultipleToolsWorkflow: + """Workflow with multiple tools that are called in sequence.""" + + @workflow.run + async def run(self, prompt: str) -> str: + client = TemporalAsyncClient() + response = await client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + config=types.GenerateContentConfig( + tools=[ + activity_as_tool( + ToolCallTracker.get_weather, + activity_config=ActivityConfig( + start_to_close_timeout=timedelta(seconds=10), + ), + ), + activity_as_tool( + ToolCallTracker.get_weather_country, + activity_config=ActivityConfig( + start_to_close_timeout=timedelta(seconds=10), + ), + ), + ], + ), + ) + return response.text or "" + + +@workflow.defn +class WorkflowMethodToolWorkflow: + """Workflow that passes a plain method as a tool (runs in-workflow, not as an activity).""" + + def __init__(self) -> None: + self.tool_calls: list[tuple[str, dict]] = [] + + @workflow.run + async def run(self, prompt: str) -> str: + client = TemporalAsyncClient() + response = await client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + config=types.GenerateContentConfig( + tools=[self.lookup_city], + ), + ) + return response.text or "" + + async def lookup_city(self, city: str) -> str: + """Look up info about a city.""" + self.tool_calls.append(("lookup_city", {"city": city})) + return f"{city} is a great place to visit" + + @workflow.query + def get_tool_calls(self) -> list[tuple[str, dict]]: + return self.tool_calls + + +@workflow.defn +class StreamedGenerateWorkflow: + """Workflow that uses generate_content_stream.""" + + @workflow.run + async def run(self, prompt: str) -> list[str]: + client = TemporalAsyncClient() + chunks: list[str] = [] + async for chunk in await client.models.generate_content_stream( + model="gemini-2.5-flash", + contents=prompt, + ): + if chunk.text: + chunks.append(chunk.text) + return chunks + + +@workflow.defn +class HttpOptionsWorkflow: + """Workflow that passes per-request http_options through generate_content.""" + + @workflow.run + async def run(self, prompt: str, http_options: types.HttpOptionsDict) -> str: + client = TemporalAsyncClient() + response = await client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + config=types.GenerateContentConfig( + http_options=types.HttpOptions.model_validate(http_options), + ), + ) + return response.text or "" + + +@workflow.defn +class FullIntegrationWorkflow: + """Exercises every activity path in a single workflow run. + + Uses the real GoogleGenAIPlugin activities (not the tracker), so this + tests the actual activity implementations end-to-end with a mocked + genai.Client. + """ + + @workflow.run + async def run(self, prompt: str) -> dict[str, Any]: + client = TemporalAsyncClient() + results: dict[str, Any] = {} + + # 1. generate_content (async_request activity) + response = await client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + ) + results["generate"] = response.text or "" + + # 2. generate_content_stream (async_request_streamed activity) + chunks: list[str] = [] + async for chunk in await client.models.generate_content_stream( + model="gemini-2.5-flash", + contents=prompt, + ): + if chunk.text: + chunks.append(chunk.text) + results["stream_chunks"] = chunks + + # 3. files.upload (gemini_files_upload activity) + uploaded = await client.files.upload( + file="/tmp/fake.txt", + config=types.UploadFileConfig(display_name="Integration Test"), + ) + results["upload_name"] = uploaded.name or "" + + # 4. files.download (gemini_files_download activity) + data = await client.files.download(file="files/some-file") + results["download"] = data.decode() if isinstance(data, bytes) else str(data) + + # 5. file_search_stores.upload_to_file_search_store activity + store_name = "fileSearchStores/test" + op = await client.file_search_stores.upload_to_file_search_store( + file_search_store_name=store_name, + file="/tmp/doc.txt", + ) + results["fss_upload_op"] = op.name or "" + + # 6. generate_content grounded with file_search tool (RAG query) + rag_response = await client.models.generate_content( + model="gemini-2.5-flash", + contents="What does the document say?", + config=types.GenerateContentConfig( + tools=[ + types.Tool( + file_search=types.FileSearch( + file_search_store_names=[store_name], + ), + ), + ], + ), + ) + results["rag"] = rag_response.text or "" + + # 7. Clean up the file search store + await client.file_search_stores.delete( + name=store_name, + config=types.DeleteFileSearchStoreConfig(force=True), + ) + results["store_deleted"] = True + + # 8. interactions.create (gemini_interactions_create activity) + interaction = await client.interactions.create( + model="gemini-2.5-flash", + input=prompt, + ) + assert isinstance(interaction, Interaction) + results["interaction_id"] = interaction.id + + # 9. interactions.create streamed (gemini_interactions_create_streamed) + stream = await client.interactions.create( + model="gemini-2.5-flash", + input=prompt, + stream=True, + ) + assert not isinstance(stream, Interaction) + event_types: list[str] = [] + async with stream: + async for event in stream: + event_types.append(event.event_type) + results["interaction_events"] = event_types + + # 10. agents.create (gemini_agents_create activity) + agent = await client.agents.create( + id="test-agent", + system_instruction="Be helpful.", + ) + results["agent_id"] = agent.id + + return results + + +@workflow.defn +class FileUploadStrWorkflow: + """Workflow that uploads a file via str path.""" + + @workflow.run + async def run(self, file_path: str) -> str: + client = TemporalAsyncClient() + uploaded = await client.files.upload( + file=file_path, + config=types.UploadFileConfig( + display_name="Test File", + mime_type="text/plain", + ), + ) + return uploaded.name or "" + + +@workflow.defn +class FileUploadBytesWorkflow: + """Workflow that uploads a file via io.BytesIO.""" + + @workflow.run + async def run(self, data: bytes) -> str: + client = TemporalAsyncClient() + uploaded = await client.files.upload( + file=io.BytesIO(data), + config=types.UploadFileConfig( + display_name="Bytes File", + mime_type="text/plain", + ), + ) + return uploaded.name or "" + + +@workflow.defn +class FileDownloadWorkflow: + """Workflow that downloads a file by name.""" + + @workflow.run + async def run(self, file_name: str) -> bytes: + client = TemporalAsyncClient() + return await client.files.download(file=file_name) + + +@workflow.defn +class FileSearchStoreUploadWorkflow: + """Workflow that uploads to a file search store.""" + + @workflow.run + async def run(self, store_name: str, file_path: str) -> str: + client = TemporalAsyncClient() + op = await client.file_search_stores.upload_to_file_search_store( + file_search_store_name=store_name, + file=file_path, + config=types.UploadToFileSearchStoreConfig( + display_name="Test Doc", + mime_type="text/plain", + ), + ) + return op.name or "" + + +@workflow.defn +class RegisterFilesWorkflow: + """Workflow that calls files.register_files.""" + + @workflow.run + async def run(self, uris: list[str]) -> str: + client = TemporalAsyncClient() + # auth arg is ignored by TemporalAsyncFiles — the activity uses + # credentials from GoogleGenAIPlugin init. We pass a dummy here; + # can't import google.auth.credentials in the sandbox so we + # use a sentinel that satisfies the type at runtime. + resp = await client.files.register_files( + auth=None, # type: ignore[arg-type] + uris=uris, + ) + return str(len(resp.files or [])) + + +@workflow.defn +class ChatWorkflow: + """Workflow that uses client.chats for multi-turn conversation.""" + + @workflow.run + async def run(self, prompt: str) -> list[str]: + client = TemporalAsyncClient() + chat = client.chats.create( + model="gemini-2.5-flash", + ) + r1 = await chat.send_message(prompt) + r2 = await chat.send_message("Follow up question") + return [r1.text or "", r2.text or ""] + + +@workflow.defn +class InteractionCreateWorkflow: + """Workflow that creates an interaction (non-streaming).""" + + @workflow.run + async def run(self, prompt: str) -> dict[str, Any]: + client = TemporalAsyncClient() + interaction = await client.interactions.create( + model="gemini-2.5-flash", + input=prompt, + timeout=120, + ) + assert isinstance(interaction, Interaction) + return {"id": interaction.id, "status": str(interaction.status)} + + +@workflow.defn +class InteractionStreamWorkflow: + """Workflow that creates a streamed interaction and collects event types.""" + + @workflow.run + async def run(self, prompt: str) -> list[str]: + client = TemporalAsyncClient() + stream = await client.interactions.create( + model="gemini-2.5-flash", + input=prompt, + stream=True, + ) + assert not isinstance(stream, Interaction) + event_types: list[str] = [] + async with stream: + async for event in stream: + event_types.append(event.event_type) + return event_types + + +@workflow.defn +class InteractionLifecycleWorkflow: + """Workflow that gets, cancels, and deletes an interaction.""" + + @workflow.run + async def run(self, interaction_id: str) -> dict[str, Any]: + client = TemporalAsyncClient() + got = await client.interactions.get(interaction_id) + assert isinstance(got, Interaction) + cancelled = await client.interactions.cancel(interaction_id) + deleted = await client.interactions.delete(interaction_id) + return { + "got_id": got.id, + "cancel_status": str(cancelled.status), + "deleted": deleted, + } + + +@workflow.defn +class AgentsWorkflow: + """Workflow that exercises managed-agent CRUD.""" + + @workflow.run + async def run(self) -> dict[str, Any]: + client = TemporalAsyncClient() + agent = await client.agents.create( + id="test-agent", + system_instruction="Be helpful.", + ) + got = await client.agents.get("test-agent") + listing = await client.agents.list(page_size=10) + deleted = await client.agents.delete("test-agent") + return { + "created_id": agent.id, + "got_id": got.id, + "listed_ids": [a.id for a in (listing.agents or [])], + "next_page_token": listing.next_page_token, + # AgentDeleteResponse defines no fields; the API's JSON comes + # back as extras, so return the dict form. + "delete_response": deleted.model_dump(mode="json"), + } + + +@workflow.defn +class WebhooksUnsupportedWorkflow: + """Workflow that verifies client.webhooks raises a clear error.""" + + @workflow.run + async def run(self) -> str: + client = TemporalAsyncClient() + try: + _ = client.webhooks + except RuntimeError as e: + return str(e) + return "no error" + + +# =========================================================================== +# Integration tests — run workflows against a real Temporal test server +# =========================================================================== + + +async def test_simple_generate_content(client: Client): + """Basic generate_content returns text through a workflow.""" + new_client, _ = apply_plugin(client, [make_text_response("Hello from Gemini!")]) + + async with new_worker(new_client, SimpleGenerateWorkflow) as worker: + result = await new_client.execute_workflow( + SimpleGenerateWorkflow.run, + "Say hello", + id=f"gemini-simple-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert result == "Hello from Gemini!" + + +async def test_tool_call_single_arg(client: Client): + """Tool calling with a single-argument activity via AFC.""" + tool_tracker = ToolCallTracker() + new_client, _ = apply_plugin( + client, + [ + make_function_call_response("get_weather", {"city": "Tokyo"}), + make_text_response("The weather in Tokyo is sunny and 20C."), + ], + ) + + async with new_worker( + new_client, + SingleArgToolWorkflow, + activities=[tool_tracker.get_weather], + ) as worker: + result = await new_client.execute_workflow( + SingleArgToolWorkflow.run, + "What's the weather in Tokyo?", + id=f"gemini-tool-single-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert tool_tracker.calls == [("get_weather", {"city": "Tokyo"})] + assert result == "The weather in Tokyo is sunny and 20C." + + +async def test_tool_call_multi_arg(client: Client): + """Tool calling with a multi-argument activity.""" + tool_tracker = ToolCallTracker() + new_client, _ = apply_plugin( + client, + [ + make_function_call_response( + "get_weather_country", {"city": "Paris", "country": "France"} + ), + make_text_response("Paris, France: Rainy, 15C."), + ], + ) + + async with new_worker( + new_client, + MultiArgToolWorkflow, + activities=[tool_tracker.get_weather_country], + ) as worker: + result = await new_client.execute_workflow( + MultiArgToolWorkflow.run, + "What's the weather in Paris, France?", + id=f"gemini-tool-multi-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert tool_tracker.calls == [ + ("get_weather_country", {"city": "Paris", "country": "France"}) + ] + assert result == "Paris, France: Rainy, 15C." + + +async def test_tool_failure_propagation(client: Client): + """Tool activity failure causes the workflow to fail.""" + tool_tracker = ToolCallTracker() + new_client, _ = apply_plugin( + client, + [ + make_function_call_response("get_weather_failure", {"city": "Nowhere"}), + ], + ) + + async with new_worker( + new_client, + ToolFailureWorkflow, + activities=[tool_tracker.get_weather_failure], + ) as worker: + with pytest.raises(WorkflowFailureError): + await new_client.execute_workflow( + ToolFailureWorkflow.run, + "Weather in Nowhere?", + id=f"gemini-tool-fail-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert tool_tracker.calls == [("get_weather_failure", {"city": "Nowhere"})] + + +async def test_multiple_tools_sequential(client: Client): + """Multiple tools called in sequence within one generate_content call.""" + tool_tracker = ToolCallTracker() + new_client, _ = apply_plugin( + client, + [ + make_function_call_response("get_weather", {"city": "Tokyo"}), + make_function_call_response( + "get_weather_country", {"city": "Paris", "country": "France"} + ), + make_text_response("Tokyo is sunny; Paris is rainy."), + ], + ) + + async with new_worker( + new_client, + MultipleToolsWorkflow, + activities=[ + tool_tracker.get_weather, + tool_tracker.get_weather_country, + ], + ) as worker: + result = await new_client.execute_workflow( + MultipleToolsWorkflow.run, + "Compare Tokyo and Paris weather", + id=f"gemini-multi-tools-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=15), + ) + + assert tool_tracker.calls == [ + ("get_weather", {"city": "Tokyo"}), + ("get_weather_country", {"city": "Paris", "country": "France"}), + ] + assert result == "Tokyo is sunny; Paris is rainy." + + +async def test_workflow_method_as_tool(client: Client): + """A plain workflow method (not an activity) used as a tool runs in-workflow.""" + new_client, _ = apply_plugin( + client, + [ + make_function_call_response("lookup_city", {"city": "Berlin"}), + make_text_response("Berlin is wonderful."), + ], + ) + + async with new_worker(new_client, WorkflowMethodToolWorkflow) as worker: + handle = await new_client.start_workflow( + WorkflowMethodToolWorkflow.run, + "Tell me about Berlin", + id=f"gemini-wf-method-tool-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + result = await handle.result() + # Query must happen while worker is alive + tool_calls = await handle.query(WorkflowMethodToolWorkflow.get_tool_calls) + + assert tool_calls == [("lookup_city", {"city": "Berlin"})] + assert result == "Berlin is wonderful." + + +async def test_streamed_generate_content(client: Client): + """generate_content_stream collects batched chunks from the activity.""" + new_client, _ = apply_plugin( + client, [make_text_response("The quick brown fox jumps over the lazy dog")] + ) + + async with new_worker(new_client, StreamedGenerateWorkflow) as worker: + result = await new_client.execute_workflow( + StreamedGenerateWorkflow.run, + "Say something", + id=f"gemini-streamed-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + # The tracker splits the text into per-word chunks + assert len(result) == 9 + assert " ".join(result) == "The quick brown fox jumps over the lazy dog" + + +# =========================================================================== +# http_options propagation tests - per request overrides +# =========================================================================== + + +async def test_http_options_headers_propagate(client: Client): + """Custom headers passed via http_options arrive at the activity.""" + new_client, api_tracker = apply_plugin(client, [make_text_response("ok")]) + + async with new_worker(new_client, HttpOptionsWorkflow) as worker: + await new_client.execute_workflow( + HttpOptionsWorkflow.run, + args=["hi", {"headers": {"X-Custom": "test-value"}}], + id=f"gemini-http-headers-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert len(api_tracker.requests) == 1 + opts = api_tracker.requests[0].http_options_overrides + assert opts is not None + assert opts.headers == {"X-Custom": "test-value"} + + +async def test_http_options_api_version_propagates(client: Client): + """api_version passed via http_options arrives at the activity.""" + new_client, api_tracker = apply_plugin(client, [make_text_response("ok")]) + + async with new_worker(new_client, HttpOptionsWorkflow) as worker: + await new_client.execute_workflow( + HttpOptionsWorkflow.run, + args=["hi", {"api_version": "v1"}], + id=f"gemini-http-version-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert len(api_tracker.requests) == 1 + opts = api_tracker.requests[0].http_options_overrides + assert opts is not None + assert opts.api_version == "v1" + + +async def test_http_options_base_url_propagates(client: Client): + """base_url passed via http_options arrives at the activity.""" + new_client, api_tracker = apply_plugin(client, [make_text_response("ok")]) + + async with new_worker(new_client, HttpOptionsWorkflow) as worker: + await new_client.execute_workflow( + HttpOptionsWorkflow.run, + args=["hi", {"base_url": "https://custom.example.com"}], + id=f"gemini-http-base-url-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert len(api_tracker.requests) == 1 + opts = api_tracker.requests[0].http_options_overrides + assert opts is not None + assert opts.base_url == "https://custom.example.com" + + +async def test_http_options_multiple_fields_propagate(client: Client): + """Multiple http_options fields propagate together to the activity.""" + new_client, api_tracker = apply_plugin(client, [make_text_response("ok")]) + + async with new_worker(new_client, HttpOptionsWorkflow) as worker: + await new_client.execute_workflow( + HttpOptionsWorkflow.run, + args=[ + "hi", + { + "api_version": "v1beta", + "headers": {"X-Foo": "bar"}, + "base_url": "https://other.example.com", + }, + ], + id=f"gemini-http-multi-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert len(api_tracker.requests) == 1 + opts = api_tracker.requests[0].http_options_overrides + assert opts is not None + assert opts.api_version == "v1beta" + assert opts.headers == {"X-Foo": "bar"} + assert opts.base_url == "https://other.example.com" + + +async def test_no_http_options_passes_none(client: Client): + """When no per-request http_options are set, None reaches the activity.""" + new_client, api_tracker = apply_plugin(client, [make_text_response("ok")]) + + async with new_worker(new_client, SimpleGenerateWorkflow) as worker: + await new_client.execute_workflow( + SimpleGenerateWorkflow.run, + "hi", + id=f"gemini-http-none-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert len(api_tracker.requests) == 1 + assert api_tracker.requests[0].http_options_overrides is None + + +# =========================================================================== +# File upload/download tests +# =========================================================================== + + +async def test_file_upload_str_path(client: Client): + """Upload a file via str path dispatches through the activity.""" + new_client, api_tracker = apply_plugin(client, []) + + async with new_worker(new_client, FileUploadStrWorkflow) as worker: + result = await new_client.execute_workflow( + FileUploadStrWorkflow.run, + "/tmp/test.txt", + id=f"gemini-file-upload-str-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert len(api_tracker.file_upload_requests) == 1 + req = api_tracker.file_upload_requests[0] + assert req.file_path == "/tmp/test.txt" + assert req.file_bytes is None + assert req.config is not None + assert req.config.display_name == "Test File" + assert result == "files/test-uploaded-file" + + +async def test_file_upload_bytes(client: Client): + """Upload a file via io.BytesIO sends bytes through the activity.""" + new_client, api_tracker = apply_plugin(client, []) + + async with new_worker(new_client, FileUploadBytesWorkflow) as worker: + result = await new_client.execute_workflow( + FileUploadBytesWorkflow.run, + b"hello world", + id=f"gemini-file-upload-bytes-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert len(api_tracker.file_upload_requests) == 1 + req = api_tracker.file_upload_requests[0] + assert req.file_bytes == b"hello world" + assert req.file_path is None + assert req.config is not None + assert req.config.display_name == "Bytes File" + assert result == "files/test-uploaded-file" + + +async def test_file_download(client: Client): + """Download a file dispatches through the activity and returns bytes.""" + new_client, api_tracker = apply_plugin(client, []) + + async with new_worker(new_client, FileDownloadWorkflow) as worker: + result = await new_client.execute_workflow( + FileDownloadWorkflow.run, + "files/some-file", + id=f"gemini-file-download-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert len(api_tracker.file_download_requests) == 1 + assert api_tracker.file_download_requests[0].file == "files/some-file" + assert result == b"fake file content" + + +# =========================================================================== +# File search store upload tests +# =========================================================================== + + +async def test_file_search_store_upload(client: Client): + """Upload to file search store dispatches through the activity.""" + new_client, api_tracker = apply_plugin(client, []) + + async with new_worker(new_client, FileSearchStoreUploadWorkflow) as worker: + result = await new_client.execute_workflow( + FileSearchStoreUploadWorkflow.run, + args=["fileSearchStores/my-store", "/tmp/doc.txt"], + id=f"gemini-fss-upload-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert len(api_tracker.file_search_store_upload_requests) == 1 + req = api_tracker.file_search_store_upload_requests[0] + assert req.file_search_store_name == "fileSearchStores/my-store" + assert req.file_path == "/tmp/doc.txt" + assert req.config is not None + assert req.config.display_name == "Test Doc" + assert result == "operations/test-op" + + +# =========================================================================== +# Multi-turn chat tests +# =========================================================================== + + +async def test_chat_multi_turn(client: Client): + """Multi-turn chat sends multiple requests through the activity.""" + new_client, api_tracker = apply_plugin( + client, + [ + make_text_response("First answer"), + make_text_response("Second answer"), + ], + ) + + async with new_worker(new_client, ChatWorkflow) as worker: + result = await new_client.execute_workflow( + ChatWorkflow.run, + "Hello", + id=f"gemini-chat-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert len(api_tracker.requests) == 2 + assert result == ["First answer", "Second answer"] + + +class _FakeAsyncStream: + """Minimal stand-in for the SDK's AsyncStream in mocked-client tests.""" + + def __init__(self, events: list[Any]) -> None: + self._events = events + + def __aiter__(self) -> Any: + return self._gen() + + async def _gen(self) -> Any: + for event in self._events: + yield event + + async def __aenter__(self) -> "_FakeAsyncStream": + return self + + async def __aexit__(self, *_args: Any) -> None: + pass + + +# =========================================================================== +# Full integration test — real activities, mocked client +# =========================================================================== + + +def _apply_plugin_with_mock_client(client: Client, mock_responses: list[str]) -> Client: + """Create a real GoogleGenAIPlugin with real activities but a mocked client. + + Unlike ``apply_plugin``, this does NOT replace the activities. The + real ``GeminiApiCaller.activities()`` are registered, exercising the + full activity code path. The underlying ``genai.Client`` HTTP layer + and high-level file methods are mocked so no network calls are made. + """ + gemini = GeminiClient(api_key="fake-test-key") + + call_state = {"index": 0} + + async def fake_async_request(*_args: Any, **_kwargs: Any) -> SdkHttpResponse: + idx = call_state["index"] + call_state["index"] += 1 + if idx >= len(mock_responses): + raise RuntimeError( + f"No more mock responses (called {idx + 1} times, " + f"have {len(mock_responses)})" + ) + return SdkHttpResponse( + headers={"content-type": "application/json"}, + body=mock_responses[idx], + ) + + async def fake_async_request_streamed(*_args: Any, **_kwargs: Any) -> Any: + idx = call_state["index"] + call_state["index"] += 1 + if idx >= len(mock_responses): + raise RuntimeError( + f"No more mock responses (called {idx + 1} times, " + f"have {len(mock_responses)})" + ) + + async def _gen(): + yield SdkHttpResponse( + headers={"content-type": "application/json"}, + body=mock_responses[idx], + ) + + return _gen() + + gemini._api_client.async_request = fake_async_request # type: ignore[assignment] + gemini._api_client.async_request_streamed = fake_async_request_streamed # type: ignore[assignment] + + # Mock file operations at the high-level SDK interface (these are what + # the real activities call). + gemini.aio.files.upload = AsyncMock( # type: ignore[method-assign] + return_value=types.File( + name="files/mock-uploaded", + uri="https://fake.uri/files/mock-uploaded", + size_bytes=42, + ) + ) + gemini.aio.files.download = AsyncMock(return_value=b"mock download content") # type: ignore[method-assign] + gemini.aio.file_search_stores.upload_to_file_search_store = AsyncMock( # type: ignore[method-assign] + return_value=types.UploadToFileSearchStoreOperation.model_construct( + name="operations/mock-op" + ) + ) + + # Interactions and agents go through the vendored nextgen client (not + # BaseApiClient); inject a mock instance so the real activities exercise + # their code path without network access. + interaction = _deserialize(make_interaction_dict(), Interaction) + sse_events = [ + _deserialize(e, InteractionSSEEvent) for e in make_interaction_sse_events() + ] + + async def _interactions_create(*_args: Any, **kwargs: Any) -> Any: + if kwargs.get("stream"): + return _FakeAsyncStream(sse_events) + return interaction + + mock_interactions = MagicMock() + mock_interactions.create = _interactions_create + mock_agents = MagicMock() + mock_agents.create = AsyncMock(return_value=_deserialize(make_agent_dict(), Agent)) + # The interactions/agents resources are cached lazily on the async client; + # set them so the activities' client.aio.interactions/.agents calls hit the + # mocks instead of the network. + gemini.aio._interactions = mock_interactions # type: ignore[assignment] + gemini.aio._agents = mock_agents # type: ignore[assignment] + + plugin = GoogleGenAIPlugin(gemini) + config = client.config() + config["plugins"] = [plugin] + return Client(**config) + + +async def test_full_integration_with_mock_client(client: Client): + """Run a workflow through real activities with a mocked genai.Client. + + This is the only test that exercises the actual activity implementations + in _gemini_activity.py. Every other test uses the GeminiApiCallTracker + which replaces the activities entirely. + """ + # Mock responses are consumed in order by the async_request and + # async_request_streamed mocks. Steps 3-5 (file upload, download, + # store upload) are mocked separately at the SDK level and don't + # consume from this list. + new_client = _apply_plugin_with_mock_client( + client, + [ + make_text_response("Real activity response"), # generate_content + make_text_response("Streamed via real activity"), # generate_content_stream + make_text_response("Grounded RAG answer"), # RAG query with file_search + make_text_response(""), # file_search_stores.delete + ], + ) + + async with new_worker(new_client, FullIntegrationWorkflow) as worker: + result = await new_client.execute_workflow( + FullIntegrationWorkflow.run, + "test prompt", + id=f"gemini-full-integration-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=15), + ) + + assert result["generate"] == "Real activity response" + assert len(result["stream_chunks"]) > 0 + assert "Streamed" in " ".join(result["stream_chunks"]) + assert result["upload_name"] == "files/mock-uploaded" + assert result["download"] == "mock download content" + assert result["fss_upload_op"] == "operations/mock-op" + assert result["rag"] == "Grounded RAG answer" + assert result["store_deleted"] is True + assert result["interaction_id"] == INTERACTION_ID + assert result["interaction_events"] == [ + "interaction.created", + "step.delta", + "step.delta", + "interaction.completed", + ] + assert result["agent_id"] == "test-agent" + + +async def test_register_files_without_credentials_fails(client: Client): + """register_files raises when no credentials are available.""" + # _apply_plugin_with_mock_client uses api_key auth with no + # extra_credentials, so the activity should raise ValueError. + new_client = _apply_plugin_with_mock_client(client, []) + + async with new_worker(new_client, RegisterFilesWorkflow) as worker: + with pytest.raises(WorkflowFailureError) as exc_info: + await new_client.execute_workflow( + RegisterFilesWorkflow.run, + ["gs://bucket/file.txt"], + id=f"gemini-register-no-creds-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + # The error is nested: WorkflowFailureError → ActivityError → ApplicationError + cause = exc_info.value.cause + while cause.__cause__ is not None: + cause = cause.__cause__ + assert "No credentials available for register_files" in str(cause) + + +# =========================================================================== +# TemporalAsyncClient wiring tests +# =========================================================================== + + +def test_temporal_async_client_has_temporal_files(): + """TemporalAsyncClient() returns a client with TemporalAsyncFiles.""" + client = TemporalAsyncClient() + assert isinstance(client, TemporalAsyncClient) + assert isinstance(client.files, TemporalAsyncFiles) + + +def test_temporal_async_client_has_temporal_file_search_stores(): + """TemporalAsyncClient() returns a client with TemporalAsyncFileSearchStores.""" + client = TemporalAsyncClient() + assert isinstance(client.file_search_stores, TemporalAsyncFileSearchStores) + + +# =========================================================================== +# Unit tests for _TemporalApiClient +# =========================================================================== + + +def test_sync_request_raises(): + """Synchronous request() raises RuntimeError.""" + api_client = _TemporalApiClient() + with pytest.raises(RuntimeError, match="Synchronous requests are not supported"): + api_client.request("GET", "/test", {}) + + +def test_sync_request_streamed_raises(): + """Synchronous request_streamed() raises RuntimeError.""" + api_client = _TemporalApiClient() + with pytest.raises(RuntimeError, match="Synchronous streaming is not supported"): + api_client.request_streamed("GET", "/test", {}) + + +def test_upload_file_raises(): + """Low-level upload_file() raises NotImplementedError.""" + api_client = _TemporalApiClient() + with pytest.raises(NotImplementedError, match="client.files.upload"): + api_client.upload_file() + + +def test_download_file_raises(): + """Low-level download_file() raises NotImplementedError.""" + api_client = _TemporalApiClient() + with pytest.raises(NotImplementedError, match="client.files.download"): + api_client.download_file() + + +# =========================================================================== +# Unit tests for activity_as_tool +# =========================================================================== + + +def test_activity_as_tool_bare_function_raises(): + """activity_as_tool rejects a function without @activity.defn.""" + + async def not_an_activity(x: str) -> str: + return x + + with pytest.raises(GoogleGenAIError, match="@activity.defn"): + activity_as_tool(not_an_activity) + + +def test_activity_as_tool_preserves_name(): + """Returned wrapper keeps the original function name.""" + wrapper = activity_as_tool(ToolCallTracker.get_weather) + assert wrapper.__name__ == "get_weather" + + +def test_activity_as_tool_preserves_doc(): + """Returned wrapper keeps the original docstring.""" + wrapper = activity_as_tool(ToolCallTracker.get_weather) + assert wrapper.__doc__ == "Get the weather for a given city." + + +def test_activity_as_tool_preserves_signature(): + """Returned wrapper has the correct parameter signature (self hidden).""" + wrapper = activity_as_tool(ToolCallTracker.get_weather) + sig = inspect.signature(wrapper) + params = list(sig.parameters.keys()) + assert params == ["city"] + + +def test_activity_as_tool_multi_arg_signature(): + """Multi-arg activity preserves all parameter names (self hidden).""" + wrapper = activity_as_tool(ToolCallTracker.get_weather_country) + sig = inspect.signature(wrapper) + params = list(sig.parameters.keys()) + assert params == ["city", "country"] + + +def test_activity_as_tool_is_async_callable(): + """Returned wrapper is an async callable.""" + wrapper = activity_as_tool(ToolCallTracker.get_weather) + assert inspect.iscoroutinefunction(wrapper) + + +# =========================================================================== +# Bound-method tool wrapping - shields workflow-method tools from google-genai's +# internal config deep-copy (>= 2.8.0), which would otherwise clone the workflow +# instance and drop in-workflow state mutations. Version-independent unit tests. +# =========================================================================== + + +class _StatefulTool: + def __init__(self) -> None: + self.calls: list[str] = [] + + async def lookup_city(self, city: str) -> str: + """Look up info about a city.""" + self.calls.append(city) + return f"{city} is great" + + +async def test_closure_if_bound_method_unbinds_and_mutates_original(): + """A bound method becomes a plain function that still mutates the real instance.""" + obj = _StatefulTool() + wrapped = cast(Callable[..., Any], _closure_if_bound_method(obj.lookup_city)) + + # No longer a bound method (so deepcopy leaves it — and its captured self — + # intact), but name/doc/signature are preserved for AFC schema building. + assert not inspect.ismethod(wrapped) + assert wrapped.__name__ == "lookup_city" + assert wrapped.__doc__ == "Look up info about a city." + assert list(inspect.signature(wrapped).parameters) == ["city"] + + assert await wrapped("Berlin") == "Berlin is great" + assert obj.calls == ["Berlin"] + + +def test_closure_survives_deepcopy_bound_method_does_not(): + """The wrapper keeps its instance across a deep-copy; a raw bound method clones it.""" + import copy + + obj = _StatefulTool() + + # Raw bound method: deepcopy clones __self__ (the 2.8.0 failure mode). + copied_method = copy.deepcopy(obj.lookup_city) + assert inspect.ismethod(copied_method) + assert copied_method.__self__ is not obj + + # Closure: deepcopy is a no-op, so the captured instance is preserved. + wrapped = _closure_if_bound_method(obj.lookup_city) + assert copy.deepcopy(wrapped) is wrapped + + +def test_closure_if_bound_method_passes_through_non_methods(): + """Plain functions and activity_as_tool wrappers are left untouched.""" + + def plain(city: str) -> str: + return city + + assert _closure_if_bound_method(plain) is plain + activity_tool = activity_as_tool(ToolCallTracker.get_weather) + assert _closure_if_bound_method(activity_tool) is activity_tool + + +def test_wrap_bound_method_tools_config_forms(): + """Config tools are wrapped for both model and dict configs, without mutating the caller.""" + obj = _StatefulTool() + + # Model config: bound method wrapped, caller's config left as-is. + config = types.GenerateContentConfig(tools=[obj.lookup_city]) + wrapped = _wrap_bound_method_tools(config) + assert isinstance(wrapped, types.GenerateContentConfig) + assert wrapped is not config + assert config.tools == [obj.lookup_city] # original untouched + assert wrapped.tools is not None + assert not inspect.ismethod(wrapped.tools[0]) + + # Dict config: same, and the original dict/list are not mutated. + dict_config: types.GenerateContentConfigDict = {"tools": [obj.lookup_city]} + wrapped_dict = _wrap_bound_method_tools(dict_config) + assert isinstance(wrapped_dict, dict) + assert dict_config.get("tools") == [obj.lookup_city] + wrapped_tools = wrapped_dict.get("tools") + assert wrapped_tools is not None + assert not inspect.ismethod(wrapped_tools[0]) + + # No bound methods -> returned unchanged (no needless copy). + def plain(city: str) -> str: + return city + + plain_config = types.GenerateContentConfig(tools=[plain]) + assert _wrap_bound_method_tools(plain_config) is plain_config + assert _wrap_bound_method_tools(None) is None + + +# =========================================================================== +# Unit tests for TemporalAsyncClient +# =========================================================================== + + +def test_temporal_async_client_vertexai_config(): + """TemporalAsyncClient() forwards Vertex AI configuration to the _TemporalApiClient.""" + result = TemporalAsyncClient(vertexai=True, project="proj", location="us-central1") + assert result._api_client.vertexai is True + assert result._api_client.project == "proj" + assert result._api_client.location == "us-central1" + + +# =========================================================================== +# Unit tests for io.IOBase text-stream rejection +# =========================================================================== + + +async def test_file_upload_text_stream_raises(): + """TemporalAsyncFiles.upload rejects text streams with a clear TypeError.""" + files = TemporalAsyncFiles(_TemporalApiClient()) + with pytest.raises( + TypeError, match="file must be a binary stream when passing an io.IOBase" + ): + await files.upload(file=io.StringIO("text")) + + +async def test_file_search_store_upload_text_stream_raises(): + """TemporalAsyncFileSearchStores.upload_to_file_search_store rejects text streams.""" + stores = TemporalAsyncFileSearchStores(_TemporalApiClient()) + with pytest.raises( + TypeError, match="file must be a binary stream when passing an io.IOBase" + ): + await stores.upload_to_file_search_store( + file_search_store_name="fileSearchStores/x", + file=io.StringIO("text"), + ) + + +# =========================================================================== +# Interactions API tests +# =========================================================================== + + +async def test_interaction_create(client: Client): + """Non-streaming interactions.create returns a typed Interaction.""" + new_client, tracker = apply_plugin(client, []) + + async with new_worker(new_client, InteractionCreateWorkflow) as worker: + result = await new_client.execute_workflow( + InteractionCreateWorkflow.run, + "What's an interaction?", + id=f"gemini-interaction-create-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert result == {"id": INTERACTION_ID, "status": "completed"} + assert len(tracker.interaction_requests) == 1 + params = tracker.interaction_requests[0].params + assert params["model"] == "gemini-2.5-flash" + assert params["input"] == "What's an interaction?" + # stream selects the activity; timeout maps to start_to_close_timeout. + assert "stream" not in params + assert "timeout" not in params + + +async def test_interaction_create_stream(client: Client): + """Streamed interactions.create yields typed events in order.""" + new_client, tracker = apply_plugin(client, []) + + async with new_worker(new_client, InteractionStreamWorkflow) as worker: + result = await new_client.execute_workflow( + InteractionStreamWorkflow.run, + "Stream me", + id=f"gemini-interaction-stream-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert result == [ + "interaction.created", + "step.delta", + "step.delta", + "interaction.completed", + ] + assert len(tracker.interaction_requests) == 1 + assert "stream" not in tracker.interaction_requests[0].params + + +async def test_interaction_lifecycle(client: Client): + """interactions.get/cancel/delete forward the interaction id.""" + new_client, tracker = apply_plugin(client, []) + + async with new_worker(new_client, InteractionLifecycleWorkflow) as worker: + result = await new_client.execute_workflow( + InteractionLifecycleWorkflow.run, + "interactions/abc", + id=f"gemini-interaction-lifecycle-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert result["got_id"] == INTERACTION_ID + assert result["cancel_status"] == "cancelled" + assert result["deleted"] == {"deleted": True} + assert [r.id for r in tracker.interaction_id_requests] == ["interactions/abc"] * 3 + + +async def test_agents_crud(client: Client): + """agents.create/get/list/delete round-trip through activities.""" + new_client, tracker = apply_plugin(client, []) + + async with new_worker(new_client, AgentsWorkflow) as worker: + result = await new_client.execute_workflow( + AgentsWorkflow.run, + id=f"gemini-agents-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert result["created_id"] == "test-agent" + assert result["got_id"] == "test-agent" + assert result["listed_ids"] == ["test-agent"] + assert result["next_page_token"] == "next-tok" + assert result["delete_response"]["id"] == "test-agent" + # create + list went through the no-id request path + assert [r.params.get("page_size") for r in tracker.interaction_requests] == [ + None, + 10, + ] + + +async def test_webhooks_unsupported(client: Client): + """client.webhooks raises a clear error inside a workflow.""" + new_client, _ = apply_plugin(client, []) + + async with new_worker(new_client, WebhooksUnsupportedWorkflow) as worker: + result = await new_client.execute_workflow( + WebhooksUnsupportedWorkflow.run, + id=f"gemini-webhooks-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert "client.webhooks is not supported in Temporal workflows" in result + + +# =========================================================================== +# Unit tests for interactions helpers +# =========================================================================== + + +def test_pop_timeout_maps_to_activity_config(): + """A numeric timeout kwarg becomes the activity start_to_close_timeout.""" + from temporalio.contrib.google_genai._temporal_interactions import _pop_timeout + + config = ActivityConfig() + params: dict[str, Any] = {"model": "gemini-2.5-flash", "timeout": 120} + _pop_timeout(params, config) + assert "timeout" not in params + assert config.get("start_to_close_timeout") == timedelta(seconds=120) + + +def test_pop_timeout_rejects_non_numeric(): + """Non-numeric timeouts (e.g. httpx.Timeout) are rejected.""" + import httpx + + from temporalio.contrib.google_genai._temporal_interactions import _pop_timeout + + with pytest.raises(ValueError, match="timeout must be numeric seconds"): + _pop_timeout({"timeout": httpx.Timeout(5.0)}, ActivityConfig()) + + +# =========================================================================== +# Retry handling + error classification (Temporal owns retries) +# =========================================================================== + + +def test_plugin_rejects_client_retry_options(): + """A genai.Client with retry_options is rejected at plugin construction.""" + from google.genai.types import HttpOptions, HttpRetryOptions + + from temporalio.contrib.google_genai._google_genai_plugin import ( + _reject_sdk_retries, + ) + + client = GeminiClient( + api_key="fake-test-key", + http_options=HttpOptions(retry_options=HttpRetryOptions(attempts=3)), + ) + with pytest.raises(ValueError, match="retry_options"): + _reject_sdk_retries(client) + + +def test_plugin_allows_no_retry_options(): + """A default client (no retry_options) passes the retry check.""" + from temporalio.contrib.google_genai._google_genai_plugin import ( + _reject_sdk_retries, + ) + + _reject_sdk_retries(GeminiClient(api_key="fake-test-key")) + + +def test_process_http_options_rejects_retry_options(): + """Per-request http_options.retry_options raises in the workflow.""" + from google.genai.types import HttpOptions, HttpRetryOptions + + from temporalio.contrib.google_genai._temporal_api_client import _TemporalApiClient + + with pytest.raises(GoogleGenAIError, match="retry_options"): + _TemporalApiClient._process_http_options( + HttpOptions(retry_options=HttpRetryOptions(attempts=2)), + ActivityConfig(), + ) + + +def test_classify_api_error_retryable_vs_non_retryable(): + """Transient HTTP statuses stay retryable; client errors are non-retryable.""" + from google.genai import errors + + from temporalio.contrib.google_genai._gemini_activity import _classify_api_error + + client_err = errors.ClientError.__new__(errors.ClientError) + client_err.code = 400 + classified = _classify_api_error(client_err) + assert classified.non_retryable is True + assert classified.type == "ClientError" + + server_err = errors.ServerError.__new__(errors.ServerError) + server_err.code = 503 + assert _classify_api_error(server_err).non_retryable is False + + +def test_google_genai_error_is_application_error(): + """GoogleGenAIError is an ApplicationError so existing handling still works.""" + assert issubclass(GoogleGenAIError, ApplicationError) + + +# =========================================================================== +# Replay determinism + side-effect (activity scheduling) tests +# =========================================================================== + + +def _replay_plugin() -> GoogleGenAIPlugin: + """Build a real plugin instance for the Replayer. + + Replay never executes activities, so a fake-key client is sufficient. + What matters is that the Replayer uses the plugin's data converter, + sandbox passthrough, and workflow runner — the same configuration that + runs in production — so a history recorded by the plugin replays under + it without nondeterminism. + """ + return GoogleGenAIPlugin(GeminiClient(api_key="fake-test-key")) + + +async def test_replay_simple_generate(client: Client): + """A recorded simple generate_content history replays deterministically.""" + new_client, _ = apply_plugin(client, [make_text_response("Hello from Gemini!")]) + + async with new_worker(new_client, SimpleGenerateWorkflow) as worker: + handle = await new_client.start_workflow( + SimpleGenerateWorkflow.run, + "Say hello", + id=f"gemini-replay-simple-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + await handle.result() + history = await handle.fetch_history() + + await Replayer( + workflows=[SimpleGenerateWorkflow], + plugins=[_replay_plugin()], + ).replay_workflow(history) + + +async def test_replay_tool_loop(client: Client): + """The in-workflow AFC tool loop replays deterministically. + + The Gemini SDK's automatic-function-calling loop runs inside the + workflow, interleaving multiple activity calls with SDK-side request + formatting. That makes it the replay path most likely to surface + nondeterminism, so the recorded history is replayed under the real + plugin to prove the loop is replay-safe. + """ + tool_tracker = ToolCallTracker() + new_client, _ = apply_plugin( + client, + [ + make_function_call_response("get_weather", {"city": "Tokyo"}), + make_function_call_response( + "get_weather_country", {"city": "Paris", "country": "France"} + ), + make_text_response("Tokyo is sunny; Paris is rainy."), + ], + ) + + async with new_worker( + new_client, + MultipleToolsWorkflow, + activities=[tool_tracker.get_weather, tool_tracker.get_weather_country], + ) as worker: + handle = await new_client.start_workflow( + MultipleToolsWorkflow.run, + "Compare Tokyo and Paris weather", + id=f"gemini-replay-tools-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=15), + ) + await handle.result() + history = await handle.fetch_history() + + await Replayer( + workflows=[MultipleToolsWorkflow], + plugins=[_replay_plugin()], + ).replay_workflow(history) + + +async def test_side_effects_activity_scheduling(client: Client): + """Each Gemini API call and tool call schedules exactly one activity. + + Runs with ``max_cached_workflows=0`` so the workflow is evicted and + replayed from history between tasks — any nondeterminism in the + in-workflow AFC loop would fail the run — then asserts the exact number + of ``ActivityTaskScheduled`` events per activity type. The multi-tool + workflow issues three generate_content calls (initial + one per tool + result) and one activity per tool invocation. + """ + tool_tracker = ToolCallTracker() + new_client, _ = apply_plugin( + client, + [ + make_function_call_response("get_weather", {"city": "Tokyo"}), + make_function_call_response( + "get_weather_country", {"city": "Paris", "country": "France"} + ), + make_text_response("Tokyo is sunny; Paris is rainy."), + ], + ) + + async with new_worker( + new_client, + MultipleToolsWorkflow, + activities=[tool_tracker.get_weather, tool_tracker.get_weather_country], + max_cached_workflows=0, + ) as worker: + handle = await new_client.start_workflow( + MultipleToolsWorkflow.run, + "Compare Tokyo and Paris weather", + id=f"gemini-side-effects-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=15), + ) + await handle.result() + + scheduled: dict[str, int] = {} + async for e in handle.fetch_history_events(): + if e.HasField("activity_task_scheduled_event_attributes"): + name = e.activity_task_scheduled_event_attributes.activity_type.name + scheduled[name] = scheduled.get(name, 0) + 1 + + assert scheduled == { + "gemini_api_client_async_request": 3, + "get_weather": 1, + "get_weather_country": 1, + } diff --git a/tests/contrib/google_genai/test_gemini_mcp.py b/tests/contrib/google_genai/test_gemini_mcp.py new file mode 100644 index 000000000..04e36bb9d --- /dev/null +++ b/tests/contrib/google_genai/test_gemini_mcp.py @@ -0,0 +1,440 @@ +"""MCP integration tests for the Google Gemini SDK Temporal integration. + +Covers the client-side ``McpClientSession`` path (Gemini Developer API) routed +through ``TemporalMcpClientSession``: +- tool discovery + call through a real stdio MCP server on the worker +- worker-side connection pooling and idle eviction +- ``cache_tools`` listing frequency +- full parameter-schema propagation to the model (the MCP wire-format check) +- replay determinism and exact activity-scheduling counts + +Plus the server-side pass-through paths that need no shim code: +- Vertex AI ``Tool(mcp_servers=[McpServer(...)])`` config serialization +- Interactions API ``MCPServerToolCallStep`` / ``MCPServerToolResultStep`` rehydration +""" + +from __future__ import annotations + +import json +import sys +from collections.abc import AsyncIterator +from contextlib import AbstractAsyncContextManager, asynccontextmanager +from datetime import timedelta +from pathlib import Path +from typing import Any +from uuid import uuid4 + +import pytest +from google.genai import types +from mcp import ClientSession, StdioServerParameters +from mcp.client.stdio import stdio_client + +from temporalio import workflow +from temporalio.client import Client +from temporalio.contrib.google_genai import ( + GoogleGenAIPlugin, + TemporalAsyncClient, + TemporalMcpClientSession, +) +from temporalio.contrib.google_genai._temporal_interactions import _deserialize +from temporalio.contrib.google_genai.testing import ( + GeminiTestServer, + function_call_response, + text_response, +) +from temporalio.worker import Replayer +from temporalio.workflow import ActivityConfig +from tests.contrib.google_genai.test_gemini import ( + GeminiApiCallTracker, + make_function_call_response, + make_text_response, +) +from tests.helpers import new_worker + +_ECHO_SERVER = str(Path(__file__).parent / "echo_mcp_server.py") + + +@asynccontextmanager +async def _echo_session() -> AsyncIterator[ClientSession]: + """Yield a connected, initialized session to the stdio echo MCP server.""" + params = StdioServerParameters(command=sys.executable, args=[_ECHO_SERVER]) + async with stdio_client(params) as (read, write): + async with ClientSession(read, write) as session: + await session.initialize() + yield session + + +class _CountingFactory: + """Wraps the echo factory to count how often a connection is opened.""" + + def __init__(self) -> None: + self.opens = 0 + + def __call__(self) -> AbstractAsyncContextManager[ClientSession]: + self.opens += 1 + return _echo_session() + + +def _apply_mcp_plugin( + client: Client, + mock_responses: list[str], + mcp_servers: dict, + mcp_connection_idle_timeout: timedelta | None = None, +) -> tuple[Client, GeminiApiCallTracker]: + """Build a plugin whose API activities are faked but MCP activities are real. + + Monkeypatches ``GeminiApiCaller.activities`` (so canned generate_content + responses drive the AFC loop) while leaving the plugin's MCP activities — + built from ``mcp_servers`` — to hit the real stdio echo server. + """ + from temporalio.contrib.google_genai._gemini_activity import GeminiApiCaller + + tracker = GeminiApiCallTracker(mock_responses) + original = GeminiApiCaller.activities + GeminiApiCaller.activities = lambda self: [ # type: ignore[method-assign] + tracker.gemini_api_client_async_request, + tracker.gemini_api_client_async_request_streamed, + ] + try: + from google.genai import Client as GeminiClient + + plugin = GoogleGenAIPlugin( + GeminiClient(api_key="fake-test-key"), + mcp_servers=mcp_servers, + mcp_connection_idle_timeout=mcp_connection_idle_timeout, + ) + finally: + GeminiApiCaller.activities = original # type: ignore[method-assign] + + config = client.config() + config["plugins"] = [plugin] + return Client(**config), tracker + + +def _replay_plugin(mcp_servers: dict) -> GoogleGenAIPlugin: + from google.genai import Client as GeminiClient + + return GoogleGenAIPlugin( + GeminiClient(api_key="fake-test-key"), mcp_servers=mcp_servers + ) + + +async def _activity_names(handle: Any) -> list[str]: + names: list[str] = [] + async for e in handle.fetch_history_events(): + if e.HasField("activity_task_scheduled_event_attributes"): + names.append(e.activity_task_scheduled_event_attributes.activity_type.name) + return names + + +@pytest.fixture(autouse=True) +def _clear_mcp_connections(): # pyright: ignore[reportUnusedFunction] + """Isolate the module-global MCP connection pool between tests.""" + from temporalio.contrib.google_genai import _mcp + + _mcp._CONNECTIONS.clear() + yield + _mcp._CONNECTIONS.clear() + + +# --------------------------------------------------------------------------- +# Workflow +# --------------------------------------------------------------------------- + + +@workflow.defn +class McpToolWorkflow: + """generate_content grounded by an MCP tool, via the AFC loop. + + Takes the MCP server name as an argument so each test can use a distinct + name (the worker-side connection pool is keyed by name and shared across + activity invocations in the worker process). The number of tool calls is + driven entirely by the mocked model responses, not the workflow. + """ + + @workflow.run + async def run(self, server_name: str, prompt: str) -> str: + client = TemporalAsyncClient() + session = TemporalMcpClientSession( + server_name, + cache_tools=True, + activity_config=ActivityConfig( + start_to_close_timeout=timedelta(seconds=30) + ), + ) + response = await client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + config=types.GenerateContentConfig(tools=[session]), + ) + return response.text or "" + + +# --------------------------------------------------------------------------- +# Client-side MCP tests +# --------------------------------------------------------------------------- + + +async def test_mcp_tool_discovery_and_call(client: Client): + """The AFC loop discovers + calls an MCP tool through activities.""" + server = "echo_basic" + new_client, _ = _apply_mcp_plugin( + client, + [ + make_function_call_response("echo", {"message": "hello"}), + make_text_response("Done!"), + ], + mcp_servers={server: _echo_session}, + ) + + async with new_worker(new_client, McpToolWorkflow) as worker: + handle = await new_client.start_workflow( + McpToolWorkflow.run, + args=[server, "echo hello"], + id=f"gemini-mcp-{uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=30), + ) + result = await handle.result() + names = await _activity_names(handle) + + assert result == "Done!" + assert names == [ + f"{server}-list-tools", + "gemini_api_client_async_request", + f"{server}-call-tool", + "gemini_api_client_async_request", + ] + + +async def test_mcp_connection_pooling(client: Client): + """Two tool calls in one workflow reuse a single worker-side connection.""" + server = "echo_pool" + factory = _CountingFactory() + new_client, _ = _apply_mcp_plugin( + client, + [ + make_function_call_response("echo", {"message": "one"}), + make_function_call_response("echo", {"message": "two"}), + make_text_response("Done!"), + ], + mcp_servers={server: factory}, + ) + + async with new_worker(new_client, McpToolWorkflow) as worker: + handle = await new_client.start_workflow( + McpToolWorkflow.run, + args=[server, "echo twice"], + id=f"gemini-mcp-pool-{uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=30), + ) + assert await handle.result() == "Done!" + names = await _activity_names(handle) + + # list-tools + two call-tools all served by one lazily-opened connection. + assert names.count(f"{server}-call-tool") == 2 + assert factory.opens == 1 + + +async def test_mcp_full_schema_propagation(client: Client): + """The model receives the MCP tool's full parameter schema, not just name.""" + server = "echo_schema" + new_client, tracker = _apply_mcp_plugin( + client, + [ + make_function_call_response("echo", {"message": "hi"}), + make_text_response("Done!"), + ], + mcp_servers={server: _echo_session}, + ) + + async with new_worker(new_client, McpToolWorkflow) as worker: + await new_client.execute_workflow( + McpToolWorkflow.run, + args=[server, "echo hi"], + id=f"gemini-mcp-schema-{uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=30), + ) + + # The first generate request carries the tool declarations the SDK built + # from the MCP list_tools result. + first = tracker.requests[0].request_dict + decls = first["tools"][0]["functionDeclarations"] # type: ignore[index] + echo_decl = next(d for d in decls if d["name"] == "echo") + assert "parameters" in echo_decl + assert "message" in echo_decl["parameters"]["properties"] + + +async def test_mcp_replay(client: Client): + """A recorded MCP tool-loop history replays deterministically.""" + server = "echo_replay" + new_client, _ = _apply_mcp_plugin( + client, + [ + make_function_call_response("echo", {"message": "hello"}), + make_text_response("Done!"), + ], + mcp_servers={server: _echo_session}, + ) + + async with new_worker(new_client, McpToolWorkflow) as worker: + handle = await new_client.start_workflow( + McpToolWorkflow.run, + args=[server, "echo hello"], + id=f"gemini-mcp-replay-{uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=30), + ) + await handle.result() + history = await handle.fetch_history() + + await Replayer( + workflows=[McpToolWorkflow], + plugins=[_replay_plugin({server: _echo_session})], + ).replay_workflow(history) + + +async def test_mcp_side_effects(client: Client): + """max_cached_workflows=0: exact ActivityTaskScheduled counts per type.""" + server = "echo_side" + new_client, _ = _apply_mcp_plugin( + client, + [ + make_function_call_response("echo", {"message": "hello"}), + make_text_response("Done!"), + ], + mcp_servers={server: _echo_session}, + ) + + async with new_worker( + new_client, McpToolWorkflow, max_cached_workflows=0 + ) as worker: + handle = await new_client.start_workflow( + McpToolWorkflow.run, + args=[server, "echo hello"], + id=f"gemini-mcp-side-effects-{uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=30), + ) + await handle.result() + names = await _activity_names(handle) + + scheduled: dict[str, int] = {} + for n in names: + scheduled[n] = scheduled.get(n, 0) + 1 + assert scheduled == { + f"{server}-list-tools": 1, + "gemini_api_client_async_request": 2, + f"{server}-call-tool": 1, + } + + +async def test_mcp_via_gemini_test_server(client: Client): + """GeminiTestServer.plugin(mcp_servers=...) scripts the model, runs MCP for real. + + This is the public testing path — the tests above reach into + ``GeminiApiCaller`` to also count API calls, but a user testing an + MCP-grounded workflow should need nothing but ``GeminiTestServer``. + """ + server = "echo_public" + test_server = GeminiTestServer( + [ + function_call_response("echo", {"message": "durable execution"}), + text_response("The echo tool returned: durable execution"), + ] + ) + + config = client.config() + config["plugins"] = [test_server.plugin(mcp_servers={server: _echo_session})] + new_client = Client(**config) + + async with new_worker(new_client, McpToolWorkflow) as worker: + handle = await new_client.start_workflow( + McpToolWorkflow.run, + args=[server, "echo the phrase: durable execution"], + id=f"gemini-mcp-public-{uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=30), + ) + result = await handle.result() + names = await _activity_names(handle) + + assert result == "The echo tool returned: durable execution" + # The MCP activities really ran; only the model HTTP layer was scripted. + assert names == [ + f"{server}-list-tools", + "gemini_api_client_async_request", + f"{server}-call-tool", + "gemini_api_client_async_request", + ] + # The echoed message reached the model as a function response. + assert any( + "durable execution" in json.dumps(request) + for request in test_server.requests[1:] + ) + + +# --------------------------------------------------------------------------- +# Server-side pass-through tests (no shim code) +# --------------------------------------------------------------------------- + + +def test_vertex_mcp_server_config_serializes(): + """Vertex server-side MCP config round-trips as plain request data.""" + tool = types.Tool( + mcp_servers=[ + types.McpServer( + name="weather", + streamable_http_transport=types.StreamableHttpTransport( + url="https://example.com/mcp", + ), + ) + ] + ) + config = types.GenerateContentConfig(tools=[tool]) + dumped = config.model_dump(mode="json", exclude_none=True) + server = dumped["tools"][0]["mcp_servers"][0] + assert server["name"] == "weather" + assert server["streamable_http_transport"]["url"] == "https://example.com/mcp" + + +def test_interactions_mcp_steps_rehydrate(): + """Interactions API MCP step payloads rehydrate via _deserialize.""" + from google.genai.interactions import InteractionSSEEvent + + call_event: Any = _deserialize( + { + "event_type": "step.start", + "index": 0, + "step": { + "type": "mcp_server_tool_call", + "id": "call-1", + "name": "lookup", + "server_name": "weather", + "arguments": {"city": "Tokyo"}, + }, + }, + InteractionSSEEvent, + ) + assert call_event.step.type == "mcp_server_tool_call" + assert call_event.step.server_name == "weather" + assert call_event.step.arguments == {"city": "Tokyo"} + + result_event: Any = _deserialize( + { + "event_type": "step.start", + "index": 0, + "step": { + "type": "mcp_server_tool_result", + "call_id": "call-1", + "name": "lookup", + "server_name": "weather", + "result": "sunny", + }, + }, + InteractionSSEEvent, + ) + assert result_event.step.type == "mcp_server_tool_result" + assert result_event.step.call_id == "call-1" diff --git a/tests/contrib/google_genai/test_gemini_streaming.py b/tests/contrib/google_genai/test_gemini_streaming.py new file mode 100644 index 000000000..e7bee8747 --- /dev/null +++ b/tests/contrib/google_genai/test_gemini_streaming.py @@ -0,0 +1,122 @@ +"""Streaming tests for the Google Gemini SDK Temporal integration. + +Covers ``generate_content_stream`` publishing each chunk to a +:class:`~temporalio.contrib.workflow_streams.WorkflowStream` topic for external +consumers, and the fail-fast when ``streaming_topic`` is set without a hosted +``WorkflowStream``. +""" + +from __future__ import annotations + +import uuid +from datetime import timedelta + +import pytest +from google.genai import types + +from temporalio import workflow +from temporalio.client import Client, WorkflowFailureError +from temporalio.contrib.google_genai import TemporalAsyncClient +from temporalio.contrib.google_genai.testing import GeminiTestServer, text_response +from temporalio.contrib.workflow_streams import WorkflowStream, WorkflowStreamClient +from tests.helpers import new_worker + + +@workflow.defn +class StreamingWorkflowStreamWorkflow: + """Streams generate_content_stream chunks to a WorkflowStream topic. + + Holds the run open on a ``finish`` signal so an external subscriber can + reliably consume the published chunk before the workflow completes. + """ + + @workflow.init + def __init__(self, prompt: str) -> None: + self.stream = WorkflowStream() + self._done = False + + @workflow.run + async def run(self, prompt: str) -> str: + client = TemporalAsyncClient(streaming_topic="gemini") + out: list[str] = [] + async for chunk in await client.models.generate_content_stream( + model="gemini-2.5-flash", + contents=prompt, + ): + out.append(chunk.text or "") + await workflow.wait_condition(lambda: self._done) + return "".join(out) + + @workflow.signal + def finish(self) -> None: + self._done = True + + +@workflow.defn +class StreamingNoStreamWorkflow: + """Sets streaming_topic but hosts no WorkflowStream — must fail fast.""" + + @workflow.run + async def run(self, prompt: str) -> str: + client = TemporalAsyncClient(streaming_topic="gemini") + async for _ in await client.models.generate_content_stream( + model="gemini-2.5-flash", + contents=prompt, + ): + pass + return "done" + + +async def test_streaming_publishes_to_workflow_stream(client: Client): + """Streamed chunks are published to the WorkflowStream for external consumers.""" + server = GeminiTestServer([text_response("Hello from Gemini stream")]) + config = client.config() + config["plugins"] = [server.plugin()] + new_client = Client(**config) + + async with new_worker(new_client, StreamingWorkflowStreamWorkflow) as worker: + wf_id = f"gemini-stream-{uuid.uuid4()}" + handle = await new_client.start_workflow( + StreamingWorkflowStreamWorkflow.run, + "say hi", + id=wf_id, + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=15), + ) + + stream = WorkflowStreamClient.create(new_client, wf_id) + received: list[types.GenerateContentResponse] = [] + async for item in stream.subscribe( + ["gemini"], + result_type=types.GenerateContentResponse, + poll_cooldown=timedelta(milliseconds=20), + ): + received.append(item.data) + break # one scripted chunk + + await handle.signal(StreamingWorkflowStreamWorkflow.finish) + result = await handle.result() + + assert result == "Hello from Gemini stream" + assert len(received) == 1 + assert received[0].text == "Hello from Gemini stream" + + +async def test_streaming_without_workflow_stream_raises(client: Client): + """streaming_topic set but no WorkflowStream hosted fails the workflow.""" + server = GeminiTestServer([text_response("unused")]) + config = client.config() + config["plugins"] = [server.plugin()] + new_client = Client(**config) + + async with new_worker(new_client, StreamingNoStreamWorkflow) as worker: + with pytest.raises(WorkflowFailureError) as exc_info: + await new_client.execute_workflow( + StreamingNoStreamWorkflow.run, + "hi", + id=f"gemini-stream-nostream-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + + assert "WorkflowStream" in str(exc_info.value.cause) diff --git a/tests/contrib/langgraph/test_summary_fn.py b/tests/contrib/langgraph/test_summary_fn.py new file mode 100644 index 000000000..687a68edb --- /dev/null +++ b/tests/contrib/langgraph/test_summary_fn.py @@ -0,0 +1,362 @@ +"""Tests for node/task summaries (static summary and summary_fn).""" + +from __future__ import annotations + +import uuid +from datetime import timedelta +from typing import Any, Callable + +import pytest +from langchain_core.runnables import ( + RunnableConfig, # pyright: ignore[reportMissingTypeStubs] +) +from langgraph.graph import START, StateGraph # pyright: ignore[reportMissingTypeStubs] +from typing_extensions import TypedDict + +import temporalio.api.sdk.v1 +from temporalio import workflow +from temporalio.client import Client +from temporalio.contrib.langgraph import LangGraphPlugin, graph +from temporalio.testing import WorkflowEnvironment +from temporalio.worker import Replayer, Worker +from tests.helpers import assert_eq_eventually + +SummaryFn = Callable[[tuple[Any, ...], dict[str, Any]], "str | None"] + + +class State(TypedDict): + value: str + + +async def passthrough(state: State) -> dict[str, str]: + return {"value": state["value"]} + + +def summarize( + args: tuple[Any, ...], + kwargs: dict[str, Any], # pyright: ignore[reportUnusedParameter] +) -> str | None: + return f"value={args[0]['value']}" + + +@workflow.defn +class SummaryWorkflow: + def __init__(self) -> None: + self.app = graph("summary-graph").compile() + + @workflow.run + async def run(self, input: str) -> Any: + return await self.app.ainvoke({"value": input}) + + +def _activity_graph( + summary_fn: SummaryFn | None, +) -> StateGraph[State, None, State, State]: + metadata: dict[str, Any] = {"execute_in": "activity"} + if summary_fn is not None: + metadata["summary_fn"] = summary_fn + g: StateGraph[State, None, State, State] = StateGraph(State) + g.add_node("node", passthrough, metadata=metadata) + g.add_edge(START, "node") + return g + + +async def _run_and_collect_summaries( + client: Client, plugin: LangGraphPlugin, input: str +) -> list[bytes]: + task_queue = f"summary-{uuid.uuid4()}" + async with Worker( + client, + task_queue=task_queue, + workflows=[SummaryWorkflow], + plugins=[plugin], + max_cached_workflows=0, + ): + handle = await client.start_workflow( + SummaryWorkflow.run, + input, + id=f"summary-{uuid.uuid4()}", + task_queue=task_queue, + ) + await handle.result() + return [ + e.user_metadata.summary.data + async for e in handle.fetch_history_events() + if e.HasField("activity_task_scheduled_event_attributes") + ] + + +def _plugin(g: StateGraph[Any, Any, Any, Any], **kwargs: Any) -> LangGraphPlugin: + return LangGraphPlugin( + graphs={"summary-graph": g}, + default_activity_options={"start_to_close_timeout": timedelta(seconds=10)}, + **kwargs, + ) + + +async def test_activity_summary_fn_in_history(client: Client) -> None: + plugin = _plugin(_activity_graph(summarize)) + summaries = await _run_and_collect_summaries(client, plugin, "hello") + assert summaries == [b'"value=hello"'] + + +@pytest.mark.parametrize( + "summary_fn,expected", + [ + (lambda args, kwargs: f"value={args[0]['value']}", b'"value=x"'), + (lambda args, kwargs: None, b""), + (lambda args, kwargs: "", b""), + ], +) +async def test_summary_fn_variants( + client: Client, summary_fn: SummaryFn, expected: bytes +) -> None: + plugin = _plugin(_activity_graph(summary_fn)) + summaries = await _run_and_collect_summaries(client, plugin, "x") + assert summaries == [expected] + + +async def test_static_summary(client: Client) -> None: + g: StateGraph[State, None, State, State] = StateGraph(State) + g.add_node( + "node", passthrough, metadata={"execute_in": "activity", "summary": "static"} + ) + g.add_edge(START, "node") + summaries = await _run_and_collect_summaries(client, _plugin(g), "x") + assert summaries == [b'"static"'] + + +async def test_node_static_summary_overrides_default_summary_fn( + client: Client, +) -> None: + g: StateGraph[State, None, State, State] = StateGraph(State) + g.add_node( + "node", + passthrough, + metadata={"execute_in": "activity", "summary": "node-static"}, + ) + g.add_edge(START, "node") + plugin = LangGraphPlugin( + graphs={"summary-graph": g}, + default_activity_options={ + "start_to_close_timeout": timedelta(seconds=10), + "summary_fn": lambda args, kwargs: "global", + }, + ) + summaries = await _run_and_collect_summaries(client, plugin, "x") + assert summaries == [b'"node-static"'] + + +async def test_node_summary_fn_overrides_default_summary(client: Client) -> None: + plugin = LangGraphPlugin( + graphs={"summary-graph": _activity_graph(lambda args, kwargs: "node-fn")}, + default_activity_options={ + "start_to_close_timeout": timedelta(seconds=10), + "summary": "global-static", + }, + ) + summaries = await _run_and_collect_summaries(client, plugin, "x") + assert summaries == [b'"node-fn"'] + + +async def test_default_summary_fn_applies_without_override(client: Client) -> None: + plugin = LangGraphPlugin( + graphs={"summary-graph": _activity_graph(None)}, + default_activity_options={ + "start_to_close_timeout": timedelta(seconds=10), + "summary_fn": lambda args, kwargs: "global", + }, + ) + summaries = await _run_and_collect_summaries(client, plugin, "x") + assert summaries == [b'"global"'] + + +def test_both_in_default_activity_options_raises() -> None: + with pytest.raises(ValueError, match="default_activity_options"): + LangGraphPlugin( + default_activity_options={ + "summary": "s", + "summary_fn": lambda args, kwargs: "f", + } + ) + + +def test_summary_and_summary_fn_raises() -> None: + g: StateGraph[State, None, State, State] = StateGraph(State) + g.add_node( + "node", + passthrough, + metadata={ + "execute_in": "activity", + "summary": "static", + "summary_fn": lambda args, kwargs: "dynamic", + }, + ) + g.add_edge(START, "node") + with pytest.raises(ValueError, match="not both"): + LangGraphPlugin(graphs={f"summary-graph-{uuid.uuid4()}": g}) + + +async def node_reads_meta(state: State, config: RunnableConfig) -> dict[str, str]: + metadata = config.get("metadata") or {} + return {"value": f"{state['value']}-has_fn={'summary_fn' in metadata}"} + + +async def test_summary_fn_not_in_node_metadata(client: Client) -> None: + g: StateGraph[State, None, State, State] = StateGraph(State) + g.add_node( + "node", + node_reads_meta, + metadata={ + "execute_in": "activity", + "summary_fn": lambda args, kwargs: "dynamic", + "my_key": "my_value", + }, + ) + g.add_edge(START, "node") + task_queue = f"summary-{uuid.uuid4()}" + async with Worker( + client, + task_queue=task_queue, + workflows=[SummaryWorkflow], + plugins=[_plugin(g)], + ): + result = await client.execute_workflow( + SummaryWorkflow.run, + "in", + id=f"summary-{uuid.uuid4()}", + task_queue=task_queue, + ) + assert result == {"value": "in-has_fn=False"} + + +@workflow.defn +class WorkflowNodeSummaryWorkflow: + def __init__(self) -> None: + self.app = graph("wf-node-graph").compile() + self._done = False + self._invoked = False + + @workflow.run + async def run(self, input: str) -> Any: + result = await self.app.ainvoke({"value": input}) + self._invoked = True + await workflow.wait_condition(lambda: self._done) + return result + + @workflow.signal + def finish(self) -> None: + self._done = True + + @workflow.query + def ran(self) -> bool: + return workflow.get_current_details() != "" + + @workflow.query + def invoked(self) -> bool: + return self._invoked + + +async def test_workflow_node_sets_current_details( + client: Client, env: WorkflowEnvironment +) -> None: + if env.supports_time_skipping: + pytest.skip("metadata query unreliable on the time-skipping test server") + g: StateGraph[State, None, State, State] = StateGraph(State) + g.add_node( + "node", + passthrough, + metadata={ + "execute_in": "workflow", + "summary_fn": lambda args, kwargs: f"wf:{args[0]['value']}", + }, + ) + g.add_edge(START, "node") + task_queue = f"wf-node-{uuid.uuid4()}" + async with Worker( + client, + task_queue=task_queue, + workflows=[WorkflowNodeSummaryWorkflow], + plugins=[LangGraphPlugin(graphs={"wf-node-graph": g})], + ): + handle = await client.start_workflow( + WorkflowNodeSummaryWorkflow.run, + "ready", + id=f"wf-node-{uuid.uuid4()}", + task_queue=task_queue, + ) + await assert_eq_eventually( + True, lambda: handle.query(WorkflowNodeSummaryWorkflow.ran) + ) + md: temporalio.api.sdk.v1.WorkflowMetadata = await handle.query( + "__temporal_workflow_metadata", + result_type=temporalio.api.sdk.v1.WorkflowMetadata, + ) + assert md.current_details == "wf:ready" + await handle.signal(WorkflowNodeSummaryWorkflow.finish) + assert await handle.result() == {"value": "ready"} + + +async def test_workflow_node_clears_current_details_on_empty( + client: Client, env: WorkflowEnvironment +) -> None: + if env.supports_time_skipping: + pytest.skip("metadata query unreliable on the time-skipping test server") + g: StateGraph[State, None, State, State] = StateGraph(State) + g.add_node( + "a", + passthrough, + metadata={"execute_in": "workflow", "summary_fn": lambda args, kwargs: "first"}, + ) + g.add_node( + "b", + passthrough, + metadata={"execute_in": "workflow", "summary_fn": lambda args, kwargs: None}, + ) + g.add_edge(START, "a") + g.add_edge("a", "b") + task_queue = f"wf-node-clear-{uuid.uuid4()}" + async with Worker( + client, + task_queue=task_queue, + workflows=[WorkflowNodeSummaryWorkflow], + plugins=[LangGraphPlugin(graphs={"wf-node-graph": g})], + ): + handle = await client.start_workflow( + WorkflowNodeSummaryWorkflow.run, + "ready", + id=f"wf-node-clear-{uuid.uuid4()}", + task_queue=task_queue, + ) + await assert_eq_eventually( + True, lambda: handle.query(WorkflowNodeSummaryWorkflow.invoked) + ) + md: temporalio.api.sdk.v1.WorkflowMetadata = await handle.query( + "__temporal_workflow_metadata", + result_type=temporalio.api.sdk.v1.WorkflowMetadata, + ) + assert md.current_details == "" + await handle.signal(WorkflowNodeSummaryWorkflow.finish) + await handle.result() + + +async def test_replay_with_summary_fn(client: Client) -> None: + plugin = _plugin(_activity_graph(summarize)) + task_queue = f"summary-replay-{uuid.uuid4()}" + async with Worker( + client, + task_queue=task_queue, + workflows=[SummaryWorkflow], + plugins=[plugin], + ): + handle = await client.start_workflow( + SummaryWorkflow.run, + "hello", + id=f"summary-replay-{uuid.uuid4()}", + task_queue=task_queue, + ) + await handle.result() + + await Replayer(workflows=[SummaryWorkflow], plugins=[plugin]).replay_workflow( + await handle.fetch_history() + ) diff --git a/tests/contrib/langsmith/conftest.py b/tests/contrib/langsmith/conftest.py index 1d90bae5e..1711c0446 100644 --- a/tests/contrib/langsmith/conftest.py +++ b/tests/contrib/langsmith/conftest.py @@ -8,6 +8,8 @@ import pytest +from tests.helpers.trace import TraceNode + @pytest.fixture(autouse=True) def _clear_langsmith_env_cache() -> Any: # pyright: ignore[reportUnusedFunction] @@ -92,13 +94,8 @@ def clear(self) -> None: self._by_id.clear() -def dump_traces(collector: InMemoryRunCollector) -> list[list[str]]: - """Reconstruct parent-child hierarchy grouped by root trace. - - Returns a list of traces, where each trace is a list of indented - strings (same format as dump_runs). Each trace starts from a - different root run. - """ +def build_trace_trees(collector: InMemoryRunCollector) -> list[TraceNode]: + """Build trace trees from the collector's run parent relationships.""" runs = collector.runs children: dict[str | None, list[_RunRecord]] = {} for r in runs: @@ -113,30 +110,18 @@ def dump_traces(collector: InMemoryRunCollector) -> list[list[str]]: f"which is not in the collected runs — dangling parent reference" ) - traces: list[list[str]] = [] - for root in children.get(None, []): - trace: list[str] = [] - - def _walk(parent_id: str | None, depth: int) -> None: - for child in children.get(parent_id, []): - trace.append(" " * depth + child.name) - _walk(child.id, depth + 1) - - trace.append(root.name) - _walk(root.id, 1) - traces.append(trace) - - return traces - + def build_tree(run: _RunRecord) -> TraceNode: + return TraceNode( + run.name, + [build_tree(child) for child in children.get(run.id, [])], + ) -def dump_runs(collector: InMemoryRunCollector) -> list[str]: - """Flat list of all runs across all traces.""" - return [run for trace in dump_traces(collector) for run in trace] + return [build_tree(root) for root in children.get(None, [])] -def find_traces(traces: list[list[str]], root_name: str) -> list[list[str]]: - """Filter traces by exact root name match.""" - return [t for t in traces if t[0] == root_name] +def find_trace_trees(traces: list[TraceNode], root_name: str) -> list[TraceNode]: + """Filter trace trees by exact root run name.""" + return [trace for trace in traces if trace.name == root_name] def make_mock_ls_client(collector: InMemoryRunCollector) -> MagicMock: diff --git a/tests/contrib/langsmith/test_integration.py b/tests/contrib/langsmith/test_integration.py index a89d1ea4a..f48d9d6ac 100644 --- a/tests/contrib/langsmith/test_integration.py +++ b/tests/contrib/langsmith/test_integration.py @@ -26,13 +26,13 @@ from temporalio.testing import WorkflowEnvironment from tests.contrib.langsmith.conftest import ( InMemoryRunCollector, - dump_runs, - dump_traces, - find_traces, + build_trace_trees, + find_trace_trees, make_mock_ls_client, ) from tests.helpers import new_worker from tests.helpers.nexus import make_nexus_endpoint_name +from tests.helpers.trace import assert_trace_hierarchy # --------------------------------------------------------------------------- # Shared @traceable functions and activities @@ -359,7 +359,6 @@ async def test_workflow_activity_trace_hierarchy( ) assert await result.result() == "activity-done" - hierarchy = dump_runs(collector) expected = [ "StartWorkflow:SimpleWorkflow", "RunWorkflow:SimpleWorkflow", @@ -367,9 +366,7 @@ async def test_workflow_activity_trace_hierarchy( " RunActivity:simple_activity", " simple_activity", ] - assert hierarchy == expected, ( - f"Hierarchy mismatch.\nExpected:\n{expected}\nActual:\n{hierarchy}" - ) + assert_trace_hierarchy(build_trace_trees(collector), expected) # Verify run_type: RunActivity is "tool", others are "chain" for run in collector.runs: @@ -421,7 +418,6 @@ async def test_no_duplicate_traces_on_replay( # Workflow→activity→@traceable flow should produce exactly these runs # with no duplicates from replay: - hierarchy = dump_runs(collector) expected = [ "StartWorkflow:TraceableActivityWorkflow", "RunWorkflow:TraceableActivityWorkflow", @@ -430,10 +426,7 @@ async def test_no_duplicate_traces_on_replay( " traceable_activity", " inner_llm_call", ] - assert hierarchy == expected, ( - f"Hierarchy mismatch (possible replay duplicates).\n" - f"Expected:\n{expected}\nActual:\n{hierarchy}" - ) + assert_trace_hierarchy(build_trace_trees(collector), expected) # --------------------------------------------------------------------------- @@ -467,7 +460,6 @@ async def test_activity_failure_marked( with pytest.raises(WorkflowFailureError): await handle.result() - hierarchy = dump_runs(collector) expected = [ "StartWorkflow:ActivityFailureWorkflow", "RunWorkflow:ActivityFailureWorkflow", @@ -475,9 +467,7 @@ async def test_activity_failure_marked( " RunActivity:failing_activity", " failing_activity", ] - assert hierarchy == expected, ( - f"Hierarchy mismatch.\nExpected:\n{expected}\nActual:\n{hierarchy}" - ) + assert_trace_hierarchy(build_trace_trees(collector), expected) # Verify the RunActivity run has an error activity_runs = [ r for r in collector.runs if r.name == "RunActivity:failing_activity" @@ -509,14 +499,11 @@ async def test_workflow_failure_marked( with pytest.raises(WorkflowFailureError): await handle.result() - hierarchy = dump_runs(collector) expected = [ "StartWorkflow:FailingWorkflow", "RunWorkflow:FailingWorkflow", ] - assert hierarchy == expected, ( - f"Hierarchy mismatch.\nExpected:\n{expected}\nActual:\n{hierarchy}" - ) + assert_trace_hierarchy(build_trace_trees(collector), expected) # Verify the RunWorkflow run has an error wf_runs = [r for r in collector.runs if r.name == "RunWorkflow:FailingWorkflow"] assert len(wf_runs) == 1 @@ -547,7 +534,6 @@ async def test_benign_error_not_marked( with pytest.raises(WorkflowFailureError): await handle.result() - hierarchy = dump_runs(collector) expected = [ "StartWorkflow:BenignErrorWorkflow", "RunWorkflow:BenignErrorWorkflow", @@ -555,9 +541,7 @@ async def test_benign_error_not_marked( " RunActivity:benign_failing_activity", " benign_failing_activity", ] - assert hierarchy == expected, ( - f"Hierarchy mismatch.\nExpected:\n{expected}\nActual:\n{hierarchy}" - ) + assert_trace_hierarchy(build_trace_trees(collector), expected) # The RunActivity run for benign error should NOT have error set activity_runs = [ r for r in collector.runs if r.name == "RunActivity:benign_failing_activity" @@ -572,6 +556,7 @@ async def test_benign_error_not_marked( class TestComprehensiveTracing: + @pytest.mark.requires_local_server async def test_comprehensive_with_temporal_runs( self, client: Client, env: WorkflowEnvironment ) -> None: @@ -650,12 +635,12 @@ async def user_pipeline() -> WorkflowHandle[Any, Any]: assert result == "comprehensive-done" - traces = dump_traces(collector) + trace_trees = build_trace_trees(collector) # user_pipeline trace: StartWorkflow + full workflow execution tree - workflow_traces = find_traces(traces, "user_pipeline") - assert len(workflow_traces) == 1 - assert workflow_traces[0] == [ + workflow_trace_trees = find_trace_trees(trace_trees, "user_pipeline") + assert len(workflow_trace_trees) == 1 + expected_workflow = [ "user_pipeline", " StartWorkflow:ComprehensiveWorkflow", " RunWorkflow:ComprehensiveWorkflow", @@ -717,54 +702,75 @@ async def user_pipeline() -> WorkflowHandle[Any, Any]: " outer_chain", " inner_llm_call", ] + assert_trace_hierarchy(workflow_trace_trees, expected_workflow) # poll_query trace (separate root, variable number of iterations) - poll_traces = find_traces(traces, "poll_query") - assert len(poll_traces) == 1 - poll = poll_traces[0] - assert poll[0] == "poll_query" - poll_children = poll[1:] - for i in range(0, len(poll_children), 2): - assert poll_children[i] == " QueryWorkflow:is_waiting_for_signal" - assert poll_children[i + 1] == " HandleQuery:is_waiting_for_signal" + poll_trace_trees = find_trace_trees(trace_trees, "poll_query") + assert len(poll_trace_trees) == 1 + poll = poll_trace_trees[0] + assert poll.name == "poll_query" + poll_children = poll.children + for poll_child in poll_children: + assert poll_child.name == "QueryWorkflow:is_waiting_for_signal" + assert [child.name for child in poll_child.children] == [ + "HandleQuery:is_waiting_for_signal" + ] + assert not poll_child.children[0].children # Raw-client query — no parent context, appears as root - raw_query_traces = [t for t in traces if t[0].startswith("HandleQuery:")] - assert len(raw_query_traces) == 1 + raw_query_trace_trees = [ + trace for trace in trace_trees if trace.name.startswith("HandleQuery:") + ] + assert len(raw_query_trace_trees) == 1 # Phase 2: each operation is its own root trace - query_traces = find_traces(traces, "QueryWorkflow:my_query") - assert len(query_traces) == 1 - assert query_traces[0] == [ - "QueryWorkflow:my_query", - " HandleQuery:my_query", - ] + query_trace_trees = find_trace_trees(trace_trees, "QueryWorkflow:my_query") + assert len(query_trace_trees) == 1 + assert_trace_hierarchy( + query_trace_trees, + [ + "QueryWorkflow:my_query", + " HandleQuery:my_query", + ], + ) - signal_traces = find_traces(traces, "SignalWorkflow:my_signal") - assert len(signal_traces) == 1 - assert signal_traces[0] == [ - "SignalWorkflow:my_signal", - " HandleSignal:my_signal", - ] + signal_trace_trees = find_trace_trees(trace_trees, "SignalWorkflow:my_signal") + assert len(signal_trace_trees) == 1 + assert_trace_hierarchy( + signal_trace_trees, + [ + "SignalWorkflow:my_signal", + " HandleSignal:my_signal", + ], + ) - update_traces = find_traces(traces, "StartWorkflowUpdate:my_update") - assert len(update_traces) == 1 - assert update_traces[0] == [ - "StartWorkflowUpdate:my_update", - " ValidateUpdate:my_update", - " HandleUpdate:my_update", - ] + update_trace_trees = find_trace_trees( + trace_trees, "StartWorkflowUpdate:my_update" + ) + assert len(update_trace_trees) == 1 + assert_trace_hierarchy( + update_trace_trees, + [ + "StartWorkflowUpdate:my_update", + " ValidateUpdate:my_update", + " HandleUpdate:my_update", + ], + ) # Update without a validator — no ValidateUpdate trace - unvalidated_traces = find_traces( - traces, "StartWorkflowUpdate:my_unvalidated_update" + unvalidated_trace_trees = find_trace_trees( + trace_trees, "StartWorkflowUpdate:my_unvalidated_update" + ) + assert len(unvalidated_trace_trees) == 1 + assert_trace_hierarchy( + unvalidated_trace_trees, + [ + "StartWorkflowUpdate:my_unvalidated_update", + " HandleUpdate:my_unvalidated_update", + ], ) - assert len(unvalidated_traces) == 1 - assert unvalidated_traces[0] == [ - "StartWorkflowUpdate:my_unvalidated_update", - " HandleUpdate:my_unvalidated_update", - ] + @pytest.mark.requires_local_server async def test_comprehensive_without_temporal_runs( self, client: Client, env: WorkflowEnvironment ) -> None: @@ -841,11 +847,11 @@ async def user_pipeline() -> WorkflowHandle[Any, Any]: assert result == "comprehensive-done" - traces = dump_traces(collector) + trace_trees = build_trace_trees(collector) # Main workflow trace (only @traceable runs, nested under user_pipeline) - workflow_traces = find_traces(traces, "user_pipeline") - assert len(workflow_traces) == 1 + workflow_trace_trees = find_trace_trees(trace_trees, "user_pipeline") + assert len(workflow_trace_trees) == 1 expected_workflow = [ "user_pipeline", " nested_traceable_activity", @@ -875,15 +881,12 @@ async def user_pipeline() -> WorkflowHandle[Any, Any]: " outer_chain", " inner_llm_call", ] - assert workflow_traces[0] == expected_workflow, ( - f"Workflow trace mismatch.\n" - f"Expected:\n{expected_workflow}\nActual:\n{workflow_traces[0]}" - ) + assert_trace_hierarchy(workflow_trace_trees, expected_workflow) # Poll query — separate root, just the @traceable wrapper, no Temporal children - poll_traces = find_traces(traces, "poll_query") - assert len(poll_traces) == 1 - assert poll_traces[0] == ["poll_query"] + poll_trace_trees = find_trace_trees(trace_trees, "poll_query") + assert len(poll_trace_trees) == 1 + assert_trace_hierarchy(poll_trace_trees, ["poll_query"]) # --------------------------------------------------------------------------- @@ -976,7 +979,6 @@ async def test_factory_traceable_no_external_context( == "response to: async|sync-response to: sync|sync-response to: mixed" ) - hierarchy = dump_runs(collector) expected = [ "outer_chain", " inner_llm_call", @@ -988,9 +990,7 @@ async def test_factory_traceable_no_external_context( " outer_chain", " inner_llm_call", ] - assert hierarchy == expected, ( - f"Hierarchy mismatch.\nExpected:\n{expected}\nActual:\n{hierarchy}" - ) + assert_trace_hierarchy(build_trace_trees(collector), expected) # Verify no duplicate run IDs (replay safety with max_cached_workflows=0) run_ids = [r.id for r in collector.runs] @@ -1063,7 +1063,6 @@ async def test_mixed_sync_async_traceable_with_temporal_runs( == "response to: async|sync-response to: sync|sync-response to: mixed" ) - hierarchy = dump_runs(collector) # With add_temporal_runs=True, Temporal operations get their own runs. # @traceable calls nest under the RunWorkflow run. expected = [ @@ -1081,9 +1080,7 @@ async def test_mixed_sync_async_traceable_with_temporal_runs( " outer_chain", " inner_llm_call", ] - assert hierarchy == expected, ( - f"Hierarchy mismatch.\nExpected:\n{expected}\nActual:\n{hierarchy}" - ) + assert_trace_hierarchy(build_trace_trees(collector), expected) # Verify no duplicate run IDs (replay safety with max_cached_workflows=0) run_ids = [r.id for r in collector.runs] @@ -1138,6 +1135,7 @@ async def run(self) -> str: class TestNexusInboundTracing: """Verifies nexus handlers receive tracing_context for @traceable collection.""" + @pytest.mark.requires_local_server async def test_nexus_direct_traceable_without_temporal_runs( self, client: Client, @@ -1183,16 +1181,13 @@ async def test_nexus_direct_traceable_without_temporal_runs( assert result == "response to: nexus-input" - hierarchy = dump_runs(collector) # @traceable runs from inside the nexus handler should be collected # via the interceptor's tracing_context setup. expected = [ "nexus_direct_traceable", " inner_llm_call", ] - assert hierarchy == expected, ( - f"Hierarchy mismatch.\nExpected:\n{expected}\nActual:\n{hierarchy}" - ) + assert_trace_hierarchy(build_trace_trees(collector), expected) # --------------------------------------------------------------------------- @@ -1268,8 +1263,6 @@ async def test_temporal_prefixed_query_not_traced( # Built-in queries — should NOT be traced await handle.query("__temporal_workflow_metadata") - await handle.query("__stack_trace") - await handle.query("__enhanced_stack_trace") # User query — should be traced await handle.query(QueryFilteringWorkflow.my_query) @@ -1278,8 +1271,10 @@ async def test_temporal_prefixed_query_not_traced( assert await handle.result() == "done" # Built-in queries should be absent; only user query and signal remain. - traces = dump_traces(collector) - assert traces == [ - ["HandleQuery:my_query"], - ["HandleSignal:complete"], - ], f"Unexpected traces: {traces}" + assert_trace_hierarchy( + build_trace_trees(collector), + [ + "HandleQuery:my_query", + "HandleSignal:complete", + ], + ) diff --git a/tests/contrib/langsmith/test_interceptor.py b/tests/contrib/langsmith/test_interceptor.py index 45d86bc5f..4c18c3f9f 100644 --- a/tests/contrib/langsmith/test_interceptor.py +++ b/tests/contrib/langsmith/test_interceptor.py @@ -16,9 +16,11 @@ HEADER_KEY, _extract_context, _inject_context, + _LangSmithWorkflowInboundInterceptor, _maybe_run, _ReplaySafeRunTree, ) +from temporalio.worker import HandleQueryInput # --------------------------------------------------------------------------- # Helpers @@ -88,6 +90,48 @@ def _get_runtree_metadata(MockRunTree: MagicMock) -> dict[str, Any]: return kwargs.get("metadata", {}) +# =================================================================== +# TestBuiltinQueryFiltering +# =================================================================== + + +class _RecordingWorkflowInboundInterceptor: + def __init__(self) -> None: + self.queries: list[HandleQueryInput] = [] + + async def handle_query(self, input: HandleQueryInput) -> str: + self.queries.append(input) + return "forwarded" + + +class TestBuiltinQueryFiltering: + async def test_builtin_queries_bypass_tracing(self) -> None: + next_interceptor = _RecordingWorkflowInboundInterceptor() + interceptor = _LangSmithWorkflowInboundInterceptor( + next_interceptor # type: ignore[arg-type] + ) + + with patch.object(interceptor, "_workflow_maybe_run") as maybe_run: + for query in ( + "__temporal_workflow_metadata", + "__stack_trace", + "__enhanced_stack_trace", + ): + assert ( + await interceptor.handle_query( + HandleQueryInput(id="id", query=query, args=[], headers={}) + ) + == "forwarded" + ) + + assert [input.query for input in next_interceptor.queries] == [ + "__temporal_workflow_metadata", + "__stack_trace", + "__enhanced_stack_trace", + ] + maybe_run.assert_not_called() + + # =================================================================== # TestContextPropagation # =================================================================== diff --git a/tests/contrib/langsmith/test_plugin.py b/tests/contrib/langsmith/test_plugin.py index 17c21cb7c..6e3cb2e86 100644 --- a/tests/contrib/langsmith/test_plugin.py +++ b/tests/contrib/langsmith/test_plugin.py @@ -12,7 +12,10 @@ from temporalio.client import Client, WorkflowHandle from temporalio.contrib.langsmith import LangSmithInterceptor, LangSmithPlugin from temporalio.testing import WorkflowEnvironment -from tests.contrib.langsmith.conftest import dump_traces, find_traces +from tests.contrib.langsmith.conftest import ( + build_trace_trees, + find_trace_trees, +) from tests.contrib.langsmith.test_integration import ( ComprehensiveWorkflow, NexusService, @@ -24,6 +27,7 @@ ) from tests.helpers import new_worker from tests.helpers.nexus import make_nexus_endpoint_name +from tests.helpers.trace import assert_trace_hierarchy class TestPluginConstruction: @@ -53,6 +57,7 @@ def test_construction_stores_all_config(self) -> None: class TestPluginIntegration: """End-to-end test using LangSmithPlugin as a Temporal client plugin.""" + @pytest.mark.requires_local_server async def test_comprehensive_plugin_trace_hierarchy( self, client: Client, env: WorkflowEnvironment ) -> None: @@ -110,12 +115,12 @@ async def user_pipeline() -> WorkflowHandle[Any, Any]: assert result == "comprehensive-done" - traces = dump_traces(collector) + trace_trees = build_trace_trees(collector) # user_pipeline trace: StartWorkflow + full workflow execution tree - workflow_traces = find_traces(traces, "user_pipeline") - assert len(workflow_traces) == 1 - assert workflow_traces[0] == [ + workflow_trace_trees = find_trace_trees(trace_trees, "user_pipeline") + assert len(workflow_trace_trees) == 1 + expected_workflow = [ "user_pipeline", " StartWorkflow:ComprehensiveWorkflow", " RunWorkflow:ComprehensiveWorkflow", @@ -177,46 +182,64 @@ async def user_pipeline() -> WorkflowHandle[Any, Any]: " outer_chain", " inner_llm_call", ] + assert_trace_hierarchy(workflow_trace_trees, expected_workflow) # poll_query trace (separate root, variable number of iterations) - poll_traces = find_traces(traces, "poll_query") - assert len(poll_traces) == 1 - poll = poll_traces[0] - assert poll[0] == "poll_query" - poll_children = poll[1:] - for i in range(0, len(poll_children), 2): - assert poll_children[i] == " QueryWorkflow:is_waiting_for_signal" - assert poll_children[i + 1] == " HandleQuery:is_waiting_for_signal" + poll_trace_trees = find_trace_trees(trace_trees, "poll_query") + assert len(poll_trace_trees) == 1 + poll = poll_trace_trees[0] + assert poll.name == "poll_query" + poll_children = poll.children + for poll_child in poll_children: + assert poll_child.name == "QueryWorkflow:is_waiting_for_signal" + assert [child.name for child in poll_child.children] == [ + "HandleQuery:is_waiting_for_signal" + ] + assert not poll_child.children[0].children # Each remaining operation is its own root trace - query_traces = find_traces(traces, "QueryWorkflow:my_query") - assert len(query_traces) == 1 - assert query_traces[0] == [ - "QueryWorkflow:my_query", - " HandleQuery:my_query", - ] + query_trace_trees = find_trace_trees(trace_trees, "QueryWorkflow:my_query") + assert len(query_trace_trees) == 1 + assert_trace_hierarchy( + query_trace_trees, + [ + "QueryWorkflow:my_query", + " HandleQuery:my_query", + ], + ) - signal_traces = find_traces(traces, "SignalWorkflow:my_signal") - assert len(signal_traces) == 1 - assert signal_traces[0] == [ - "SignalWorkflow:my_signal", - " HandleSignal:my_signal", - ] + signal_trace_trees = find_trace_trees(trace_trees, "SignalWorkflow:my_signal") + assert len(signal_trace_trees) == 1 + assert_trace_hierarchy( + signal_trace_trees, + [ + "SignalWorkflow:my_signal", + " HandleSignal:my_signal", + ], + ) - update_traces = find_traces(traces, "StartWorkflowUpdate:my_update") - assert len(update_traces) == 1 - assert update_traces[0] == [ - "StartWorkflowUpdate:my_update", - " ValidateUpdate:my_update", - " HandleUpdate:my_update", - ] + update_trace_trees = find_trace_trees( + trace_trees, "StartWorkflowUpdate:my_update" + ) + assert len(update_trace_trees) == 1 + assert_trace_hierarchy( + update_trace_trees, + [ + "StartWorkflowUpdate:my_update", + " ValidateUpdate:my_update", + " HandleUpdate:my_update", + ], + ) # Update without a validator — no ValidateUpdate trace - unvalidated_traces = find_traces( - traces, "StartWorkflowUpdate:my_unvalidated_update" + unvalidated_trace_trees = find_trace_trees( + trace_trees, "StartWorkflowUpdate:my_unvalidated_update" + ) + assert len(unvalidated_trace_trees) == 1 + assert_trace_hierarchy( + unvalidated_trace_trees, + [ + "StartWorkflowUpdate:my_unvalidated_update", + " HandleUpdate:my_unvalidated_update", + ], ) - assert len(unvalidated_traces) == 1 - assert unvalidated_traces[0] == [ - "StartWorkflowUpdate:my_unvalidated_update", - " HandleUpdate:my_unvalidated_update", - ] diff --git a/tests/contrib/langsmith/test_tracing_env_override.py b/tests/contrib/langsmith/test_tracing_env_override.py index 9d871e1d3..c8aaa2fb0 100644 --- a/tests/contrib/langsmith/test_tracing_env_override.py +++ b/tests/contrib/langsmith/test_tracing_env_override.py @@ -162,6 +162,7 @@ async def test_no_runs_when_langchain_tracing_v2_disabled( f"{[r.name for r in collector.runs]}" ) + @pytest.mark.requires_local_server async def test_no_runs_when_tracing_disabled_for_nexus_start( self, client: Client, diff --git a/tests/contrib/openai_agents/test_openai.py b/tests/contrib/openai_agents/test_openai.py index 96cc25133..25597ee55 100644 --- a/tests/contrib/openai_agents/test_openai.py +++ b/tests/contrib/openai_agents/test_openai.py @@ -89,7 +89,10 @@ ) from temporalio.contrib.openai_agents._invoke_model_activity import _build_tool from temporalio.contrib.openai_agents._model_parameters import ModelSummaryProvider -from temporalio.contrib.openai_agents._openai_runner import _convert_agent +from temporalio.contrib.openai_agents._openai_runner import ( + _coerce_run_config, + _convert_agent, +) from temporalio.contrib.openai_agents._temporal_model_stub import ( _TemporalModelStub, ) @@ -461,7 +464,7 @@ async def test_tool_failure_workflow(client: Client): "What is the weather in Tokio?", id=f"tools-failure-workflow-{uuid.uuid4()}", task_queue=worker.task_queue, - execution_timeout=timedelta(seconds=2), + execution_timeout=timedelta(seconds=30), ) with pytest.raises(WorkflowFailureError) as e: await workflow_handle.result() @@ -471,6 +474,7 @@ async def test_tool_failure_workflow(client: Client): @pytest.mark.parametrize("use_local_model", [True, False]) +@pytest.mark.requires_local_server async def test_nexus_tool_workflow( client: Client, env: WorkflowEnvironment, use_local_model: bool ): @@ -2073,13 +2077,14 @@ def get_model(self, model_name: str | None) -> Model: return self._model +MULTIPLE_MODELS_FINAL_RESPONSE = "I'm here to help! Was there a specific task you needed assistance with regarding the storeroom?" + + def multiple_models_mock_model(): return TestModel.returning_responses( [ ResponseBuilders.tool_call("{}", "transfer_to_underling"), - ResponseBuilders.output_message( - "I'm here to help! Was there a specific task you needed assistance with regarding the storeroom?" - ), + ResponseBuilders.output_message(MULTIPLE_MODELS_FINAL_RESPONSE), ] ) @@ -2159,6 +2164,84 @@ async def test_run_config_models(client: Client): assert provider.model_names == {"gpt-4o"} +@workflow.defn +class DictRunConfigWorkflow: + """Same agents as MultipleModelWorkflow, but passes run_config as a plain + dict, which openai-agents >= 0.19 accepts at its public runner boundaries.""" + + @workflow.run + async def run(self) -> str: + underling = Agent[None]( + name="Underling", + instructions="You do all the work you are told.", + ) + + starting_agent = Agent[None]( + name="Lazy Assistant", + model="gpt-4o-mini", + instructions="You delegate all your work to another agent.", + handoffs=[underling], + ) + # Typed as Any so this also type-checks against openai-agents + # versions whose run_config annotation does not include dict. + dict_run_config: Any = {"model": "gpt-4o"} + result = await Runner.run( + starting_agent=starting_agent, + input="Have you cleaned the store room yet?", + run_config=dict_run_config, + ) + return result.final_output + + +async def test_dict_run_config_models(client: Client): + # A dict run_config must behave identically to the equivalent + # RunConfig(model="gpt-4o") in test_run_config_models above. + provider = AssertDifferentModelProvider(multiple_models_mock_model()) + async with AgentEnvironment( + model_params=ModelActivityParameters( + start_to_close_timeout=timedelta(seconds=120) + ), + model_provider=provider, + ) as env: + client = env.applied_on_client(client) + + async with new_worker( + client, + DictRunConfigWorkflow, + ) as worker: + workflow_handle = await client.start_workflow( + DictRunConfigWorkflow.run, + id=f"dict-run-config-model-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + result = await workflow_handle.result() + + # Only the model from the runconfig override is used + assert provider.model_names == {"gpt-4o"} + assert result == MULTIPLE_MODELS_FINAL_RESPONSE + + +def test_coerce_run_config_validation(): + # Mirrors upstream agents' normalization: equivalent RunConfig out of a + # dict, and the same TypeErrors for invalid input. + coerced = _coerce_run_config({"model": "gpt-4o", "workflow_name": "wf"}) + assert isinstance(coerced, RunConfig) + assert coerced.model == "gpt-4o" + assert coerced.workflow_name == "wf" + + run_config = RunConfig(model="gpt-4o") + assert _coerce_run_config(run_config) is run_config + + with pytest.raises(TypeError, match="Unknown run_config settings: bogus_setting"): + _coerce_run_config({"model": "gpt-4o", "bogus_setting": True}) + + with pytest.raises( + TypeError, match="run_config must be a RunConfig instance or a dict, got int" + ): + _coerce_run_config(42) + + async def test_summary_provider(client: Client): class SummaryProvider(ModelSummaryProvider): def provide( diff --git a/tests/contrib/openai_agents/test_openai_streaming.py b/tests/contrib/openai_agents/test_openai_streaming.py index 851dc207d..ab711cd86 100644 --- a/tests/contrib/openai_agents/test_openai_streaming.py +++ b/tests/contrib/openai_agents/test_openai_streaming.py @@ -159,7 +159,9 @@ async def stream_response( input_tokens=10, output_tokens=5, total_tokens=15, - input_tokens_details=InputTokensDetails(cached_tokens=0), + input_tokens_details=InputTokensDetails.model_validate( + {"cached_tokens": 0, "cache_write_tokens": 0} + ), output_tokens_details=OutputTokensDetails(reasoning_tokens=0), ), ) diff --git a/tests/contrib/openai_agents/test_openai_tracing.py b/tests/contrib/openai_agents/test_openai_tracing.py index facc3212b..77950c035 100644 --- a/tests/contrib/openai_agents/test_openai_tracing.py +++ b/tests/contrib/openai_agents/test_openai_tracing.py @@ -11,6 +11,7 @@ from temporalio import activity, workflow from temporalio.client import Client +from temporalio.contrib.openai_agents import _temporal_openai_agents from temporalio.contrib.openai_agents.testing import ( AgentEnvironment, ) @@ -50,6 +51,33 @@ def force_flush(self) -> None: pass +def test_otel_instrumentation_lifecycle_does_not_nest() -> None: + from openinference.instrumentation.openai_agents._processor import ( + OpenInferenceTracingProcessor, + ) + from opentelemetry import trace + + original = OpenInferenceTracingProcessor.on_trace_start + _temporal_openai_agents._install_otel_instrumentation(trace.get_tracer_provider()) + try: + installed_patch = OpenInferenceTracingProcessor.on_trace_start + assert installed_patch is not original + + _temporal_openai_agents._install_otel_instrumentation( + trace.get_tracer_provider() + ) + try: + assert OpenInferenceTracingProcessor.on_trace_start is installed_patch + finally: + _temporal_openai_agents._uninstall_otel_instrumentation() + + assert OpenInferenceTracingProcessor.on_trace_start is installed_patch + finally: + _temporal_openai_agents._uninstall_otel_instrumentation() + + assert OpenInferenceTracingProcessor.on_trace_start is original + + async def test_tracing(client: Client): async with AgentEnvironment(model=research_mock_model()) as env: client = env.applied_on_client(client) diff --git a/tests/contrib/opentelemetry/test_opentelemetry.py b/tests/contrib/opentelemetry/test_opentelemetry.py index 71e2fa41d..1bab931ac 100644 --- a/tests/contrib/opentelemetry/test_opentelemetry.py +++ b/tests/contrib/opentelemetry/test_opentelemetry.py @@ -457,6 +457,7 @@ async def test_opentelemetry_tracing_update_with_start( ] +@pytest.mark.requires_local_server async def test_opentelemetry_tracing_nexus(client: Client, env: WorkflowEnvironment): if env.supports_time_skipping: pytest.skip( diff --git a/tests/contrib/opentelemetry/test_opentelemetry_plugin.py b/tests/contrib/opentelemetry/test_opentelemetry_plugin.py index 3fd50e89b..337270d0c 100644 --- a/tests/contrib/opentelemetry/test_opentelemetry_plugin.py +++ b/tests/contrib/opentelemetry/test_opentelemetry_plugin.py @@ -233,6 +233,7 @@ def validate_update_status(self, status: str) -> None: raise ValueError("Status cannot be empty") +@pytest.mark.requires_local_server async def test_opentelemetry_comprehensive_tracing( client: Client, env: WorkflowEnvironment, diff --git a/tests/contrib/pydantic/test_pydantic.py b/tests/contrib/pydantic/test_pydantic.py index 69a723a56..7d70dfd19 100644 --- a/tests/contrib/pydantic/test_pydantic.py +++ b/tests/contrib/pydantic/test_pydantic.py @@ -2,6 +2,7 @@ import datetime import os import pathlib +import typing import uuid import pydantic @@ -9,7 +10,11 @@ from pydantic import BaseModel from temporalio.client import Client -from temporalio.contrib.pydantic import pydantic_data_converter +from temporalio.contrib.pydantic import ( + PydanticJSONPlainPayloadConverter, + PydanticPayloadConverter, + pydantic_data_converter, +) from temporalio.worker import Worker from temporalio.worker.workflow_sandbox._restrictions import ( RestrictionContext, @@ -41,6 +46,157 @@ clone_objects, ) +_MANY_TYPE_HINTS = tuple( + typing.cast(type, typing.cast(object, typing.Annotated[list[int], index])) + for index in range(1025) +) +_UNHASHABLE_TYPE_HINT = typing.cast( + type, typing.cast(object, typing.Annotated[list[int], []]) +) + + +@pytest.mark.parametrize( + ( + "converter_kwargs", + "type_hints", + "expected_type_adapter_constructions", + ), + [ + # Default caches repeated hints + ({}, (list[int], list[int]), 1), + # Zero disables caching + ({"max_cached_type_adapters": 0}, (list[int], list[int]), 2), + # Unhashable hints bypass the cache + ({}, (_UNHASHABLE_TYPE_HINT, _UNHASHABLE_TYPE_HINT), 2), + # Default bound is 1024: 1025 distinct hints evict the first + ({}, _MANY_TYPE_HINTS + (_MANY_TYPE_HINTS[0],), 1026), + # None is unbounded: no eviction + ( + {"max_cached_type_adapters": None}, + _MANY_TYPE_HINTS + (_MANY_TYPE_HINTS[0],), + 1025, + ), + # Explicit bound evicts least recently used + ( + {"max_cached_type_adapters": 1}, + (list[int], _MANY_TYPE_HINTS[0], list[int]), + 3, + ), + ], +) +def test_type_adapter_reuse( + monkeypatch: pytest.MonkeyPatch, + converter_kwargs: dict[str, typing.Any], + type_hints: tuple[type, ...], + expected_type_adapter_constructions: int, +): + actual_type_adapter = pydantic.TypeAdapter + type_adapter_constructions = 0 + + def counting_type_adapter( + type_hint: typing.Any, + ) -> pydantic.TypeAdapter[typing.Any]: + nonlocal type_adapter_constructions + type_adapter_constructions += 1 + return actual_type_adapter(type_hint) + + monkeypatch.setattr( + "temporalio.contrib.pydantic.TypeAdapter", counting_type_adapter + ) + converter = PydanticJSONPlainPayloadConverter(**converter_kwargs) + payload = converter.to_payload([1]) + assert payload is not None + for type_hint in type_hints: + assert converter.from_payload(payload, type_hint) == [1] + assert type_adapter_constructions == expected_type_adapter_constructions + + +@pytest.mark.parametrize( + ("max_cached_type_adapters", "expected_type_adapter_constructions"), + [(None, 1), (0, 2)], +) +def test_composite_converter_forwards_type_adapter_cache_size( + monkeypatch: pytest.MonkeyPatch, + max_cached_type_adapters: int | None, + expected_type_adapter_constructions: int, +): + actual_type_adapter = pydantic.TypeAdapter + type_adapter_constructions = 0 + + def counting_type_adapter( + type_hint: typing.Any, + ) -> pydantic.TypeAdapter[typing.Any]: + nonlocal type_adapter_constructions + type_adapter_constructions += 1 + return actual_type_adapter(type_hint) + + monkeypatch.setattr( + "temporalio.contrib.pydantic.TypeAdapter", counting_type_adapter + ) + converter = PydanticPayloadConverter( + max_cached_type_adapters=max_cached_type_adapters + ) + payloads = converter.to_payloads([[1], [2]]) + assert converter.from_payloads(payloads, [list[int], list[int]]) == [[1], [2]] + assert type_adapter_constructions == expected_type_adapter_constructions + + +def test_type_adapter_reuse_across_threads_with_deferred_build(): + import concurrent.futures + + class DeferredModel(BaseModel): + model_config = pydantic.ConfigDict(defer_build=True) + value: int + + converter = PydanticJSONPlainPayloadConverter() + payload = converter.to_payload(DeferredModel(value=1)) + assert payload is not None + + def decode() -> DeferredModel: + return converter.from_payload(payload, DeferredModel) + + with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor: + results = list(executor.map(lambda _: decode(), range(64))) + assert all(result == DeferredModel(value=1) for result in results) + + +def test_type_adapter_cache_distinguishes_reimported_classes(): + # The workflow sandbox re-imports user modules, producing distinct class + # objects with identical names. Class hints hash by identity, so each + # world's class must get its own cache slot and validate to itself. + import types + + source = "from pydantic import BaseModel\n\nclass Foo(BaseModel):\n name: str\n" + + def load_module() -> types.ModuleType: + module = types.ModuleType("test_reimported_models") + exec(compile(source, "test_reimported_models.py", "exec"), module.__dict__) + return module + + foo_outside = load_module().Foo + foo_sandbox = load_module().Foo + assert foo_outside is not foo_sandbox + assert hash(foo_outside) != hash(foo_sandbox) + + # Worst case: one converter shared across both worlds (the real sandbox + # creates a separate converter per workflow instance). + converter = PydanticJSONPlainPayloadConverter() + payload = converter.to_payload(foo_outside(name="x")) + assert payload is not None + decoded_outside = converter.from_payload(payload, foo_outside) + decoded_sandbox = converter.from_payload(payload, foo_sandbox) + assert type(decoded_outside) is foo_outside + assert type(decoded_sandbox) is foo_sandbox + + +@pytest.mark.parametrize( + "converter_type", + [PydanticJSONPlainPayloadConverter, PydanticPayloadConverter], +) +def test_type_adapter_cache_rejects_negative_size(converter_type: type): + with pytest.raises(ValueError, match="max_cached_type_adapters cannot be negative"): + converter_type(max_cached_type_adapters=-1) + async def test_instantiation_outside_sandbox(): make_list_of_pydantic_objects() diff --git a/tests/contrib/strands/test_hooks.py b/tests/contrib/strands/test_hooks.py index 19976cb44..7bcf0172f 100644 --- a/tests/contrib/strands/test_hooks.py +++ b/tests/contrib/strands/test_hooks.py @@ -67,7 +67,7 @@ async def run(self, prompt: str) -> list[str]: async def test_hooks(client: Client): _AUDIT_LOG.clear() - task_queue = "test_hooks" + task_queue = f"test_hooks-{uuid4()}" plugin = StrandsPlugin( models={ "mock": lambda: MockModel( diff --git a/tests/contrib/strands/test_interrupt.py b/tests/contrib/strands/test_interrupt.py index 64f72bc07..b5c76add0 100644 --- a/tests/contrib/strands/test_interrupt.py +++ b/tests/contrib/strands/test_interrupt.py @@ -65,7 +65,7 @@ async def run(self, prompt: str) -> str: async def test_interrupt(client: Client): - task_queue = "test_interrupt" + task_queue = f"test_interrupt-{uuid4()}" plugin = StrandsPlugin( models={ "mock": lambda: MockModel( diff --git a/tests/contrib/strands/test_interrupt_exception.py b/tests/contrib/strands/test_interrupt_exception.py index ed858b32b..e7492f286 100644 --- a/tests/contrib/strands/test_interrupt_exception.py +++ b/tests/contrib/strands/test_interrupt_exception.py @@ -99,7 +99,7 @@ async def run(self, prompt: str) -> str: async def test_in_workflow_tool_interrupt(client: Client): - task_queue = "test_in_workflow_tool_interrupt" + task_queue = f"test_in_workflow_tool_interrupt-{uuid4()}" plugin = StrandsPlugin( models={ "mock": lambda: MockModel( @@ -140,7 +140,7 @@ async def test_in_workflow_tool_interrupt(client: Client): async def test_activity_tool_interrupt(client: Client): global _activity_delete_calls _activity_delete_calls = 0 - task_queue = "test_activity_tool_interrupt" + task_queue = f"test_activity_tool_interrupt-{uuid4()}" plugin = StrandsPlugin( models={ "mock": lambda: MockModel( diff --git a/tests/contrib/strands/test_invocation_state.py b/tests/contrib/strands/test_invocation_state.py index 01fd4e004..84b5531a4 100644 --- a/tests/contrib/strands/test_invocation_state.py +++ b/tests/contrib/strands/test_invocation_state.py @@ -58,11 +58,12 @@ async def run(self, prompt: str) -> str: async def test_invocation_state_round_trip(client: Client): _RECEIVED.clear() + task_queue = f"test_invocation_state-{uuid4()}" plugin = StrandsPlugin(models={"recording": lambda: _RecordingModel()}) async with Worker( client, - task_queue="test_invocation_state", + task_queue=task_queue, workflows=[_InvocationStateWorkflow], plugins=[plugin], max_cached_workflows=0, @@ -71,7 +72,7 @@ async def test_invocation_state_round_trip(client: Client): _InvocationStateWorkflow.run, "hi", id=f"test_invocation_state_{uuid4()}", - task_queue="test_invocation_state", + task_queue=task_queue, ) # The serializable key crosses the activity boundary; the non-serializable diff --git a/tests/contrib/strands/test_mcp.py b/tests/contrib/strands/test_mcp.py index 0f989cd83..9849e6dda 100644 --- a/tests/contrib/strands/test_mcp.py +++ b/tests/contrib/strands/test_mcp.py @@ -52,7 +52,7 @@ async def run(self, prompt: str) -> str: async def test_mcp(client: Client): - task_queue = "test_mcp" + task_queue = f"test_mcp-{uuid4()}" plugin = StrandsPlugin( models={ "mock": lambda: MockModel( @@ -125,7 +125,7 @@ async def run(self, prompt: str) -> str: async def test_mcp_reuses_connection(client: Client): """Successive MCP tool calls reuse one cached worker-side connection.""" - task_queue = "test_mcp_reuses_connection" + task_queue = f"test_mcp_reuses_connection-{uuid4()}" # Count how often the worker opens a connection. One lazily-opened # connection serves the list-tools discovery and both tool calls (1); # reconnecting per call would make it more. @@ -206,7 +206,7 @@ async def run(self, prompt: str) -> str: async def test_mcp_connection_idle_timeout(client: Client): """A short idle timeout evicts the cached connection while the worker runs.""" - task_queue = "test_mcp_connection_idle_timeout" + task_queue = f"test_mcp_connection_idle_timeout-{uuid4()}" factory_calls = [0] def counting_factory() -> MCPClient: @@ -282,7 +282,7 @@ async def run(self, prompt: str) -> str: async def test_mcp_lists_tools_each_turn_when_uncached(client: Client): """With cache_tools=False the tool list is re-fetched on every model call.""" - task_queue = "test_mcp_lists_tools_each_turn_when_uncached" + task_queue = f"test_mcp_lists_tools_each_turn_when_uncached-{uuid4()}" plugin = StrandsPlugin( models={ "mock": lambda: MockModel( diff --git a/tests/contrib/strands/test_model.py b/tests/contrib/strands/test_model.py index 68d578e5c..c96d4c0d7 100644 --- a/tests/contrib/strands/test_model.py +++ b/tests/contrib/strands/test_model.py @@ -24,7 +24,7 @@ async def run(self, prompt: str) -> str: async def test_model(client: Client): - task_queue = "test_model" + task_queue = f"test_model-{uuid4()}" plugin = StrandsPlugin(models={"mock": lambda: MockModel(["Done!"])}) async with Worker( diff --git a/tests/contrib/strands/test_model_streaming.py b/tests/contrib/strands/test_model_streaming.py index 41f3ff2f1..1a0b84447 100644 --- a/tests/contrib/strands/test_model_streaming.py +++ b/tests/contrib/strands/test_model_streaming.py @@ -30,7 +30,7 @@ async def run(self, prompt: str) -> str: async def test_model_streaming(client: Client): - task_queue = "test_model_streaming" + task_queue = f"test_model_streaming-{uuid4()}" plugin = StrandsPlugin(models={"mock": lambda: MockModel(["Done!"])}) workflow_id = f"test_model_streaming_{uuid4()}" diff --git a/tests/contrib/strands/test_structured_output.py b/tests/contrib/strands/test_structured_output.py index 18c77c553..eafe2b364 100644 --- a/tests/contrib/strands/test_structured_output.py +++ b/tests/contrib/strands/test_structured_output.py @@ -33,7 +33,7 @@ async def run(self, prompt: str) -> PersonInfo: async def test_structured_output(client: Client): - task_queue = "test_structured_output" + task_queue = f"test_structured_output-{uuid4()}" plugin = StrandsPlugin( models={ "mock": lambda: MockModel( diff --git a/tests/contrib/strands/test_tool.py b/tests/contrib/strands/test_tool.py index 39985e2df..d13fcbec1 100644 --- a/tests/contrib/strands/test_tool.py +++ b/tests/contrib/strands/test_tool.py @@ -59,7 +59,7 @@ async def run(self, prompt: str) -> str: async def test_tool(client: Client, tmp_path: Path): - task_queue = "test_tool" + task_queue = f"test_tool-{uuid4()}" fixture = tmp_path / "greeting.txt" fixture.write_text("hello\n") diff --git a/tests/contrib/workflow_streams/test_workflow_streams.py b/tests/contrib/workflow_streams/test_workflow_streams.py index 12026ff1a..e7cedd038 100644 --- a/tests/contrib/workflow_streams/test_workflow_streams.py +++ b/tests/contrib/workflow_streams/test_workflow_streams.py @@ -2699,6 +2699,7 @@ async def test_subscribe_iterates_through_more_ready(client: Client) -> None: @pytest.mark.asyncio +@pytest.mark.requires_local_server async def test_cross_namespace_nexus_stream( client: Client, env: WorkflowEnvironment ) -> None: @@ -2722,8 +2723,7 @@ async def test_cross_namespace_nexus_stream( ) ) - handler_client = await Client.connect( - client.service_client.config.target_host, + handler_client = await env.connect_client( namespace=handler_ns, ) diff --git a/tests/helpers/trace.py b/tests/helpers/trace.py new file mode 100644 index 000000000..913bd27da --- /dev/null +++ b/tests/helpers/trace.py @@ -0,0 +1,111 @@ +"""Helpers for asserting indented trace hierarchies.""" + +from __future__ import annotations + +from collections.abc import Sequence +from dataclasses import dataclass, field + + +@dataclass +class TraceNode: + name: str + children: list[TraceNode] = field(default_factory=list) + + +def format_trace_hierarchy(roots: Sequence[TraceNode]) -> list[str]: + """Render a trace forest as indented lines.""" + lines: list[str] = [] + + def render(node: TraceNode, depth: int) -> None: + lines.append(" " * depth + node.name) + for child in node.children: + render(child, depth + 1) + + for root in roots: + render(root, 0) + return lines + + +def assert_trace_hierarchy( + actual: Sequence[TraceNode], expected: Sequence[str] +) -> None: + """Assert a trace hierarchy, allowing matching Start/Run siblings to swap. + + Activity execution can begin before the asynchronously published Start span + reaches an in-memory exporter. The two spans retain the same parent, so this + accepts only that sibling transposition and preserves all other ordering. + """ + actual_tree = TraceNode("", list(actual)) + expected_tree = _parse_trace(expected) + assert _nodes_match(actual_tree, expected_tree), ( + "Trace hierarchy differed.\n" + f"Actual:\n{chr(10).join(format_trace_hierarchy(actual))}\n" + f"Expected:\n{chr(10).join(expected)}" + ) + + +def _parse_trace(trace: Sequence[str]) -> TraceNode: + root = TraceNode("") + stack: list[tuple[int, TraceNode]] = [(-1, root)] + for line in trace: + name = line.lstrip() + indent = len(line) - len(name) + assert name and indent % 2 == 0, f"Invalid trace line: {line!r}" + depth = indent // 2 + while stack[-1][0] >= depth: + stack.pop() + assert depth == stack[-1][0] + 1, f"Invalid trace nesting: {line!r}" + node = TraceNode(name) + stack[-1][1].children.append(node) + stack.append((depth, node)) + return root + + +def _nodes_match(actual: TraceNode, expected: TraceNode) -> bool: + if actual.name != expected.name or len(actual.children) != len(expected.children): + return False + + index = 0 + while index < len(actual.children): + actual_child = actual.children[index] + expected_child = expected.children[index] + if _nodes_match(actual_child, expected_child): + index += 1 + continue + + if index + 1 == len(actual.children): + return False + actual_pair = actual.children[index : index + 2] + expected_pair = expected.children[index : index + 2] + if not _is_start_run_pair(*actual_pair) or not _is_start_run_pair( + *expected_pair + ): + return False + expected_by_name = {node.name: node for node in expected_pair} + if any(node.name not in expected_by_name for node in actual_pair): + return False + if not all( + _nodes_match(node, expected_by_name[node.name]) for node in actual_pair + ): + return False + index += 2 + + return True + + +def _is_start_run_pair(first: TraceNode, second: TraceNode) -> bool: + first_start = _start_run_suffix(first.name) + second_start = _start_run_suffix(second.name) + first_run = _run_suffix(first.name) + second_run = _run_suffix(second.name) + return (first_start is not None and first_start == second_run) or ( + second_start is not None and second_start == first_run + ) + + +def _start_run_suffix(name: str) -> str | None: + return name.removeprefix("Start") if name.startswith("Start") else None + + +def _run_suffix(name: str) -> str | None: + return name.removeprefix("Run") if name.startswith("Run") else None diff --git a/tests/nexus/test_dynamic_creation_of_user_handler_classes.py b/tests/nexus/test_dynamic_creation_of_user_handler_classes.py index f7306a46b..214e02ab9 100644 --- a/tests/nexus/test_dynamic_creation_of_user_handler_classes.py +++ b/tests/nexus/test_dynamic_creation_of_user_handler_classes.py @@ -10,6 +10,10 @@ from temporalio.worker import Worker from tests.helpers.nexus import make_nexus_endpoint_name +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server + @workflow.defn class MyWorkflow: diff --git a/tests/nexus/test_link_conversion.py b/tests/nexus/test_link_conversion.py index 4afe3367e..15fc8d77f 100644 --- a/tests/nexus/test_link_conversion.py +++ b/tests/nexus/test_link_conversion.py @@ -304,6 +304,19 @@ def test_link_conversion_workflow_to_link_and_back( url="temporal:///namespaces/ns/nexus-operations/op%2Fid//details", ), ), + ( + temporalio.api.common.v1.Link( + nexus_operation=temporalio.api.common.v1.Link.NexusOperation( + namespace="ns", + operation_id="op/id", + run_id="run/id", + ) + ), + nexusrpc.Link( + type=temporalio.api.common.v1.Link.NexusOperation.DESCRIPTOR.full_name, + url="temporal:///namespaces/ns/nexus-operations/op%2Fid/run%2Fid/details", + ), + ), ], ) def test_link_conversion_nexus_operation_to_link_and_back( @@ -342,6 +355,62 @@ def test_nexus_operation_link_with_unparseable_url_is_ignored(): assert temporalio.nexus._link_conversion.nexus_link_to_temporal_link(link) is None +@pytest.mark.parametrize( + ["link", "expected_link"], + [ + ( + nexusrpc.Link( + type=temporalio.api.common.v1.Link.Activity.DESCRIPTOR.full_name, + url="temporal:///namespaces/ns/activities/act-id/run-id/details", + ), + temporalio.api.common.v1.Link( + activity=temporalio.api.common.v1.Link.Activity( + namespace="ns", + activity_id="act-id", + run_id="run-id", + ), + ), + ), + ( + nexusrpc.Link( + type=temporalio.api.common.v1.Link.Activity.DESCRIPTOR.full_name, + url="temporal:///namespaces/ns%2F/activities/act-id%2F/run-id%3E/details", + ), + temporalio.api.common.v1.Link( + activity=temporalio.api.common.v1.Link.Activity( + namespace="ns/", + activity_id="act-id/", + run_id="run-id>", + ), + ), + ), + ], +) +def test_link_conversion_nexus_link_to_activity_link( + link: nexusrpc.Link, + expected_link: temporalio.api.common.v1.Link, +): + from_activity_link = temporalio.nexus._link_conversion.activity_link_to_nexus_link( + expected_link.activity + ) + assert link == from_activity_link + + from_temporal_link = temporalio.nexus._link_conversion.temporal_link_to_nexus_link( + expected_link + ) + assert link == from_temporal_link + + actual_activity = temporalio.nexus._link_conversion.nexus_link_to_activity_link( + link + ) + assert expected_link == actual_activity + + actual_temporal_link = ( + temporalio.nexus._link_conversion.nexus_link_to_temporal_link(link) + ) + assert expected_link == actual_temporal_link + + def test_link_conversion_utilities(): p2c = temporalio.nexus._link_conversion._event_type_pascal_case_to_constant_case c2p = temporalio.nexus._link_conversion._event_type_constant_case_to_pascal_case diff --git a/tests/nexus/test_signal_link_propagation.py b/tests/nexus/test_link_propagation.py similarity index 84% rename from tests/nexus/test_signal_link_propagation.py rename to tests/nexus/test_link_propagation.py index daff71c78..a7b78426c 100644 --- a/tests/nexus/test_signal_link_propagation.py +++ b/tests/nexus/test_link_propagation.py @@ -1,14 +1,14 @@ -"""Unit tests for Nexus signal-backlink propagation. +"""Unit tests for Nexus link propagation. -These exercise the in/out link propagation that happens when a Nexus operation handler issues a -signal, signal-with-start, or start-workflow RPC, against a mocked workflow service. -The corresponding end-to-end behavior requires a real server with EnableCHASMSignalBacklinks=true and is therefore -and is therefore not covered here. +These exercise link propagation when a Nexus operation handler signals or starts a +workflow or activity against a mocked workflow service. End-to-end signal backlinks +require a server with EnableCHASMSignalBacklinks enabled and are not covered here. """ from __future__ import annotations from collections.abc import Generator +from datetime import timedelta from typing import Any from unittest import mock @@ -32,9 +32,11 @@ import temporalio.converter import temporalio.nexus._link_conversion import temporalio.nexus._operation_context +import temporalio.nexus._token from temporalio.client._impl import _ClientImpl from temporalio.client._interceptor import ( SignalWorkflowInput, + StartActivityInput, StartWorkflowInput, ) from temporalio.nexus._operation_context import _TemporalStartOperationContext @@ -84,7 +86,7 @@ def nexus_ctx() -> Generator[_TemporalStartOperationContext]: operation="op", headers={}, request_id="req-id", - callback_url=None, + callback_url="https://callback.example", inbound_links=[inbound], callback_headers={}, task_cancellation=_NexusTaskCancellation(), @@ -163,6 +165,30 @@ def _start_input(start_signal: str | None = None) -> StartWorkflowInput: ) +def _start_activity_input() -> StartActivityInput: + return StartActivityInput( + activity_type="TestActivity", + args=[], + id="activity-target", + task_queue="tq", + result_type=None, + schedule_to_close_timeout=None, + start_to_close_timeout=timedelta(seconds=10), + schedule_to_start_timeout=None, + heartbeat_timeout=None, + id_reuse_policy=temporalio.common.ActivityIDReusePolicy.ALLOW_DUPLICATE, + id_conflict_policy=temporalio.common.ActivityIDConflictPolicy.FAIL, + retry_policy=None, + priority=temporalio.common.Priority.default, + search_attributes=None, + summary=None, + start_delay=None, + headers={}, + rpc_metadata={}, + rpc_timeout=None, + ) + + def _outbound_link_urls(ctx: Any) -> list[str]: return [link.url for link in ctx.nexus_context.outbound_links] @@ -433,7 +459,7 @@ async def test_backing_workflow_start_sets_on_conflict_options_without_duplicati # also re-add the context's request links. start_input = _start_input() start_input.links = [_inbound_nexus_link()] - with temporalio.nexus._operation_context._nexus_backing_workflow_start_context(): + with temporalio.nexus._operation_context._nexus_backing_start_context(): await impl.start_workflow(start_input) sent = workflow_service.start_workflow_execution.call_args.args[0] @@ -459,6 +485,64 @@ async def test_start_outside_nexus_context_leaves_on_conflict_options_unset() -> assert not sent.HasField("on_conflict_options") +# ── activity start ────────────────────────────────────────────────────────────────────────── + + +@pytest.mark.usefixtures("nexus_ctx") +async def test_activity_start_forwards_inbound_links() -> None: + impl = _make_client_impl(mock.MagicMock()) + + req = await impl._build_start_activity_execution_request(_start_activity_input()) + + assert len(req.links) == 1 + assert req.links[0] == _inbound_nexus_link() + assert req.request_id == "req-id" + assert len(req.completion_callbacks) == 0 + + +async def test_activity_start_without_server_link_synthesizes_backlink( + nexus_ctx: _TemporalStartOperationContext, +) -> None: + workflow_service = mock.MagicMock() + workflow_service.start_activity_execution = mock.AsyncMock( + return_value=temporalio.api.workflowservice.v1.StartActivityExecutionResponse( + run_id="activity-run", + ) + ) + impl = _make_client_impl(workflow_service) + + await impl.start_activity(_start_activity_input()) + + assert len(nexus_ctx.nexus_context.outbound_links) == 1 + link = nexus_ctx.nexus_context.outbound_links[0] + assert link.url == ( + "temporal:///namespaces/test-namespace/" + "activities/activity-target/activity-run/details" + ) + assert link.type == temporalio.api.common.v1.Link.Activity.DESCRIPTOR.full_name + + +@pytest.mark.usefixtures("nexus_ctx") +async def test_backing_activity_start_gets_nexus_request_fields() -> None: + impl = _make_client_impl(mock.MagicMock()) + + with temporalio.nexus._operation_context._nexus_backing_start_context(): + req = await impl._build_start_activity_execution_request( + _start_activity_input() + ) + + assert len(req.links) == 0 + assert req.request_id == "req-id" + assert len(req.completion_callbacks) == 1 + operation_token = temporalio.nexus._token.OperationToken.decode( + req.completion_callbacks[0].nexus.header["nexus-operation-token"] + ) + assert operation_token.type is temporalio.nexus._token.OperationTokenType.ACTIVITY + assert operation_token.namespace == NAMESPACE + assert operation_token.activity_id == "activity-target" + assert list(req.completion_callbacks[0].links) == [_inbound_nexus_link()] + + # ── handler-level: backlinks land on the StartOperationResponse ────────────────────────────── # A response link that a handler stashes on ctx.outbound_links, mimicking what a signal RPC inside diff --git a/tests/nexus/test_nexus_client_updates.py b/tests/nexus/test_nexus_client_updates.py index f63d5482c..97dd251da 100644 --- a/tests/nexus/test_nexus_client_updates.py +++ b/tests/nexus/test_nexus_client_updates.py @@ -3,6 +3,7 @@ import uuid import nexusrpc +import pytest from nexusrpc.handler import StartOperationContext, service_handler, sync_operation import temporalio.nexus @@ -11,6 +12,10 @@ from temporalio.testing import WorkflowEnvironment from temporalio.worker import Worker +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server + @nexusrpc.service class ClientTestService: @@ -48,9 +53,7 @@ async def test_nexus_client_updates_when_worker_client_changes( """Test that Nexus operations get the updated client when worker.client is changed.""" # Create a second client (simulating a new client after cert rotation) # Must use the same runtime - client2 = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + client2 = await env.connect_client( data_converter=env.client.data_converter, runtime=env.client.service_client.config.runtime, ) diff --git a/tests/nexus/test_nexus_type_errors.py b/tests/nexus/test_nexus_type_errors.py index 1486f9791..946e34035 100644 --- a/tests/nexus/test_nexus_type_errors.py +++ b/tests/nexus/test_nexus_type_errors.py @@ -11,7 +11,7 @@ import nexusrpc import temporalio.nexus -from temporalio import workflow +from temporalio import activity, workflow from temporalio.client import Client, NexusOperationHandle from temporalio.nexus import TemporalOperationStartHandlerFunc from temporalio.service import ServiceClient @@ -71,6 +71,74 @@ async def run( pass +@activity.defn +async def my_no_arg_activity() -> None: + pass + + +@activity.defn +async def my_one_arg_activity(_input: MyInput) -> None: + pass + + +@activity.defn +async def my_two_arg_activity(_input: MyInput, _arg2: int) -> None: + pass + + +@activity.defn +async def my_three_arg_activity(_input: MyInput, _arg2: int, _arg3: int) -> None: + pass + + +@activity.defn +async def my_four_arg_activity( + _input: MyInput, _arg2: int, _arg3: int, _arg4: int +) -> None: + pass + + +@activity.defn +async def my_five_arg_activity( + _input: MyInput, _arg2: int, _arg3: int, _arg4: int, _arg5: int +) -> None: + pass + + +@activity.defn +def my_sync_no_arg_activity() -> None: + pass + + +@activity.defn +def my_sync_one_arg_activity(_input: MyInput) -> None: + pass + + +@activity.defn +def my_sync_two_arg_activity(_input: MyInput, _arg2: int) -> None: + pass + + +@activity.defn +def my_sync_three_arg_activity(_input: MyInput, _arg2: int, _arg3: int) -> None: + pass + + +@activity.defn +def my_sync_four_arg_activity( + _input: MyInput, _arg2: int, _arg3: int, _arg4: int +) -> None: + pass + + +@activity.defn +def my_sync_five_arg_activity( + _input: MyInput, _arg2: int, _arg3: int, _arg4: int, _arg5: int +) -> None: + pass + + @nexusrpc.service class MyService: my_sync_operation: nexusrpc.Operation[MyInput, MyOutput] @@ -105,8 +173,9 @@ async def my_temporal_operation( input: int, ) -> temporalio.nexus.TemporalOperationResult[None]: """ - Typed proc workflow starts from a generic Temporal Nexus operation handler - infer TemporalOperationResult[None] for 0 to 5 workflow parameters. + Typed proc workflow and activity starts from a generic Temporal Nexus + operation handler infer TemporalOperationResult[None] for 0 to 5 + workflow or activity parameters. """ if input == 0: result_0: temporalio.nexus.TemporalOperationResult[ @@ -154,6 +223,147 @@ async def my_temporal_operation( id="proc-5", ) return result_5 + + # Typed activity starts infer TemporalOperationResult[None] for 0 to 5 + # activity parameters. Activities require a start_to_close_timeout. + if input == 6: + activity_result_0: temporalio.nexus.TemporalOperationResult[ + None + ] = await client.start_activity( + my_no_arg_activity, + id="activity-0", + start_to_close_timeout=timedelta(seconds=5), + ) + return activity_result_0 + if input == 7: + activity_result_1: temporalio.nexus.TemporalOperationResult[ + None + ] = await client.start_activity( + my_one_arg_activity, + MyInput(), + id="activity-1", + start_to_close_timeout=timedelta(seconds=5), + ) + return activity_result_1 + if input == 8: + activity_result_2: temporalio.nexus.TemporalOperationResult[ + None + ] = await client.start_activity( + my_two_arg_activity, + args=[MyInput(), 2], + id="activity-2", + start_to_close_timeout=timedelta(seconds=5), + ) + return activity_result_2 + if input == 9: + activity_result_3: temporalio.nexus.TemporalOperationResult[ + None + ] = await client.start_activity( + my_three_arg_activity, + args=[MyInput(), 2, 3], + id="activity-3", + start_to_close_timeout=timedelta(seconds=5), + ) + return activity_result_3 + if input == 10: + activity_result_4: temporalio.nexus.TemporalOperationResult[ + None + ] = await client.start_activity( + my_four_arg_activity, + args=[MyInput(), 2, 3, 4], + id="activity-4", + start_to_close_timeout=timedelta(seconds=5), + ) + return activity_result_4 + if input == 11: + activity_result_5: temporalio.nexus.TemporalOperationResult[ + None + ] = await client.start_activity( + my_five_arg_activity, + args=[MyInput(), 2, 3, 4, 5], + id="activity-5", + start_to_close_timeout=timedelta(seconds=5), + ) + return activity_result_5 + if input == 12: + sync_activity_result_0: temporalio.nexus.TemporalOperationResult[ + None + ] = await client.start_activity( + my_sync_no_arg_activity, + id="sync-activity-0", + start_to_close_timeout=timedelta(seconds=5), + ) + return sync_activity_result_0 + if input == 13: + sync_activity_result_1: temporalio.nexus.TemporalOperationResult[ + None + ] = await client.start_activity( + my_sync_one_arg_activity, + MyInput(), + id="sync-activity-1", + start_to_close_timeout=timedelta(seconds=5), + ) + return sync_activity_result_1 + if input == 14: + sync_activity_result_2: temporalio.nexus.TemporalOperationResult[ + None + ] = await client.start_activity( + my_sync_two_arg_activity, + args=[MyInput(), 2], + id="sync-activity-2", + start_to_close_timeout=timedelta(seconds=5), + ) + return sync_activity_result_2 + if input == 15: + sync_activity_result_3: temporalio.nexus.TemporalOperationResult[ + None + ] = await client.start_activity( + my_sync_three_arg_activity, + args=[MyInput(), 2, 3], + id="sync-activity-3", + start_to_close_timeout=timedelta(seconds=5), + ) + return sync_activity_result_3 + if input == 16: + sync_activity_result_4: temporalio.nexus.TemporalOperationResult[ + None + ] = await client.start_activity( + my_sync_four_arg_activity, + args=[MyInput(), 2, 3, 4], + id="sync-activity-4", + start_to_close_timeout=timedelta(seconds=5), + ) + return sync_activity_result_4 + if input == 17: + sync_activity_result_5: temporalio.nexus.TemporalOperationResult[ + None + ] = await client.start_activity( + my_sync_five_arg_activity, + args=[MyInput(), 2, 3, 4, 5], + id="sync-activity-5", + start_to_close_timeout=timedelta(seconds=5), + ) + return sync_activity_result_5 + if input == 18: + string_activity_result: temporalio.nexus.TemporalOperationResult[ + None + ] = await client.start_activity( + "string-activity", + id="string-activity", + result_type=type(None), + start_to_close_timeout=timedelta(seconds=5), + ) + return string_activity_result + if input == 19: + # assert-type-error-pyright: 'No overloads for "start_activity" match' + return await client.start_activity( # type: ignore + my_one_arg_activity, + # assert-type-error-pyright: 'Argument of type .+ cannot be assigned to parameter' + "wrong-input-type", # type: ignore + id="activity-wrong-input", + start_to_close_timeout=timedelta(seconds=5), + ) + # assert-type-error-pyright: 'No overloads for "start_workflow" match' return await client.start_workflow( # type: ignore MyOneArgProcWorkflow.run, diff --git a/tests/nexus/test_nexus_worker_shutdown.py b/tests/nexus/test_nexus_worker_shutdown.py index bd9063237..2a94027d5 100644 --- a/tests/nexus/test_nexus_worker_shutdown.py +++ b/tests/nexus/test_nexus_worker_shutdown.py @@ -23,6 +23,10 @@ make_nexus_endpoint_name, ) +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server + @nexusrpc.service class ShutdownTestService: diff --git a/tests/nexus/test_operation_token.py b/tests/nexus/test_operation_token.py index 385f4f872..58d4a7859 100644 --- a/tests/nexus/test_operation_token.py +++ b/tests/nexus/test_operation_token.py @@ -8,6 +8,7 @@ OperationToken, OperationTokenType, WorkflowHandle, + _base64url_decode_no_padding, ) @@ -36,6 +37,39 @@ def test_operation_token_encode_decode_round_trip(): ) +def test_operation_token_activity_encode_decode_round_trip(): + token = OperationToken( + type=OperationTokenType.ACTIVITY, + namespace="default", + activity_id="activity-id", + run_id="run-id", + version=0, + ).encode() + + assert "=" not in token + assert OperationToken.decode(token) == OperationToken( + type=OperationTokenType.ACTIVITY, + namespace="default", + activity_id="activity-id", + run_id="run-id", + version=0, + ) + + +def test_operation_token_activity_encode_uses_activity_id_and_omits_workflow_id(): + token = OperationToken( + type=OperationTokenType.ACTIVITY, + namespace="default", + activity_id="activity-id", + ).encode() + + assert json.loads(_base64url_decode_no_padding(token)) == { + "t": 2, + "ns": "default", + "aid": "activity-id", + } + + def test_workflow_handle_to_from_token_round_trip(): handle = WorkflowHandle[str](namespace="default", workflow_id="workflow-id") @@ -80,6 +114,58 @@ def test_workflow_handle_to_from_token_round_trip(): version=0, ), ), + # Activity tokens + ( + _encode_json_token( + {"t": 2, "ns": "default", "aid": "activity-id", "rid": "run-id"} + ), + OperationToken( + type=OperationTokenType.ACTIVITY, + namespace="default", + activity_id="activity-id", + run_id="run-id", + ), + ), + ( + _encode_json_token( + {"t": 2, "ns": "", "aid": "activity-id", "rid": "run-id"} + ), + OperationToken( + type=OperationTokenType.ACTIVITY, + namespace="", + activity_id="activity-id", + run_id="run-id", + ), + ), + ( + _encode_json_token( + { + "t": 2, + "ns": "default", + "aid": "activity-id", + "rid": "run-id", + "v": None, + } + ), + OperationToken( + type=OperationTokenType.ACTIVITY, + namespace="default", + activity_id="activity-id", + run_id="run-id", + ), + ), + ( + _encode_json_token( + {"t": 2, "ns": "default", "aid": "activity-id", "rid": "run-id", "v": 0} + ), + OperationToken( + type=OperationTokenType.ACTIVITY, + namespace="default", + activity_id="activity-id", + run_id="run-id", + version=0, + ), + ), ], ) def test_operation_token_decode_accepts_valid_tokens( @@ -110,7 +196,7 @@ def test_operation_token_decode_accepts_valid_tokens( ), ( _encode_json_token({"t": 1, "ns": "default"}), - "expected workflow id to be a string", + "expected non-empty workflow id for token type `WORKFLOW`", ), ( _encode_json_token({"t": 1, "ns": "default", "wid": 123}), @@ -134,6 +220,41 @@ def test_operation_token_decode_accepts_valid_tokens( ), "expected version to be an int or null", ), + # Activity tokens + ( + _encode_json_token({"t": 2, "ns": "default"}), + "expected non-empty activity id for token type `ACTIVITY`", + ), + ( + _encode_json_token({"t": 2, "ns": "default", "aid": ""}), + "expected non-empty activity id for token type `ACTIVITY`", + ), + ( + _encode_json_token({"t": 2, "ns": "default", "aid": 123}), + "expected activity id to be a string", + ), + ( + _encode_json_token( + {"t": 2, "ns": "default", "aid": "activity-id", "rid": 123} + ), + "expected run_id to be a string", + ), + ( + _encode_json_token({"t": 2, "aid": "activity-id", "rid": "run-id"}), + "expected namespace to be a string", + ), + ( + _encode_json_token( + { + "t": 2, + "ns": "default", + "aid": "activity-id", + "rid": "run-id", + "v": "0", + } + ), + "expected version to be an int or null", + ), ], ) def test_operation_token_decode_rejects_invalid_tokens(token: str, message: str): diff --git a/tests/nexus/test_signal_link_propagation_e2e.py b/tests/nexus/test_signal_link_propagation_e2e.py index e489ad8a7..9e51d4b93 100644 --- a/tests/nexus/test_signal_link_propagation_e2e.py +++ b/tests/nexus/test_signal_link_propagation_e2e.py @@ -56,6 +56,10 @@ workflow_event_link_event_type, ) +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server + EventType = temporalio.api.enums.v1.EventType diff --git a/tests/nexus/test_standalone_operations.py b/tests/nexus/test_standalone_operations.py index 8193ba7ba..26a8316b4 100644 --- a/tests/nexus/test_standalone_operations.py +++ b/tests/nexus/test_standalone_operations.py @@ -71,6 +71,11 @@ # --------------------------------------------------------------------------- +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server + + @dataclass class EchoInput: value: str @@ -308,8 +313,10 @@ async def test_started_workflow_has_link_to_standalone_nexus_operation( workflow_history = await _assert_workflow_started_with_nexus_operation_link( client, workflow_id, handle ) - await _assert_nexus_operation_has_link_to_started_workflow( - client, workflow_history, handle + await assert_eventually( + lambda: _assert_nexus_operation_has_link_to_started_workflow( + client, workflow_history, handle + ) ) workflow_handle = client.get_workflow_handle(workflow_id) diff --git a/tests/nexus/test_temporal_extstore.py b/tests/nexus/test_temporal_extstore.py new file mode 100644 index 000000000..f23a69259 --- /dev/null +++ b/tests/nexus/test_temporal_extstore.py @@ -0,0 +1,274 @@ +"""Integration tests for external storage with Nexus task processing. + +Mirrors sdk-typescript's test-integration-extstore-nexus. A caller workflow +invokes a Nexus operation whose input and/or result is large enough to be +offloaded to external storage. Verifies that the offloaded input is retrieved +before the handler runs, that a large synchronous result is offloaded when +completing the task (and retrieved by the caller), and that a transient +storage-driver failure fails the Nexus task retryably and then recovers. +""" + +from __future__ import annotations + +import dataclasses +import uuid +from collections.abc import Sequence +from datetime import timedelta +from typing import Any + +import nexusrpc +import pytest +from nexusrpc.handler import ( + StartOperationContext, + service_handler, + sync_operation, +) + +import temporalio.converter +from temporalio import workflow +from temporalio.api.common.v1 import Payload +from temporalio.client import Client, WorkflowFailureError +from temporalio.converter import ( + ExternalStorage, + StorageDriverClaim, + StorageDriverRetrieveContext, + StorageDriverStoreContext, +) +from temporalio.exceptions import ApplicationError, NexusOperationError +from temporalio.testing import WorkflowEnvironment +from temporalio.types import MethodAsyncSingleParam +from temporalio.worker import UnsandboxedWorkflowRunner, Worker +from tests.helpers.nexus import make_nexus_endpoint_name +from tests.test_extstore import InMemoryTestDriver + +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server + +PAYLOAD_SIZE = 4096 +PAYLOAD_SIZE_THRESHOLD = 1024 +_STORE_FAILURE_MESSAGE = "external storage store failed" + + +@nexusrpc.service +class ExtStoreNexusService: + size_op: nexusrpc.Operation[str, int] + big_result_op: nexusrpc.Operation[int, str] + + +@service_handler(service=ExtStoreNexusService) +class ExtStoreNexusServiceHandler: + @sync_operation + async def size_op(self, _ctx: StartOperationContext, data: str) -> int: + return len(data) + + @sync_operation + async def big_result_op(self, _ctx: StartOperationContext, size: int) -> str: + return "x" * size + + +@workflow.defn +class SizeOpCallerWorkflow: + """Calls ``size_op`` with a large input (offloaded) and returns its length.""" + + @workflow.run + async def run(self, task_queue: str) -> int: + nexus_client = workflow.create_nexus_client( + service=ExtStoreNexusService, + endpoint=make_nexus_endpoint_name(task_queue), + ) + return await nexus_client.execute_operation( + ExtStoreNexusService.size_op, "x" * PAYLOAD_SIZE + ) + + +@workflow.defn +class BigResultOpCallerWorkflow: + """Calls ``big_result_op`` (whose result is offloaded) and returns its length.""" + + @workflow.run + async def run(self, task_queue: str) -> int: + nexus_client = workflow.create_nexus_client( + service=ExtStoreNexusService, + endpoint=make_nexus_endpoint_name(task_queue), + ) + result = await nexus_client.execute_operation( + ExtStoreNexusService.big_result_op, PAYLOAD_SIZE + ) + return len(result) + + +class TransientFailureDriver(InMemoryTestDriver): + """In-memory driver that fails the first store and/or retrieve call, then + behaves normally. Simulates a transient storage-driver outage.""" + + def __init__( + self, + *, + fail_first_store: bool = False, + fail_first_retrieve: bool = False, + driver_name: str = "test-driver", + ): + super().__init__(driver_name=driver_name) + self._fail_first_store = fail_first_store + self._fail_first_retrieve = fail_first_retrieve + self.store_attempts = 0 + self.retrieve_attempts = 0 + + async def store( + self, + context: StorageDriverStoreContext, + payloads: Sequence[Payload], + ) -> list[StorageDriverClaim]: + self.store_attempts += 1 + if self._fail_first_store and self.store_attempts == 1: + raise RuntimeError("transient store failure") + return await super().store(context, payloads) + + async def retrieve( + self, + context: StorageDriverRetrieveContext, + claims: Sequence[StorageDriverClaim], + ) -> list[Payload]: + self.retrieve_attempts += 1 + if self._fail_first_retrieve and self.retrieve_attempts == 1: + raise RuntimeError("transient retrieve failure") + return await super().retrieve(context, claims) + + +def _client_with_extstore( + env: WorkflowEnvironment, driver: InMemoryTestDriver +) -> Client: + config = env.client.config() + config["data_converter"] = dataclasses.replace( + temporalio.converter.default(), + external_storage=ExternalStorage( + drivers=[driver], + payload_size_threshold=PAYLOAD_SIZE_THRESHOLD, + ), + ) + return Client(**config) + + +async def _run_caller( + env: WorkflowEnvironment, + driver: InMemoryTestDriver, + workflow_run: MethodAsyncSingleParam[Any, str, int], +) -> int: + client = _client_with_extstore(env, driver) + task_queue = str(uuid.uuid4()) + async with Worker( + client, + task_queue=task_queue, + workflows=[SizeOpCallerWorkflow, BigResultOpCallerWorkflow], + nexus_service_handlers=[ExtStoreNexusServiceHandler()], + workflow_runner=UnsandboxedWorkflowRunner(), + ): + await env.create_nexus_endpoint( + make_nexus_endpoint_name(task_queue), task_queue + ) + return await client.execute_workflow( + workflow_run, + task_queue, + id=str(uuid.uuid4()), + task_queue=task_queue, + execution_timeout=timedelta(seconds=30), + ) + + +def _cause_chain(err: BaseException) -> list[BaseException]: + chain: list[BaseException] = [] + e: BaseException | None = err + while e is not None: + chain.append(e) + e = e.__cause__ + return chain + + +async def test_nexus_operation_input_offloaded_and_retrieved(env: WorkflowEnvironment): + """The offloaded operation input is retrieved before the handler runs.""" + if env.supports_time_skipping: + pytest.skip("Nexus tests don't work with the Java test server") + + driver = InMemoryTestDriver() + result = await _run_caller(env, driver, SizeOpCallerWorkflow.run) + + assert result == PAYLOAD_SIZE + assert driver._store_calls >= 1 + assert driver._retrieve_calls >= 1 + + +async def test_nexus_operation_sync_result_offloaded_and_retrieved( + env: WorkflowEnvironment, +): + """A large synchronous result is offloaded and retrieved by the caller.""" + if env.supports_time_skipping: + pytest.skip("Nexus tests don't work with the Java test server") + + driver = InMemoryTestDriver() + result = await _run_caller(env, driver, BigResultOpCallerWorkflow.run) + + assert result == PAYLOAD_SIZE + assert driver._store_calls >= 1 + assert driver._retrieve_calls >= 1 + + +async def test_nexus_operation_transient_retrieve_failure_recovers( + env: WorkflowEnvironment, +): + """A transient retrieve failure fails the task retryably; it then recovers.""" + if env.supports_time_skipping: + pytest.skip("Nexus tests don't work with the Java test server") + + driver = TransientFailureDriver(fail_first_retrieve=True) + result = await _run_caller(env, driver, SizeOpCallerWorkflow.run) + + assert result == PAYLOAD_SIZE + assert driver.retrieve_attempts >= 2 + + +async def test_nexus_operation_transient_store_failure_recovers( + env: WorkflowEnvironment, +): + """A transient store failure fails the task retryably; it then recovers.""" + if env.supports_time_skipping: + pytest.skip("Nexus tests don't work with the Java test server") + + driver = TransientFailureDriver(fail_first_store=True) + result = await _run_caller(env, driver, BigResultOpCallerWorkflow.run) + + assert result == PAYLOAD_SIZE + assert driver.store_attempts >= 2 + + +class PermanentFailStoreDriver(InMemoryTestDriver): + """Store always fails non-retryably, so the Nexus operation fails permanently.""" + + async def store( + self, + context: StorageDriverStoreContext, + payloads: Sequence[Payload], + ) -> list[StorageDriverClaim]: + raise ApplicationError(_STORE_FAILURE_MESSAGE, non_retryable=True) + + +async def test_nexus_operation_store_failure_fails_operation( + env: WorkflowEnvironment, +): + """A non-retryable store failure fails the operation and surfaces the driver + error to the caller (deterministically, with no retries).""" + if env.supports_time_skipping: + pytest.skip("Nexus tests don't work with the Java test server") + + driver = PermanentFailStoreDriver() + with pytest.raises(WorkflowFailureError) as exc_info: + await _run_caller(env, driver, BigResultOpCallerWorkflow.run) + + causes = _cause_chain(exc_info.value) + assert [type(c) for c in causes] == [ + WorkflowFailureError, + NexusOperationError, + nexusrpc.HandlerError, + ApplicationError, + ] + assert _STORE_FAILURE_MESSAGE in str(causes[-1]) diff --git a/tests/nexus/test_temporal_operation.py b/tests/nexus/test_temporal_operation.py index c97792c8d..85948deb3 100644 --- a/tests/nexus/test_temporal_operation.py +++ b/tests/nexus/test_temporal_operation.py @@ -1,28 +1,57 @@ import asyncio import uuid from dataclasses import dataclass +from datetime import timedelta import nexusrpc import pytest from nexusrpc import HandlerErrorType, Operation, service -from nexusrpc.handler import operation_handler, service_handler +from nexusrpc.handler import ( + CancelOperationContext, + OperationTaskCancellation, + operation_handler, + service_handler, +) from typing_extensions import override import temporalio.exceptions -from temporalio import nexus, workflow -from temporalio.client import Client, WorkflowExecutionStatus, WorkflowFailureError -from temporalio.common import NexusOperationExecutionStatus, WorkflowIDConflictPolicy +from temporalio import activity, nexus, workflow +from temporalio.api.activity.v1 import ActivityExecutionInfo +from temporalio.api.common.v1 import Link +from temporalio.client import ( + ActivityExecutionStatus, + Client, + NexusOperationFailureError, + WorkflowExecutionStatus, + WorkflowFailureError, +) +from temporalio.common import ( + NexusOperationExecutionStatus, + RetryPolicy, + WorkflowIDConflictPolicy, +) from temporalio.nexus._token import OperationToken, OperationTokenType from temporalio.testing import WorkflowEnvironment from temporalio.worker import Worker from tests.helpers import EventType, assert_event_subsequence, assert_eventually -from tests.helpers.nexus import make_nexus_endpoint_name +from tests.helpers.nexus import ( + assert_links_match, + expected_nexus_operation_link, + make_nexus_endpoint_name, +) + +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server @dataclass class Input: value: str task_queue: str + update_value: str = "" + update_id: str = "" + expect_sync_response: bool = False def test_temporal_operation_result_validates_single_result_kind() -> None: @@ -54,6 +83,29 @@ async def run(self, input: Input) -> str: return input.value +@activity.defn +async def echo_activity(input: Input) -> str: + return input.value + + +@activity.defn +async def raise_error_activity() -> None: + raise temporalio.exceptions.ApplicationError( + "test-activity-error-message", + type="test-activity-error-type", + non_retryable=True, + ) + + +@activity.defn +async def wait_for_cancel_activity() -> None: + # Heartbeat in a loop so the activity receives cancellation. Letting the + # resulting CancelledError bubble out transitions the activity to CANCELED. + while True: + await asyncio.sleep(0.3) + activity.heartbeat() + + @service class TestService: echo: Operation[Input, str] @@ -63,6 +115,13 @@ class TestService: retry_after_failed_start: Operation[Input, str] sync_result: Operation[Input, str] custom_cancel: Operation[str, None] + update_op: Operation[Input, str] + echo_activity: Operation[Input, str] + error_activity: Operation[Input, None] + blocking_activity: Operation[str, None] + custom_cancel_activity: Operation[str, None] + double_start_activity: Operation[Input, None] + mixed_start: Operation[Input, None] @service_handler(service=TestService) @@ -72,6 +131,8 @@ class TestServiceHandler: def __init__(self) -> None: self.started_custom_cancel_workflow = asyncio.Event() + self.started_custom_cancel_activity = asyncio.Event() + self.custom_cancel_activity_called = asyncio.Event() @nexus.temporal_operation async def echo( @@ -216,6 +277,147 @@ async def cancel_workflow_run( return CustomCancelNexusOpHandler() + @nexus.temporal_operation + async def update_op( + self, + _ctx: nexus.TemporalStartOperationContext, + client: nexus.TemporalNexusClient, + input: Input, + ) -> nexus.TemporalOperationResult[str]: + # input.value carries the target workflow_id, input.update_value has actual update + return await client.start_workflow_update( + input.value, + UpdatableWorkflow.do_update, + input.update_value, + update_id=input.update_id, + ) + + @nexus.temporal_operation + async def echo_activity( + self, + _ctx: nexus.TemporalStartOperationContext, + client: nexus.TemporalNexusClient, + input: Input, + ) -> nexus.TemporalOperationResult[str]: + return await client.start_activity( + echo_activity, + input, + id=f"echo_activity-{uuid.uuid4()}", + start_to_close_timeout=timedelta(seconds=5), + ) + + @nexus.temporal_operation + async def error_activity( + self, + _ctx: nexus.TemporalStartOperationContext, + client: nexus.TemporalNexusClient, + _input: Input, + ) -> nexus.TemporalOperationResult[None]: + # The activity raises immediately. With a single permitted attempt it + # fails the backing activity, which in turn fails the Nexus operation. + return await client.start_activity( + raise_error_activity, + id=f"error_activity-{uuid.uuid4()}", + start_to_close_timeout=timedelta(seconds=5), + retry_policy=RetryPolicy(maximum_attempts=1), + ) + + @nexus.temporal_operation + async def blocking_activity( + self, + _ctx: nexus.TemporalStartOperationContext, + client: nexus.TemporalNexusClient, + input: str, + ) -> nexus.TemporalOperationResult[None]: + return await client.start_activity( + wait_for_cancel_activity, + id=input, + start_to_close_timeout=timedelta(seconds=30), + heartbeat_timeout=timedelta(seconds=1), + ) + + @nexus.temporal_operation + async def double_start_activity( + self, + _ctx: nexus.TemporalStartOperationContext, + client: nexus.TemporalNexusClient, + _input: Input, + ) -> nexus.TemporalOperationResult[None]: + # Keep the first activity running so its callback cannot race the + # handler error raised by the second start. + await client.start_activity( + wait_for_cancel_activity, + id=f"double-start-activity-{uuid.uuid4()}", + start_to_close_timeout=timedelta(seconds=30), + heartbeat_timeout=timedelta(seconds=1), + ) + await client.start_activity( + wait_for_cancel_activity, + id=f"double-start-activity-{uuid.uuid4()}", + start_to_close_timeout=timedelta(seconds=30), + heartbeat_timeout=timedelta(seconds=1), + ) + return nexus.TemporalOperationResult.sync(None) + + @nexus.temporal_operation + async def mixed_start( + self, + _ctx: nexus.TemporalStartOperationContext, + client: nexus.TemporalNexusClient, + input: Input, + ) -> nexus.TemporalOperationResult[None]: + # Starting a workflow reserves the single async start, so the subsequent + # start_activity must hit the same guard and raise a BAD_REQUEST error. + await client.start_workflow( + EchoWorkflow.run, input, id=f"mixed-start-{uuid.uuid4()}" + ) + await client.start_activity( + echo_activity, + input, + id=f"mixed-start-{uuid.uuid4()}", + start_to_close_timeout=timedelta(seconds=5), + ) + return nexus.TemporalOperationResult.sync(None) + + @operation_handler + def custom_cancel_activity(self) -> nexus.TemporalOperationHandler[str, None]: + started = self.started_custom_cancel_activity + cancel_called = self.custom_cancel_activity_called + + class CustomCancelActivityNexusOpHandler( + nexus.TemporalOperationHandler[str, None] + ): + @override + async def start_operation( + self, + ctx: nexus.TemporalStartOperationContext, + client: nexus.TemporalNexusClient, + input: str, + ) -> nexus.TemporalOperationResult[None]: + result = await client.start_activity( + wait_for_cancel_activity, + id=input, + start_to_close_timeout=timedelta(seconds=30), + heartbeat_timeout=timedelta(seconds=1), + ) + started.set() + return result + + @override + async def cancel_activity( + self, + ctx: nexus.TemporalCancelOperationContext, + options: nexus.CancelActivityOptions, + ): + # record that the custom override ran + cancel_called.set() + + # get a handle to the activity and cancel it + handle = nexus.client().get_activity_handle(options.activity_id) + await handle.cancel() + + return CustomCancelActivityNexusOpHandler() + @workflow.defn class EchoWorkflowCaller: @@ -258,6 +460,357 @@ async def test_temporal_operation_start_workflow( ) +async def test_temporal_operation_update_workflow( + client: Client, env: WorkflowEnvironment +) -> None: + if ( + env.supports_time_skipping + ): # time skipping server uses different dynamic configs + pytest.skip("Update workflow tests don't work with time-skipping server") + task_queue = str(uuid.uuid4()) + endpoint_name = make_nexus_endpoint_name(task_queue) + await env.create_nexus_endpoint(endpoint_name, task_queue) + async with Worker( + env.client, + task_queue=task_queue, + nexus_service_handlers=[TestServiceHandler()], + workflows=[UpdatableWorkflow, UpdateWorkflowCaller], + ): + update_workflow_id = f"updatable-workflow-{uuid.uuid4()}" + target_handle = await client.start_workflow( + UpdatableWorkflow.run, id=update_workflow_id, task_queue=task_queue + ) + + async def check_simple_update_and_links(): + """Run an update, check state changes from pending to created, verify forward and back links are correct""" + wf_handle = await client.start_workflow( + UpdateWorkflowCaller.run, + Input( + value=update_workflow_id, + task_queue=task_queue, + update_value="Created", + ), + task_queue=task_queue, + id=f"update-workflow-caller-created-{uuid.uuid4()}", + ) + result = await wf_handle.result() + assert result == "Updated workflow status from Pending to Created" + # assert expected events are in expected sequence in caller history + await assert_event_subsequence( + wf_handle, + [ + EventType.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED, + EventType.EVENT_TYPE_NEXUS_OPERATION_STARTED, + EventType.EVENT_TYPE_NEXUS_OPERATION_COMPLETED, + ], + ) + # now, check the links + caller_history = await wf_handle.fetch_history() + handler_history = await target_handle.fetch_history() + scheduled_event = next( + e + for e in caller_history.events + if e.event_type == EventType.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED + ) + caller_request_id = ( + scheduled_event.nexus_operation_scheduled_event_attributes.request_id + ) + assert target_handle.result_run_id is not None + # from caller ns to target ns + expected_forward_link = Link( + workflow_event=Link.WorkflowEvent( + namespace=client.namespace, + workflow_id=update_workflow_id, + run_id=target_handle.result_run_id, + request_id_ref=Link.WorkflowEvent.RequestIdReference( + request_id=caller_request_id, + event_type=EventType.EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED, + ), + ) + ) + assert wf_handle.result_run_id is not None + # from target ns back to caller ns + expected_backward_link = Link( + workflow_event=Link.WorkflowEvent( + namespace=client.namespace, + workflow_id=wf_handle.id, + run_id=wf_handle.result_run_id, + event_ref=Link.WorkflowEvent.EventReference( + event_id=scheduled_event.event_id, + event_type=EventType.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED, + ), + ) + ) + caller_links = [ + link + for e in caller_history.events + if e.event_type == EventType.EVENT_TYPE_NEXUS_OPERATION_STARTED + for link in e.links + ] + handler_links = [ + link + for e in handler_history.events + if e.event_type + == EventType.EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED + for link in e.links + ] + assert expected_forward_link in caller_links + assert expected_backward_link in handler_links + + async def check_sequential_updates_consistent(): + """Run updates back-to-back, verify update isnt re-processed""" + stable_update_id = "sequential-update" + wf_handle = await client.start_workflow( + UpdateWorkflowCaller.run, + Input( + value=update_workflow_id, + task_queue=task_queue, + update_value="Processed", + update_id=stable_update_id, + ), + task_queue=task_queue, + id="sequential-update-workflow-caller-processed-0", + ) + result = await wf_handle.result() + assert result == "Updated workflow status from Created to Processed" + + # same update_id -> wont be processed again, receives a sync result + wf_handle = await client.start_workflow( + UpdateWorkflowCaller.run, + Input( + value=update_workflow_id, + task_queue=task_queue, + update_value="Processed", + update_id=stable_update_id, + expect_sync_response=True, + ), + task_queue=task_queue, + id="sequential-update-workflow-caller-processed-1", + ) + result = await wf_handle.result() + assert result == "Updated workflow status from Created to Processed" + + async def check_parallel_updates_idempotent_and_finish(): + """Run multiple updates in parallel, verify they are idempotent and finish with same result""" + stable_id = "parallel-updates-id" + num_parallel = 3 + gate = asyncio.Event() + + async def run_update(i: int) -> str: + await gate.wait() + wf_handle = await client.start_workflow( + UpdateWorkflowCaller.run, + Input( + value=update_workflow_id, + task_queue=task_queue, + update_value="Completed", + update_id=stable_id, + ), + task_queue=task_queue, + id=f"parallel-update-workflow-caller-completed-{i}", + ) + return await wf_handle.result() + + tasks = [asyncio.create_task(run_update(i)) for i in range(num_parallel)] + gate.set() + results = await asyncio.gather(*tasks) + + for result in results: + assert result == "Updated workflow status from Processed to Completed" + + async def check_updates_on_completed_workflows_fail(): + """The handler workflow already finished at this point, further updaes should just fail""" + wf_handle = await client.start_workflow( + UpdateWorkflowCaller.run, + Input( + value=update_workflow_id, + task_queue=task_queue, + update_value="dummy, will fail anyway", + ), + task_queue=task_queue, + id=f"{uuid.uuid4()}", + ) + with pytest.raises(WorkflowFailureError): + await wf_handle.result() + + await check_simple_update_and_links() + await check_sequential_updates_consistent() + await check_parallel_updates_idempotent_and_finish() + await check_updates_on_completed_workflows_fail() + + +async def test_temporal_operation_update_workflow_delayed( + client: Client, env: WorkflowEnvironment +) -> None: + if ( + env.supports_time_skipping + ): # time skipping server uses different dynamic configs + pytest.skip("Update workflow tests don't work with time-skipping server") + task_queue = str(uuid.uuid4()) + endpoint_name = make_nexus_endpoint_name(task_queue) + await env.create_nexus_endpoint(endpoint_name, task_queue) + + update_workflow_id = f"another-updatable-workflow-{uuid.uuid4()}" + + # start both caller and handler without starting worker + wf_handle = await client.start_workflow( + UpdateWorkflowCaller.run, + Input( + value=update_workflow_id, + task_queue=task_queue, + update_value="Completed", + ), + task_queue=task_queue, + id=f"update-workflow-caller-created-{uuid.uuid4()}", + ) + target_handle = await client.start_workflow( + UpdatableWorkflow.run, id=update_workflow_id, task_queue=task_queue + ) + + # now, start the worker, it should process both the handler + # and the caller and finish the enqueued update + async with Worker( + env.client, + task_queue=task_queue, + nexus_service_handlers=[TestServiceHandler()], + workflows=[UpdatableWorkflow, UpdateWorkflowCaller], + ): + result = await wf_handle.result() + assert result == "Updated workflow status from Pending to Completed" + + await assert_event_subsequence( + wf_handle, + [ + EventType.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED, + EventType.EVENT_TYPE_NEXUS_OPERATION_STARTED, + EventType.EVENT_TYPE_NEXUS_OPERATION_COMPLETED, + ], + ) + + caller_history = await wf_handle.fetch_history() + handler_history = await target_handle.fetch_history() + scheduled_event = next( + e + for e in caller_history.events + if e.event_type == EventType.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED + ) + caller_request_id = ( + scheduled_event.nexus_operation_scheduled_event_attributes.request_id + ) + assert target_handle.result_run_id is not None + expected_forward_link = Link( + workflow_event=Link.WorkflowEvent( + namespace=client.namespace, + workflow_id=update_workflow_id, + run_id=target_handle.result_run_id, + request_id_ref=Link.WorkflowEvent.RequestIdReference( + request_id=caller_request_id, + event_type=EventType.EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED, + ), + ) + ) + assert wf_handle.result_run_id is not None + expected_backward_link = Link( + workflow_event=Link.WorkflowEvent( + namespace=client.namespace, + workflow_id=wf_handle.id, + run_id=wf_handle.result_run_id, + event_ref=Link.WorkflowEvent.EventReference( + event_id=scheduled_event.event_id, + event_type=EventType.EVENT_TYPE_NEXUS_OPERATION_SCHEDULED, + ), + ) + ) + caller_links = [ + link + for e in caller_history.events + if e.event_type == EventType.EVENT_TYPE_NEXUS_OPERATION_STARTED + for link in e.links + ] + handler_links = [ + link + for e in handler_history.events + if e.event_type == EventType.EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED + for link in e.links + ] + assert expected_forward_link in caller_links + assert expected_backward_link in handler_links + + +async def test_temporal_operation_cancel_rejects_unknown_tokens(): + class FakeNexusTaskCancellation(OperationTaskCancellation): + def is_cancelled(self) -> bool: + return False + + def cancellation_reason(self) -> str | None: + return None + + def wait_until_cancelled_sync(self, timeout: float | None = None) -> bool: + return False + + async def wait_until_cancelled(self) -> None: + return None + + def cancel(self, _reason: str) -> bool: + return False + + cancel_ctx = CancelOperationContext( + service="TestService", + operation="echo", + headers={}, + task_cancellation=FakeNexusTaskCancellation(), + ) + + service_handler = TestServiceHandler() + + # Use a factory style operation form the handler to allow calling cancel directly + op_handler = service_handler.custom_cancel() + + # Invalid token type + token = OperationToken(type=30, namespace="default") # type: ignore + with pytest.raises(nexusrpc.HandlerError) as err: + await op_handler.cancel(cancel_ctx, token.encode()) + assert err.value.type == HandlerErrorType.INTERNAL + assert not err.value.retryable + underlying = err.value.__cause__ + assert isinstance(underlying, TypeError) + assert "unknown token type, got 30" in str(underlying) + + # Workflow ID missing from workflow type + token = OperationToken(type=OperationTokenType.WORKFLOW, namespace="default") + with pytest.raises(nexusrpc.HandlerError) as err: + await op_handler.cancel(cancel_ctx, token.encode()) + assert err.value.type == HandlerErrorType.INTERNAL + assert not err.value.retryable + underlying = err.value.__cause__ + assert isinstance(underlying, TypeError) + assert "expected non-empty workflow id for token type `WORKFLOW`" in str(underlying) + + # Activity ID missing from activity type + token = OperationToken(type=OperationTokenType.ACTIVITY, namespace="default") + with pytest.raises(nexusrpc.HandlerError) as err: + await op_handler.cancel(cancel_ctx, token.encode()) + assert err.value.type == HandlerErrorType.INTERNAL + assert not err.value.retryable + underlying = err.value.__cause__ + assert isinstance(underlying, TypeError) + assert "expected non-empty activity id for token type `ACTIVITY`" in str(underlying) + + activity_op_handler = service_handler.custom_cancel_activity() + for run_id in (None, ""): + token = OperationToken( + type=OperationTokenType.ACTIVITY, + namespace="default", + activity_id="activity-id", + run_id=run_id, + ) + with pytest.raises(nexusrpc.HandlerError) as err: + await activity_op_handler.cancel(cancel_ctx, token.encode()) + assert err.value.type == HandlerErrorType.INTERNAL + assert not err.value.retryable + assert not service_handler.custom_cancel_activity_called.is_set() + + @workflow.defn class BlockingWorkflow: def __init__(self) -> None: @@ -485,6 +1038,41 @@ async def test_temporal_operation_failed_start_allows_retry( await conflict_handle.cancel() +async def test_temporal_operation_mixed_start_raises_handler_err( + client: Client, env: WorkflowEnvironment +): + if env.supports_time_skipping: + pytest.skip( + "Standalone Nexus Operation tests don't work with time-skipping server" + ) + + task_queue = str(uuid.uuid4()) + endpoint_name = make_nexus_endpoint_name(task_queue) + await env.create_nexus_endpoint(endpoint_name, task_queue) + async with Worker( + env.client, + task_queue=task_queue, + nexus_service_handlers=[TestServiceHandler()], + workflows=[EchoWorkflow], + activities=[echo_activity], + ): + nexus_client = client.create_nexus_client(TestService, endpoint_name) + + with pytest.raises(NexusOperationFailureError) as err: + await nexus_client.execute_operation( + TestService.mixed_start, + Input(value="test", task_queue=task_queue), + id=str(uuid.uuid4()), + ) + + assert isinstance(err.value.cause, nexusrpc.HandlerError) + assert err.value.cause.type == HandlerErrorType.BAD_REQUEST + assert ( + "Only one async operation can be started per operation handler invocation" + in err.value.cause.message + ) + + @workflow.defn class SyncResultCaller: @workflow.run @@ -524,6 +1112,245 @@ async def test_temporal_operation_sync_result(client: Client, env: WorkflowEnvir ) +async def test_temporal_operation_start_activity( + client: Client, env: WorkflowEnvironment +): + if env.supports_time_skipping: + pytest.skip( + "Standalone Nexus Operation tests don't work with time-skipping server" + ) + + task_queue = str(uuid.uuid4()) + endpoint_name = make_nexus_endpoint_name(task_queue) + await env.create_nexus_endpoint(endpoint_name, task_queue) + async with Worker( + env.client, + task_queue=task_queue, + nexus_service_handlers=[TestServiceHandler()], + activities=[echo_activity], + ): + nexus_client = client.create_nexus_client(TestService, endpoint_name) + + result = await nexus_client.execute_operation( + TestService.echo_activity, + Input(value="test", task_queue=task_queue), + id=str(uuid.uuid4()), + ) + assert result == "test" + + +async def test_temporal_operation_backing_activity_does_not_duplicate_links( + client: Client, env: WorkflowEnvironment +): + if env.supports_time_skipping: + pytest.skip( + "Standalone Nexus Operation tests don't work with time-skipping server" + ) + + task_queue = str(uuid.uuid4()) + endpoint_name = make_nexus_endpoint_name(task_queue) + await env.create_nexus_endpoint(endpoint_name, task_queue) + activity_id = f"link-activity-{uuid.uuid4()}" + + @service_handler + class LinkActivityHandler: + @nexus.temporal_operation + async def echo_activity( + self, + _ctx: nexus.TemporalStartOperationContext, + client: nexus.TemporalNexusClient, + input: Input, + ) -> nexus.TemporalOperationResult[str]: + return await client.start_activity( + echo_activity, + input, + id=activity_id, + start_to_close_timeout=timedelta(seconds=5), + ) + + async with Worker( + env.client, + task_queue=task_queue, + nexus_service_handlers=[LinkActivityHandler()], + activities=[echo_activity], + ): + nexus_client = client.create_nexus_client(LinkActivityHandler, endpoint_name) + operation_handle = await nexus_client.start_operation( + LinkActivityHandler.echo_activity, + Input(value="test", task_queue=task_queue), + id=str(uuid.uuid4()), + ) + + assert await operation_handle.result() == "test" + activity_description = await client.get_activity_handle(activity_id).describe() + assert isinstance(activity_description.raw_info, ActivityExecutionInfo) + assert operation_handle.run_id is not None + callback_links = [ + link + for callback in activity_description.raw_callbacks + for link in callback.info.callback.links + ] + assert_links_match( + [*activity_description.raw_info.links, *callback_links], + expected_nexus_operation_link( + namespace=client.namespace, + operation_id=operation_handle.operation_id, + run_id=operation_handle.run_id, + ), + ) + + +async def test_temporal_operation_start_activity_raises_error( + client: Client, env: WorkflowEnvironment +): + if env.supports_time_skipping: + pytest.skip( + "Standalone Nexus Operation tests don't work with time-skipping server" + ) + + task_queue = str(uuid.uuid4()) + endpoint_name = make_nexus_endpoint_name(task_queue) + await env.create_nexus_endpoint(endpoint_name, task_queue) + async with Worker( + env.client, + task_queue=task_queue, + nexus_service_handlers=[TestServiceHandler()], + activities=[raise_error_activity], + ): + nexus_client = client.create_nexus_client(TestService, endpoint_name) + + with pytest.raises(NexusOperationFailureError) as err: + await nexus_client.execute_operation( + TestService.error_activity, + Input(value="test", task_queue=task_queue), + id=str(uuid.uuid4()), + ) + + operation_err = err.value.__cause__ + assert isinstance(operation_err, temporalio.exceptions.ApplicationError) + assert operation_err.type == "OperationError" + assert "nexus operation completed unsuccessfully" in str(operation_err) + + application_err = operation_err.__cause__ + assert isinstance(application_err, temporalio.exceptions.ApplicationError) + + assert application_err.type == "test-activity-error-type" + assert "test-activity-error-message" in str(application_err) + assert application_err.__cause__ is None + + +async def test_temporal_operation_cancel_activity( + client: Client, env: WorkflowEnvironment +): + if env.supports_time_skipping: + pytest.skip( + "Standalone Nexus Operation tests don't work with time-skipping server" + ) + + task_queue = str(uuid.uuid4()) + endpoint_name = make_nexus_endpoint_name(task_queue) + await env.create_nexus_endpoint(endpoint_name, task_queue) + async with Worker( + env.client, + task_queue=task_queue, + nexus_service_handlers=[TestServiceHandler()], + activities=[wait_for_cancel_activity], + ): + nexus_client = client.create_nexus_client(TestService, endpoint_name) + + activity_id = f"blocking-activity-{uuid.uuid4()}" + op_handle = await nexus_client.start_operation( + TestService.blocking_activity, activity_id, id=str(uuid.uuid4()) + ) + + await op_handle.cancel() + + activity_handle = client.get_activity_handle(activity_id) + + async def check_cancelled(): + op_desc = await op_handle.describe() + assert op_desc.status is NexusOperationExecutionStatus.CANCELED + activity_desc = await activity_handle.describe() + assert activity_desc.status is ActivityExecutionStatus.CANCELED + + await assert_eventually(check_cancelled) + + +async def test_customized_temporal_operation_cancel_activity( + client: Client, env: WorkflowEnvironment +): + if env.supports_time_skipping: + pytest.skip( + "Standalone Nexus Operation tests don't work with time-skipping server" + ) + + task_queue = str(uuid.uuid4()) + endpoint_name = make_nexus_endpoint_name(task_queue) + await env.create_nexus_endpoint(endpoint_name, task_queue) + + service_handler = TestServiceHandler() + async with Worker( + env.client, + task_queue=task_queue, + nexus_service_handlers=[service_handler], + activities=[wait_for_cancel_activity], + ): + nexus_client = client.create_nexus_client(TestService, endpoint_name) + + activity_id = f"custom-cancel-activity-{uuid.uuid4()}" + op_handle = await nexus_client.start_operation( + TestService.custom_cancel_activity, activity_id, id=str(uuid.uuid4()) + ) + await service_handler.started_custom_cancel_activity.wait() + + await op_handle.cancel() + + activity_handle = client.get_activity_handle(activity_id) + + async def check_cancelled(): + assert service_handler.custom_cancel_activity_called.is_set() + op_desc = await op_handle.describe() + assert op_desc.status is NexusOperationExecutionStatus.CANCELED + activity_desc = await activity_handle.describe() + assert activity_desc.status is ActivityExecutionStatus.CANCELED + + await assert_eventually(check_cancelled) + + +async def test_temporal_operation_double_start_activity_raises_handler_err( + client: Client, env: WorkflowEnvironment +): + if env.supports_time_skipping: + pytest.skip( + "Standalone Nexus Operation tests don't work with time-skipping server" + ) + + task_queue = str(uuid.uuid4()) + endpoint_name = make_nexus_endpoint_name(task_queue) + await env.create_nexus_endpoint(endpoint_name, task_queue) + async with Worker( + env.client, + task_queue=task_queue, + nexus_service_handlers=[TestServiceHandler()], + activities=[wait_for_cancel_activity], + ): + nexus_client = client.create_nexus_client(TestService, endpoint_name) + + with pytest.raises(NexusOperationFailureError) as err: + await nexus_client.execute_operation( + TestService.double_start_activity, + Input(value="test", task_queue=task_queue), + id=str(uuid.uuid4()), + ) + + assert isinstance(err.value.cause, nexusrpc.HandlerError) + assert err.value.cause.type == HandlerErrorType.BAD_REQUEST + assert ( + "Only one async operation can be started per operation handler invocation" + in err.value.cause.message + ) + + @dataclass class TemporalOperationOverloadTestValue: value: int @@ -724,3 +1551,109 @@ async def test_temporal_operation_includes_token_in_callback( ).encode() assert token == expected_token + + +@workflow.defn +class UpdateWorkflowCaller: + """Simple caller workflow that triggers a workflow update via nexus op""" + + @workflow.run + async def run(self, input: Input) -> str: + client = workflow.create_nexus_client( + service=TestService, + endpoint=make_nexus_endpoint_name(input.task_queue), + ) + op_handle = await client.start_operation(TestService.update_op, input) + if input.expect_sync_response: + if op_handle.operation_token: + raise RuntimeError("unexpected operation token on a sync operation") + else: + if not op_handle.operation_token: + raise RuntimeError( + "unexpected empty operation token on an async operation" + ) + return await op_handle + + +@workflow.defn +class UpdatableWorkflow: + """Workflow that accepts updates and exits when it receives a specific status""" + + def __init__(self) -> None: + self.order_status = "Pending" + + @workflow.run + async def run(self) -> None: + await workflow.wait_condition(lambda: self.order_status == "Completed") + # some more order processing etc + + @workflow.update + async def do_update(self, value: str) -> str: + status = self.order_status + await workflow.sleep( + timedelta(seconds=1) + ) # small sleep to ensure updates are async and backlinks are to STARTED events + self.order_status = value + update_result = f"Updated workflow status from {status} to {value}" + return update_result + + +async def test_temporal_operation_includes_activity_token_in_callback( + client: Client, env: WorkflowEnvironment +): + if env.supports_time_skipping: + pytest.skip( + "Standalone Nexus Operation tests don't work with time-skipping server" + ) + task_queue = str(uuid.uuid4()) + endpoint_name = make_nexus_endpoint_name(task_queue) + await env.create_nexus_endpoint(endpoint_name, task_queue) + + @service_handler + class ActivityTokenHandler: + @nexus.temporal_operation + async def echo_activity( + self, + _ctx: nexus.TemporalStartOperationContext, + client: nexus.TemporalNexusClient, + input: Input, + ) -> nexus.TemporalOperationResult[str]: + return await client.start_activity( + echo_activity, + input, + id=input.value, + start_to_close_timeout=timedelta(seconds=10), + start_delay=timedelta(milliseconds=100), + ) + + async with Worker( + client, + task_queue=task_queue, + nexus_service_handlers=[ActivityTokenHandler()], + activities=[echo_activity], + ): + input_value = f"test-{uuid.uuid4()}" + + nexus_client = client.create_nexus_client(ActivityTokenHandler, endpoint_name) + + result = await nexus_client.execute_operation( + ActivityTokenHandler.echo_activity, + Input(value=input_value, task_queue=task_queue), + id=str(uuid.uuid4()), + ) + assert result == input_value + + activity_handle = client.get_activity_handle(input_value) + + desc = await activity_handle.describe() + token = desc.raw_callbacks[0].info.callback.nexus.header[ + "nexus-operation-token" + ] + + expected_token = OperationToken( + type=OperationTokenType.ACTIVITY, + namespace=client.namespace, + activity_id=input_value, + ).encode() + + assert token == expected_token diff --git a/tests/nexus/test_temporal_system_nexus.py b/tests/nexus/test_temporal_system_nexus.py index b689ee8d9..ff6b36e41 100644 --- a/tests/nexus/test_temporal_system_nexus.py +++ b/tests/nexus/test_temporal_system_nexus.py @@ -16,6 +16,7 @@ import temporalio.nexus.system as nexus_system from temporalio import workflow from temporalio.bridge._visitor import PayloadVisitor +from temporalio.bridge._visitor_functions import VisitorFunctions from temporalio.bridge.proto.workflow_completion.workflow_completion_pb2 import ( WorkflowActivationCompletion, ) @@ -34,6 +35,7 @@ from tests.test_extstore import InMemoryTestDriver interceptor_traces: list[tuple[str, object]] = [] +SYSTEM_NEXUS_PAYLOAD_METADATA_KEY = "__temporal_system_payload" @workflow.defn @@ -143,7 +145,7 @@ def _assert_start_nexus_operation_interceptor_trace() -> None: assert request.workflow_type.name == "test-workflow" -class _MarkingPayloadVisitor: +class _MarkingPayloadVisitor(VisitorFunctions): def __init__(self) -> None: self.visited_payload_count = 0 self.system_envelope_count = 0 @@ -186,14 +188,22 @@ def _new_system_nexus_request_payload() -> temporalio.api.common.v1.Payload: assert nested_payload is not None request = workflowservice_pb2.SignalWithStartWorkflowExecutionRequest() request.input.payloads.add().CopyFrom(nested_payload) - payload = nexus_system.get_payload_converter().to_payload(request) + payload = nexus_system._get_payload_converter( + temporalio.converter.PayloadConverter.default + ).to_payload(request) assert payload is not None return payload -async def test_schedule_system_nexus_endpoint_ignores_operation_registry() -> None: +def _new_unmarked_system_nexus_request_payload() -> temporalio.api.common.v1.Payload: + payload = _new_system_nexus_request_payload() + del payload.metadata[SYSTEM_NEXUS_PAYLOAD_METADATA_KEY] + return payload + + +async def test_schedule_marked_system_nexus_payload_ignores_endpoint() -> None: completion = _new_schedule_nexus_completion( - nexus_system.TEMPORAL_SYSTEM_ENDPOINT, + "not-the-system-endpoint", _new_system_nexus_request_payload(), ) visitor = _MarkingPayloadVisitor() @@ -201,7 +211,9 @@ async def test_schedule_system_nexus_endpoint_ignores_operation_registry() -> No await PayloadVisitor().visit(visitor, completion) schedule = completion.successful.commands[0].schedule_nexus_operation - decoded = nexus_system.get_payload_converter().from_payload(schedule.input) + decoded = nexus_system._get_payload_converter( + temporalio.converter.PayloadConverter.default + ).from_payload(schedule.input) assert isinstance( decoded, workflowservice_pb2.SignalWithStartWorkflowExecutionRequest ) @@ -211,10 +223,12 @@ async def test_schedule_system_nexus_endpoint_ignores_operation_registry() -> No assert visitor.system_envelope_count == 1 -async def test_schedule_non_system_nexus_visits_input_as_regular_payload() -> None: +async def test_schedule_unmarked_system_nexus_payload_visits_input_as_regular_payload() -> ( + None +): completion = _new_schedule_nexus_completion( - "not-the-system-endpoint", - _new_system_nexus_request_payload(), + nexus_system.TEMPORAL_SYSTEM_ENDPOINT, + _new_unmarked_system_nexus_request_payload(), ) visitor = _MarkingPayloadVisitor() @@ -222,6 +236,13 @@ async def test_schedule_non_system_nexus_visits_input_as_regular_payload() -> No schedule = completion.successful.commands[0].schedule_nexus_operation assert schedule.input.metadata["visited"] == b"true" + decoded = nexus_system._get_payload_converter( + temporalio.converter.default().payload_converter + ).from_payload(schedule.input) + assert isinstance( + decoded, workflowservice_pb2.SignalWithStartWorkflowExecutionRequest + ) + assert "visited" not in decoded.input.payloads[0].metadata assert visitor.visited_payload_count == 1 assert visitor.system_envelope_count == 0 @@ -339,17 +360,22 @@ def _field_is_repeated(field: FieldDescriptor) -> bool: ], ) def test_system_nexus_proto_roundtrip(message_type: type[Message]) -> None: - payload_converter = nexus_system.get_payload_converter() + payload_converter = nexus_system._get_payload_converter( + temporalio.converter.PayloadConverter.default + ) proto_value = _build_proto_sample(message_type) payload = payload_converter.to_payload(proto_value) assert payload is not None assert payload.metadata["encoding"] == b"binary/protobuf" assert payload.metadata["messageType"] == message_type.DESCRIPTOR.full_name.encode() + assert payload.metadata[SYSTEM_NEXUS_PAYLOAD_METADATA_KEY] == b"true" roundtripped = payload_converter.from_payload(payload, message_type) assert isinstance(roundtripped, message_type) assert roundtripped == proto_value +# Cloud namespaces created by CI do not have the System Nexus dynamic config. +@pytest.mark.requires_local_server async def test_external_workflow_handle_signal_with_start_workflow_uses_system_nexus( env: WorkflowEnvironment, ): diff --git a/tests/nexus/test_use_existing_conflict_policy.py b/tests/nexus/test_use_existing_conflict_policy.py index e7cee9e1c..8ffa9f8f8 100644 --- a/tests/nexus/test_use_existing_conflict_policy.py +++ b/tests/nexus/test_use_existing_conflict_policy.py @@ -4,6 +4,7 @@ import uuid from dataclasses import dataclass +import pytest from nexusrpc.handler import service_handler from temporalio import nexus, workflow @@ -13,6 +14,10 @@ from temporalio.worker import Worker from tests.helpers.nexus import make_nexus_endpoint_name +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server + @dataclass class OpInput: diff --git a/tests/nexus/test_workflow_caller.py b/tests/nexus/test_workflow_caller.py index 89ce2719a..0c47f17c1 100644 --- a/tests/nexus/test_workflow_caller.py +++ b/tests/nexus/test_workflow_caller.py @@ -90,6 +90,11 @@ class OpDefinitionType(IntEnum): LONGHAND = 1 +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server + + @dataclass class SyncResponse: op_definition_type: OpDefinitionType @@ -1979,9 +1984,7 @@ async def test_workflow_caller_custom_metrics(client: Client, env: WorkflowEnvir ) # New client with the runtime - client = await Client.connect( - client.service_client.config.target_host, - namespace=client.namespace, + client = await env.connect_client( runtime=runtime, ) @@ -2052,9 +2055,7 @@ async def test_workflow_caller_buffered_metrics( assert not buffer.retrieve_updates() # Create a new client on the runtime and execute the custom metric workflow - client = await Client.connect( - client.service_client.config.target_host, - namespace=client.namespace, + client = await env.connect_client( runtime=runtime, ) task_queue = str(uuid.uuid4()) diff --git a/tests/nexus/test_workflow_caller_cancellation_types.py b/tests/nexus/test_workflow_caller_cancellation_types.py index bf33983a5..eca269984 100644 --- a/tests/nexus/test_workflow_caller_cancellation_types.py +++ b/tests/nexus/test_workflow_caller_cancellation_types.py @@ -25,6 +25,10 @@ from tests.helpers import LogCapturer, assert_event_subsequence, assert_eventually from tests.helpers.nexus import make_nexus_endpoint_name +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server + @dataclass class TestContext: diff --git a/tests/nexus/test_workflow_caller_cancellation_types_when_cancel_handler_fails.py b/tests/nexus/test_workflow_caller_cancellation_types_when_cancel_handler_fails.py index 4cdeeeb15..44d91a7d1 100644 --- a/tests/nexus/test_workflow_caller_cancellation_types_when_cancel_handler_fails.py +++ b/tests/nexus/test_workflow_caller_cancellation_types_when_cancel_handler_fails.py @@ -30,6 +30,10 @@ has_event, ) +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server + @dataclass class TestContext: diff --git a/tests/nexus/test_workflow_caller_error_chains.py b/tests/nexus/test_workflow_caller_error_chains.py index 9ff84f405..1012d8a94 100644 --- a/tests/nexus/test_workflow_caller_error_chains.py +++ b/tests/nexus/test_workflow_caller_error_chains.py @@ -25,6 +25,10 @@ from temporalio.worker import Worker from tests.helpers.nexus import make_nexus_endpoint_name +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server + @dataclass class ExpectedError: diff --git a/tests/nexus/test_workflow_caller_errors.py b/tests/nexus/test_workflow_caller_errors.py index 9246b9fd7..0f1b6a789 100644 --- a/tests/nexus/test_workflow_caller_errors.py +++ b/tests/nexus/test_workflow_caller_errors.py @@ -42,6 +42,10 @@ from tests.helpers import LogCapturer, assert_eq_eventually from tests.helpers.nexus import make_nexus_endpoint_name +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server + operation_invocation_counts = Counter[str]() logger = getLogger(__name__) diff --git a/tests/nexus/test_workflow_run_operation.py b/tests/nexus/test_workflow_run_operation.py index 851f408ec..7135fde71 100644 --- a/tests/nexus/test_workflow_run_operation.py +++ b/tests/nexus/test_workflow_run_operation.py @@ -23,6 +23,10 @@ from temporalio.worker import Worker from tests.helpers.nexus import make_nexus_endpoint_name +# Cloud CI's namespace credentials cannot manage Nexus endpoints. +# See https://github.com/temporalio/sdk-python/issues/1704. +pytestmark = pytest.mark.requires_local_server + @dataclass class Input: diff --git a/tests/test_activity.py b/tests/test_activity.py index 6efa0d644..6a6d14206 100644 --- a/tests/test_activity.py +++ b/tests/test_activity.py @@ -202,6 +202,8 @@ async def count_activities(self, input: CountActivitiesInput): return await super().count_activities(input) +# Cloud namespaces created by CI do not have the activity start-delay dynamic config. +@pytest.mark.requires_local_server async def test_start_activity_calls_interceptor( client: Client, env: WorkflowEnvironment ): @@ -461,6 +463,8 @@ async def test_get_result(client: Client, env: WorkflowEnvironment): assert await result_via_execute_activity == 2 +# Cloud namespaces created by CI do not have the activity start-delay dynamic config. +@pytest.mark.requires_local_server async def test_start_activity_start_delay(client: Client, env: WorkflowEnvironment): if env.supports_time_skipping: pytest.skip( diff --git a/tests/test_client.py b/tests/test_client.py index d611eda3a..15324cf78 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -556,7 +556,7 @@ async def test_interceptor(client: Client, worker: ExternalWorker): assert interceptor.traces[4][1].id == handle.id -async def test_lazy_client(client: Client, env: WorkflowEnvironment): +async def test_lazy_client(env: WorkflowEnvironment): # TODO(cretz): Fix if env.supports_time_skipping: pytest.skip( @@ -564,23 +564,17 @@ async def test_lazy_client(client: Client, env: WorkflowEnvironment): ) # Create another client that is lazy. This test just makes sure the # functionality continues to work. - lazy_client = await Client.connect( - client.service_client.config.target_host, - namespace=client.namespace, - lazy=True, - ) + lazy_client = await env.connect_client(lazy=True) assert not lazy_client.service_client.worker_service_client._bridge_client await lazy_client.workflow_service.get_system_info(GetSystemInfoRequest()) assert lazy_client.service_client.worker_service_client._bridge_client -async def test_client_connected_skips_connect_lock(client: Client): +async def test_client_connected_skips_connect_lock(env: WorkflowEnvironment): # Once connected, RPCs must not touch the lazy-connect lock. Acquiring it # per-RPC put an event-loop-bound primitive on the hot path, pinning a # connected client to the loop it connected on. - other = await Client.connect( - client.service_client.config.target_host, namespace=client.namespace - ) + other = await env.connect_client() svc = other.service_client.worker_service_client assert svc._bridge_client @@ -599,18 +593,13 @@ async def acquire(self) -> Literal[True]: assert counting.acquire_count == 0 -def test_client_reuse_across_event_loops(client: Client): +def test_client_reuse_across_event_loops(env: WorkflowEnvironment): # A connected client must not be pinned to the loop (or thread) it # connected on. This mirrors the long-lived-loop reuse pattern used by # gevent/gunicorn and synchronous services. - target_host = client.service_client.config.target_host - namespace = client.namespace - connect_loop = asyncio.new_event_loop() try: - reused_client = connect_loop.run_until_complete( - Client.connect(target_host, namespace=namespace) - ) + reused_client = connect_loop.run_until_complete(env.connect_client()) finally: connect_loop.close() @@ -668,21 +657,24 @@ async def test_list_workflows_and_fetch_history( ) expected_id_and_input.append((workflow_id, f'"user{i}"')) - # List them and get their history - actual_id_and_input = sorted( - [ - ( - hist.workflow_id, - hist.events[0] - .workflow_execution_started_event_attributes.input.payloads[0] - .data.decode(), - ) - async for hist in client.list_workflows( - f"WorkflowId = '{workflow_id}'" - ).map_histories() - ] - ) - assert actual_id_and_input == expected_id_and_input + # Visibility is eventually consistent, so wait for all runs before fetching + # their histories. + async def list_id_and_input() -> list[tuple[str, str]]: + return sorted( + [ + ( + hist.workflow_id, + hist.events[0] + .workflow_execution_started_event_attributes.input.payloads[0] + .data.decode(), + ) + async for hist in client.list_workflows( + f"WorkflowId = '{workflow_id}'" + ).map_histories() + ] + ) + + await assert_eq_eventually(expected_id_and_input, list_id_and_input) # Verify listing can limit results limited = [ @@ -837,6 +829,7 @@ def test_history_from_json(): ) +@pytest.mark.requires_local_server async def test_schedule_basics( client: Client, worker: ExternalWorker, env: WorkflowEnvironment ): @@ -844,8 +837,6 @@ async def test_schedule_basics( pytest.skip("Java test server doesn't support schedules") elif os.getenv("TEMPORAL_TEST_PROTO3"): pytest.skip("Older proto library cannot compare repeated fields") - await assert_no_schedules(client) - # Create a schedule with a lot of stuff schedule = Schedule( action=ScheduleActionStartWorkflow( @@ -1083,10 +1074,9 @@ async def list_ids() -> list[str]: assert list_descs[0].id in [f"{handle.id}-3", f"{handle.id}-4"] assert list_descs[1].id in [f"{handle.id}-3", f"{handle.id}-4"] - # Delete all of the schedules - for id in await list_ids(): + # Delete the schedules created by this test. + for id in expected_ids: await client.get_schedule_handle(id).delete() - await assert_no_schedules(client) async def test_schedule_calendar_spec_defaults( @@ -1094,8 +1084,6 @@ async def test_schedule_calendar_spec_defaults( ): if env.supports_time_skipping: pytest.skip("Java test server doesn't support schedules") - await assert_no_schedules(client) - handle = await client.create_schedule( f"schedule-{uuid.uuid4()}", Schedule( @@ -1124,7 +1112,6 @@ async def test_schedule_calendar_spec_defaults( assert time == desc.info.next_action_times[i - 1] + timedelta(days=1) await handle.delete() - await assert_no_schedules(client) async def test_schedule_trigger_immediately( @@ -1132,8 +1119,6 @@ async def test_schedule_trigger_immediately( ): if env.supports_time_skipping: pytest.skip("Java test server doesn't support schedules") - await assert_no_schedules(client) - # Create paused schedule that triggers immediately handle = await client.create_schedule( f"schedule-{uuid.uuid4()}", @@ -1165,7 +1150,6 @@ async def test_schedule_trigger_immediately( ) await handle.delete() - await assert_no_schedules(client) async def test_schedule_backfill( @@ -1173,8 +1157,6 @@ async def test_schedule_backfill( ): if env.supports_time_skipping: pytest.skip("Java test server doesn't support schedules") - await assert_no_schedules(client) - begin = datetime(year=2020, month=1, day=20, hour=5) # Create paused schedule that runs every minute and has two backfills @@ -1225,7 +1207,6 @@ async def test_schedule_backfill( ) finally: await handle.delete() - await assert_no_schedules(client) async def test_schedule_create_limited_actions_validation( @@ -1251,13 +1232,12 @@ async def test_schedule_create_limited_actions_validation( assert "are remaining actions set" in str(err.value) +@pytest.mark.requires_local_server async def test_schedule_workflow_search_attribute_update( client: Client, env: WorkflowEnvironment ): if env.supports_time_skipping: pytest.skip("Java test server doesn't support schedules") - await assert_no_schedules(client) - # Put search attribute on server text_attr_key = SearchAttributeKey.for_text("python-test-schedule-text") untyped_keyword_key = SearchAttributeKey.for_keyword("python-test-schedule-keyword") @@ -1350,7 +1330,6 @@ def update_schedule_typed_attrs( assert desc.typed_search_attributes[text_attr_key] == "some-schedule-attr1" await handle.delete() - await assert_no_schedules(client) @pytest.mark.parametrize( @@ -1362,13 +1341,12 @@ def update_schedule_typed_attrs( "partial-new-values-overwrites-and-drops", ], ) +@pytest.mark.requires_local_server async def test_schedule_search_attribute_update( client: Client, env: WorkflowEnvironment, test_case: str ): if env.supports_time_skipping: pytest.skip("Java test server doesn't support schedules") - await assert_no_schedules(client) - # Put search attributes on server key_1 = SearchAttributeKey.for_text("python-test-schedule-sa-update-key-1") key_2 = SearchAttributeKey.for_keyword("python-test-schedule-sa-update-key-2") @@ -1486,15 +1464,6 @@ async def expectation() -> bool: raise ValueError(f"Invalid test case: {test_case}") await handle.delete() - await assert_no_schedules(client) - - -async def assert_no_schedules(client: Client) -> None: - # Listing appears eventually consistent - async def schedule_count() -> int: - return len([d async for d in await client.list_schedules()]) - - await assert_eq_eventually(0, schedule_count) async def test_build_id_interactions(client: Client, env: WorkflowEnvironment): @@ -1556,6 +1525,9 @@ async def run(self) -> str: return "My First Result" +# Cloud does not reliably provide a prior completion result to manually +# triggered schedule actions. +@pytest.mark.requires_local_server async def test_schedule_last_completion_result( client: Client, env: WorkflowEnvironment ): @@ -1590,12 +1562,14 @@ async def get_schedule_result() -> tuple[int, str | None]: result = await workflow_handle.result() return length, result - assert await get_schedule_result() == (1, "My First Result") + expected_first_result: tuple[int, str | None] = (1, "My First Result") + await assert_eq_eventually(expected_first_result, get_schedule_result) await handle.trigger() - assert await get_schedule_result() == ( + expected_second_result: tuple[int, str | None] = ( 2, "From last completion: My First Result", ) + await assert_eq_eventually(expected_second_result, get_schedule_result) await handle.delete() diff --git a/tests/test_cloud.py b/tests/test_cloud.py index b701bdf94..d7fefb4da 100644 --- a/tests/test_cloud.py +++ b/tests/test_cloud.py @@ -1,18 +1,11 @@ -"""Tests that run against Temporal Cloud.""" +"""Tests that run against the Temporal Cloud Operations API.""" -import multiprocessing import os -from collections.abc import AsyncGenerator, Iterator import pytest -import pytest_asyncio from temporalio.api.cloud.cloudservice.v1 import GetNamespaceRequest -from temporalio.client import Client, CloudOperationsClient -from temporalio.service import TLSConfig -from temporalio.testing import WorkflowEnvironment -from temporalio.worker import SharedStateManager -from tests.helpers.worker import ExternalPythonWorker, ExternalWorker +from temporalio.client import CloudOperationsClient # Skip entire module unless explicitly enabled pytestmark = pytest.mark.skipif( @@ -21,54 +14,6 @@ ) -@pytest_asyncio.fixture(scope="module") # type: ignore[reportUntypedFunctionDecorator] -async def env() -> AsyncGenerator[WorkflowEnvironment, None]: - tls_config: bool | TLSConfig = True - client_cert = os.environ.get("TEMPORAL_CLIENT_CERT") - client_key = os.environ.get("TEMPORAL_CLIENT_KEY") - if client_cert and client_key: - tls_config = TLSConfig( - client_cert=client_cert.encode(), - client_private_key=client_key.encode(), - ) - client = await Client.connect( - os.environ["TEMPORAL_CLIENT_CLOUD_TARGET"], - namespace=os.environ["TEMPORAL_CLIENT_CLOUD_NAMESPACE"], - api_key=os.environ.get("TEMPORAL_CLIENT_CLOUD_API_KEY"), - tls=tls_config, - ) - env = WorkflowEnvironment.from_client(client) - yield env - await env.shutdown() - - -@pytest_asyncio.fixture # type: ignore[reportUntypedFunctionDecorator] -async def client(env: WorkflowEnvironment) -> Client: - return env.client - - -@pytest_asyncio.fixture(scope="module") # type: ignore[reportUntypedFunctionDecorator] -async def worker( - env: WorkflowEnvironment, -) -> AsyncGenerator[ExternalWorker, None]: - w = ExternalPythonWorker(env) - yield w - await w.close() - - -@pytest.fixture(scope="module") -def shared_state_manager() -> Iterator[SharedStateManager]: - mp_mgr = multiprocessing.Manager() - mgr = SharedStateManager.create_from_multiprocessing(mp_mgr) - try: - yield mgr - finally: - mp_mgr.shutdown() - - -# --- Cloud-specific tests --- - - async def test_cloud_client_simple(): client = await CloudOperationsClient.connect( api_key=os.environ["TEMPORAL_CLIENT_CLOUD_API_KEY"], @@ -78,11 +23,3 @@ async def test_cloud_client_simple(): GetNamespaceRequest(namespace=os.environ["TEMPORAL_CLIENT_CLOUD_NAMESPACE"]) ) assert os.environ["TEMPORAL_CLIENT_CLOUD_NAMESPACE"] == result.namespace.namespace - - -# --- Delegated tests --- -# Import test functions to re-run them against cloud fixtures. - -from tests.worker.test_activity import ( # noqa: E402 - test_activity_info, # pyright: ignore[reportUnusedImport] # noqa: F401 -) diff --git a/tests/test_converter.py b/tests/test_converter.py index 10365f9c1..f1a056c5f 100644 --- a/tests/test_converter.py +++ b/tests/test_converter.py @@ -6,6 +6,7 @@ import logging import sys import traceback +import typing from collections import deque from collections.abc import Iterable, Mapping, MutableMapping, Sequence from dataclasses import dataclass @@ -14,8 +15,11 @@ from typing import ( Any, Dict, # type:ignore[reportDeprecated] + Generic, Literal, NewType, + TypeVar, + cast, get_args, get_type_hints, ) @@ -44,10 +48,15 @@ JSONTypeConverter, JSONTypeConverterUnhandled, PayloadCodec, + TransferTypeConverter, decode_search_attributes, encode_search_attribute_values, + transfer_type_convertible, value_to_type, ) +from temporalio.converter._payload_converter import ( + _TemporalTransferTypePayloadConverter, +) from temporalio.exceptions import ( ApplicationError, FailureError, @@ -254,6 +263,179 @@ def test_binary_proto(): assert decoded == proto +class TemporalTransferTypeValueConverter( + TransferTypeConverter[ + "TemporalTransferTypeValue", + temporalio.api.common.v1.WorkflowExecution, + ] +): + transfer_type = temporalio.api.common.v1.WorkflowExecution + + def to_transfer_type( + self, value: TemporalTransferTypeValue + ) -> temporalio.api.common.v1.WorkflowExecution: + return temporalio.api.common.v1.WorkflowExecution( + workflow_id=value.value, + run_id="run-id", + ) + + def from_transfer_type( + self, + value: temporalio.api.common.v1.WorkflowExecution, + type_hint: type[TemporalTransferTypeValue], + ) -> TemporalTransferTypeValue: + return TemporalTransferTypeValue(value=value.workflow_id) + + +@transfer_type_convertible(TemporalTransferTypeValueConverter) +@dataclass +class TemporalTransferTypeValue: + value: str + + +class TemporalTransferTypeValueWithoutHintConverter( + TransferTypeConverter[ + "TemporalTransferTypeValueWithoutHint", + temporalio.api.common.v1.WorkflowExecution, + ] +): + def to_transfer_type( + self, value: TemporalTransferTypeValueWithoutHint + ) -> temporalio.api.common.v1.WorkflowExecution: + return temporalio.api.common.v1.WorkflowExecution( + workflow_id=value.value, + run_id="run-id", + ) + + def from_transfer_type( + self, + value: temporalio.api.common.v1.WorkflowExecution, + type_hint: type[TemporalTransferTypeValueWithoutHint], + ) -> TemporalTransferTypeValueWithoutHint: + return TemporalTransferTypeValueWithoutHint(value=value.workflow_id) + + +@transfer_type_convertible(TemporalTransferTypeValueWithoutHintConverter) +@dataclass +class TemporalTransferTypeValueWithoutHint: + value: str + + +T = TypeVar("T") + + +@dataclass +class TemporalTransferTypeGenericValue(Generic[T]): + value: T + + +class TemporalTransferTypeGenericValueConverter( + TransferTypeConverter[ + TemporalTransferTypeGenericValue[T], + temporalio.api.common.v1.WorkflowExecution, + ] +): + transfer_type = temporalio.api.common.v1.WorkflowExecution + + def to_transfer_type( + self, value: TemporalTransferTypeGenericValue[T] + ) -> temporalio.api.common.v1.WorkflowExecution: + return temporalio.api.common.v1.WorkflowExecution( + workflow_id=str(value.value), + run_id="run-id", + ) + + def from_transfer_type( + self, + value: temporalio.api.common.v1.WorkflowExecution, + type_hint: type[TemporalTransferTypeGenericValue[T]], + ) -> TemporalTransferTypeGenericValue[T]: + converted_value: str | int = value.workflow_id + if typing.get_args(type_hint)[0] is int: + converted_value = int(converted_value) + return TemporalTransferTypeGenericValue(value=cast(T, converted_value)) + + +# Register after both classes are defined so the generic type can be resolved. +transfer_type_convertible(TemporalTransferTypeGenericValueConverter)( + TemporalTransferTypeGenericValue +) + + +class CustomDefaultPayloadConverter(DefaultPayloadConverter): + pass + + +def test_temporal_transfer_type_payload_converter_wraps_user_converter(): + data_converter = DataConverter( + payload_converter_class=CustomDefaultPayloadConverter + ) + converter = data_converter.payload_converter + assert isinstance(converter, _TemporalTransferTypePayloadConverter) + value = TemporalTransferTypeValue("workflow-id") + + payload = converter.to_payload(value) + + assert payload.metadata["encoding"] == b"json/protobuf" + assert ( + payload.metadata["messageType"] == b"temporal.api.common.v1.WorkflowExecution" + ) + assert all("temporal-wire" not in key for key in payload.metadata) + assert all(b"temporal-wire" not in value for value in payload.metadata.values()) + assert converter.from_payload(payload, TemporalTransferTypeValue) == value + + plain_proto_payload = converter.to_payload( + temporalio.api.common.v1.WorkflowExecution(workflow_id="id1", run_id="id2") + ) + assert plain_proto_payload.metadata["encoding"] == b"json/protobuf" + + +def test_temporal_transfer_type_payload_converter_without_transfer_type_hint(): + converter = DataConverter.default.payload_converter + value = TemporalTransferTypeValueWithoutHint("workflow-id") + + payload = converter.to_payload(value) + + assert payload.metadata["encoding"] == b"json/protobuf" + assert ( + payload.metadata["messageType"] == b"temporal.api.common.v1.WorkflowExecution" + ) + assert ( + converter.from_payload(payload, TemporalTransferTypeValueWithoutHint) == value + ) + + +@pytest.mark.parametrize( + ("value", "type_hint"), + [ + ( + TemporalTransferTypeGenericValue("workflow-id"), + TemporalTransferTypeGenericValue[str], + ), + ( + TemporalTransferTypeGenericValue(123), + TemporalTransferTypeGenericValue[int], + ), + ], +) +def test_temporal_transfer_type_payload_converter_with_generic_value( + value: TemporalTransferTypeGenericValue[T], + type_hint: type[TemporalTransferTypeGenericValue[T]], +): + converter = DataConverter.default.payload_converter + + payload = converter.to_payload(value) + + assert converter.from_payload(payload, type_hint) == value + + +def test_transfer_type_convertible_rejects_existing_converter(): + with pytest.raises(TypeError, match="already has a transfer type converter"): + transfer_type_convertible(TemporalTransferTypeValueConverter)( + TemporalTransferTypeValue + ) + + def test_encode_search_attribute_values(): with pytest.raises(TypeError, match="of type tuple not one of"): encode_search_attribute_values([("bad type",)]) # type: ignore[arg-type] diff --git a/tests/test_envconfig.py b/tests/test_envconfig.py index c1a7e32ab..0b44db731 100644 --- a/tests/test_envconfig.py +++ b/tests/test_envconfig.py @@ -1,12 +1,20 @@ import os import textwrap from pathlib import Path +from types import SimpleNamespace +from unittest.mock import AsyncMock import pytest +import temporalio.runtime +import temporalio.service from temporalio.client import Client from temporalio.envconfig import ClientConfig, ClientConfigProfile, ClientConfigTLS from temporalio.service import TLSConfig +from temporalio.testing import WorkflowEnvironment +from tests import conftest + +pytestmark = pytest.mark.requires_local_server # A base TOML config with a default and a custom profile TOML_CONFIG_BASE = textwrap.dedent( @@ -147,6 +155,82 @@ def test_load_profile_from_data_env_overrides(): assert config.get("target_host") == "env-address" +@pytest.mark.parametrize( + ("env_type", "expected"), + [ + ("envconfig", True), + ("local", False), + ("time-skipping", False), + ], +) +def test_envconfig_server_selection( + env_type: str, + expected: bool, +): + assert conftest._uses_envconfig_server(env_type) is expected + + +async def test_envconfig_workflow_environment_uses_client_connect_config( + monkeypatch: pytest.MonkeyPatch, +): + monkeypatch.setenv("TEMPORAL_ADDRESS", "env-address") + monkeypatch.setenv("TEMPORAL_NAMESPACE", "env-namespace") + monkeypatch.setenv("TEMPORAL_API_KEY", "env-api-key") + monkeypatch.setenv("TEMPORAL_TLS", "false") + monkeypatch.setenv("TEMPORAL_GRPC_META_TEST_HEADER", "env-value") + client = object() + environment = object() + connect = AsyncMock(return_value=client) + monkeypatch.setattr(Client, "connect", connect) + monkeypatch.setattr( + WorkflowEnvironment, + "from_client", + lambda actual_client: environment, + ) + + assert await conftest._create_env_from_envconfig() is environment + connect.assert_awaited_once_with( + target_host="env-address", + namespace="env-namespace", + api_key="env-api-key", + tls=False, + rpc_metadata={"test-header": "env-value"}, + ) + + +async def test_workflow_environment_connect_client_inherits_connection_options( + monkeypatch: pytest.MonkeyPatch, +): + runtime = temporalio.runtime.Runtime.default() + source_client = SimpleNamespace( + namespace="env-namespace", + service_client=SimpleNamespace( + config=temporalio.service.ConnectConfig( + target_host="env-address", + api_key="env-api-key", + tls=False, + rpc_metadata={"test-header": "env-value"}, + runtime=runtime, + ) + ), + ) + environment = WorkflowEnvironment(source_client) # type: ignore[arg-type] + connect = AsyncMock(return_value=object()) + monkeypatch.setattr(Client, "connect", connect) + + await environment.connect_client(lazy=True) + + connect.assert_awaited_once_with( + "env-address", + namespace="env-namespace", + api_key="env-api-key", + tls=False, + rpc_metadata={"test-header": "env-value"}, + runtime=runtime, + lazy=True, + ) + + def test_load_profile_env_overrides(base_config_file: Path): """Test that environment variables correctly override file settings.""" env = { diff --git a/tests/test_plugins.py b/tests/test_plugins.py index e8823af27..9414e8df0 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -60,6 +60,7 @@ async def connect_service_client( return await next(config) +@pytest.mark.requires_local_server async def test_client_plugin(client: Client, env: WorkflowEnvironment): if env.supports_time_skipping: pytest.skip("Client connect is only designed for local") diff --git a/tests/test_prepare_release.py b/tests/test_prepare_release.py index 07bdbd4a5..ab5d4bf7e 100644 --- a/tests/test_prepare_release.py +++ b/tests/test_prepare_release.py @@ -1,75 +1,154 @@ from __future__ import annotations import datetime +import importlib.util +import pathlib +import subprocess +import sys +from types import ModuleType + +import pytest from scripts.prepare_release import ( + create_release_branch, + create_release_pr, + ensure_clean_worktree, + ensure_only_release_changes, finalize_changelog_release, + push_release_branch, replace_project_version, replace_service_version, ) -def test_finalize_changelog_release_seeds_unreleased_and_versions_notes() -> None: - changelog = """# Changelog - -## [Unreleased] - -### Added - -### Changed - -- Changed a thing. - -### Fixed - -## [1.29.0] - 2026-06-17 - -### Added - -- Previous release. -""" - - updated = finalize_changelog_release( - changelog, - version="1.30.0", - release_date=datetime.date(2026, 6, 18), +def _release_verify_module() -> ModuleType: + path = pathlib.Path(__file__).parents[1] / ".github/scripts/release_verify.py" + spec = importlib.util.spec_from_file_location("release_verify", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def test_sdk_core_changelog_entries_runs_core_script( + monkeypatch: pytest.MonkeyPatch, +) -> None: + release_verify = _release_verify_module() + calls: list[tuple[list[str], pathlib.Path]] = [] + + def check_output(args: list[str], *, cwd: pathlib.Path, **_kwargs: object) -> str: + calls.append((args, cwd)) + return "#### Added\n\n* Core feature.\n" + + monkeypatch.setattr(subprocess, "check_output", check_output) + core_path = pathlib.Path("sdk-core") + assert release_verify._sdk_core_changelog_entries("old", "new", core_path) == [ + "#### Added", + "", + "* Core feature.", + ] + assert calls == [ + ( + [ + "cargo", + "run", + "--quiet", + "-p", + "temporalio-sdk-core", + "--bin", + "changelog-release-notes", + "--", + "--from", + "old", + "--to", + "new", + ], + core_path, + ) + ] + + +def test_sdk_core_release_notes_embed_core_output( + monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path +) -> None: + release_verify = _release_verify_module() + (tmp_path / ".git").mkdir() + monkeypatch.setattr(release_verify, "_previous_release_tag", lambda _version: "old") + monkeypatch.setattr(release_verify, "_gitlink", lambda revision, _path: revision) + monkeypatch.setattr( + subprocess, + "check_output", + lambda *_args, **_kwargs: "#### Commits\n\n- Core commit\n", ) - assert updated.startswith( - """# Changelog + assert release_verify._sdk_core_release_notes("1.30.0", str(tmp_path)) == [ + "### SDK Core", + "", + "#### Commits", + "", + "- Core commit", + ] -## [Unreleased] -### Added - -### Changed - -### Deprecated +def test_finalize_changelog_release() -> None: + text = "## [Unreleased]\n\n### Added\n\n- A thing.\n" + assert "## [1.30.0] - 2026-06-18" in finalize_changelog_release( + text, version="1.30.0", release_date=datetime.date(2026, 6, 18) + ) -### Breaking Changes -### Fixed +def test_replace_versions() -> None: + assert 'version = "1.30.0"' in replace_project_version( + 'version = "1.29.0"\n', "1.30.0" + ) + assert '__version__ = "1.30.0"' in replace_service_version( + '__version__ = "1.29.0"\n', "1.30.0" + ) -### Security -## [1.30.0] - 2026-06-18 +def test_create_release_branch(monkeypatch: pytest.MonkeyPatch) -> None: + calls: list[list[str]] = [] + monkeypatch.setattr( + subprocess, "run", lambda command, **_kwargs: calls.append(command) + ) + create_release_branch(pathlib.Path("/repo"), "1.30.0") + assert calls[1] == [ + "git", + "switch", + "--create", + "chore/release-1.30.0", + "origin/main", + ] + + +def test_clean_worktree_rejected(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr( + subprocess, + "run", + lambda *_args, **_kwargs: subprocess.CompletedProcess([], 0, " M file\n"), + ) + with pytest.raises(RuntimeError, match="clean worktree"): + ensure_clean_worktree(pathlib.Path("/repo")) + with pytest.raises(RuntimeError, match="unexpected files"): + ensure_only_release_changes(pathlib.Path("/repo")) -### Changed -- Changed a thing. -""" +def test_create_release_pr(monkeypatch: pytest.MonkeyPatch) -> None: + calls: list[list[str]] = [] + monkeypatch.setattr( + subprocess, "run", lambda command, **_kwargs: calls.append(command) ) - assert "### Added\n\n### Changed\n\n- Changed a thing." not in updated + create_release_pr(pathlib.Path("/repo"), "1.30.0") + assert "chore/release-1.30.0" in calls[0] -def test_replace_versions() -> None: - assert ( - replace_project_version( - '[project]\nname = "temporalio"\nversion = "1.29.0"\n', "1.30.0" - ) - == '[project]\nname = "temporalio"\nversion = "1.30.0"' - ) - assert ( - replace_service_version('__version__ = "1.29.0"\n', "1.30.0") - == '__version__ = "1.30.0"' +def test_push_release_branch(monkeypatch: pytest.MonkeyPatch) -> None: + calls: list[list[str]] = [] + monkeypatch.setattr( + subprocess, "run", lambda command, **_kwargs: calls.append(command) ) + push_release_branch(pathlib.Path("/repo"), "1.30.0") + assert calls == [ + ["git", "push", "--set-upstream", "origin", "chore/release-1.30.0"] + ] diff --git a/tests/test_runtime.py b/tests/test_runtime.py index c29961c52..e003af768 100644 --- a/tests/test_runtime.py +++ b/tests/test_runtime.py @@ -4,22 +4,26 @@ import re import uuid from datetime import timedelta -from typing import cast +from typing import Any, cast from urllib.request import urlopen import pytest +import temporalio.bridge.metric +import temporalio.bridge.runtime from temporalio import workflow from temporalio.client import Client from temporalio.runtime import ( LogForwardingConfig, LoggingConfig, + OpenTelemetryConfig, PrometheusConfig, Runtime, TelemetryConfig, TelemetryFilter, _RuntimeRef, ) +from temporalio.testing import WorkflowEnvironment from temporalio.worker import Worker from tests.helpers import ( LogHandler, @@ -36,22 +40,18 @@ async def run(self, name: str) -> str: return f"Hello, {name}!" -async def test_different_runtimes(client: Client): +async def test_different_runtimes(env: WorkflowEnvironment): # Create two workers in separate runtimes and run workflows on them. # Confirm they each have different Prometheus addresses. prom_addr1 = f"127.0.0.1:{find_free_port()}" - client1 = await Client.connect( - client.service_client.config.target_host, - namespace=client.namespace, + client1 = await env.connect_client( runtime=Runtime( telemetry=TelemetryConfig(metrics=PrometheusConfig(bind_address=prom_addr1)) ), ) prom_addr2 = f"127.0.0.1:{find_free_port()}" - client2 = await Client.connect( - client.service_client.config.target_host, - namespace=client.namespace, + client2 = await env.connect_client( runtime=Runtime( telemetry=TelemetryConfig(metrics=PrometheusConfig(bind_address=prom_addr2)) ), @@ -150,16 +150,14 @@ async def run(self) -> None: raise RuntimeError("Intentional error") -async def test_runtime_task_fail_log_forwarding(client: Client): +async def test_runtime_task_fail_log_forwarding(env: WorkflowEnvironment): # Client with lo capturing runtime log_queue: queue.Queue[logging.LogRecord] = queue.Queue() log_queue_list = cast(list[logging.LogRecord], log_queue.queue) handler = logging.handlers.QueueHandler(log_queue) logger = logging.getLogger(f"log-{uuid.uuid4()}") logger.setLevel(logging.WARN) - client = await Client.connect( - client.service_client.config.target_host, - namespace=client.namespace, + client = await env.connect_client( runtime=Runtime( telemetry=TelemetryConfig( logging=LoggingConfig( @@ -198,7 +196,7 @@ async def has_log() -> bool: assert record.temporal_log.fields["run_id"] == handle.result_run_id # type: ignore -async def test_prometheus_histogram_bucket_overrides(client: Client): +async def test_prometheus_histogram_bucket_overrides(env: WorkflowEnvironment): # Set up a Prometheus configuration with custom histogram bucket overrides prom_addr = f"127.0.0.1:{find_free_port()}" special_value = float(1234.5678) @@ -228,9 +226,7 @@ async def test_prometheus_histogram_bucket_overrides(client: Client): custom_histogram.record(600) # Create client with overrides - client_with_overrides = await Client.connect( - client.service_client.config.target_host, - namespace=client.namespace, + client_with_overrides = await env.connect_client( runtime=runtime, ) @@ -269,6 +265,105 @@ async def check_metrics() -> None: await assert_eventually(check_metrics) +async def test_opentelemetry_histogram_bucket_overrides(env: WorkflowEnvironment): + # Set up an OpenTelemetry configuration with custom histogram bucket overrides + import threading + from http.server import BaseHTTPRequestHandler, HTTPServer + + from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import ( + ExportMetricsServiceRequest, + ExportMetricsServiceResponse, + ) + + special_value = float(1234.5678) + histogram_overrides = { + "temporal_long_request_latency": [special_value / 2, special_value], + "custom_histogram": [special_value / 2, special_value], + } + + captured: dict[str, list[float]] = {} + lock = threading.Lock() + + class Handler(BaseHTTPRequestHandler): + def log_message(self, format: str, *args: Any): + pass # silence default stderr logging + + def do_POST(self): + length = int(self.headers.get("Content-Length", "0")) + req = ExportMetricsServiceRequest() + req.ParseFromString(self.rfile.read(length)) + with lock: + for rm in req.resource_metrics: + for sm in rm.scope_metrics: + for m in sm.metrics: + if m.HasField("histogram"): + for dp in m.histogram.data_points: + captured[m.name] = list(dp.explicit_bounds) + body = ExportMetricsServiceResponse().SerializeToString() + self.send_response(200) + self.send_header("Content-Type", "application/x-protobuf") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + otel_port = find_free_port() + server = HTTPServer(("127.0.0.1", otel_port), Handler) + thread = threading.Thread(target=server.serve_forever, daemon=True) + thread.start() + try: + runtime = Runtime( + telemetry=TelemetryConfig( + metrics=OpenTelemetryConfig( + url=f"http://127.0.0.1:{otel_port}/v1/metrics", + http=True, + metric_periodicity=timedelta(milliseconds=100), + durations_as_seconds=False, + histogram_bucket_overrides=histogram_overrides, + ), + ), + ) + + # Create and record to a custom histogram + custom_histogram = runtime.metric_meter.create_histogram( + "custom_histogram", "Custom histogram", "ms" + ) + custom_histogram.record(600) + + # Run a workflow so built-in histograms (e.g. temporal_long_request_latency) + # are recorded and exported. + client_with_overrides = await env.connect_client( + runtime=runtime, + ) + task_queue = f"task-queue-{uuid.uuid4()}" + async with Worker( + client_with_overrides, + task_queue=task_queue, + workflows=[HelloWorkflow], + ): + assert "Hello, World!" == await client_with_overrides.execute_workflow( + HelloWorkflow.run, + "World", + id=f"workflow-{uuid.uuid4()}", + task_queue=task_queue, + ) + + async def check_metrics() -> None: + with lock: + snapshot = dict(captured) + for key, buckets in histogram_overrides.items(): + assert key in snapshot, ( + f"Missing {key} in captured metrics: {list(snapshot)}" + ) + assert snapshot[key] == pytest.approx(buckets), ( + f"Bucket mismatch for {key}: got {snapshot[key]} expected {buckets}" + ) + + await assert_eventually(check_metrics) + finally: + server.shutdown() + server.server_close() + + def test_runtime_options_invalid_heartbeat() -> None: with pytest.raises(ValueError): Runtime( @@ -276,6 +371,31 @@ def test_runtime_options_invalid_heartbeat() -> None: ) +def test_runtime_environment_info_option(monkeypatch: pytest.MonkeyPatch) -> None: + captured_options = [] + + class MockRuntime: + def __init__(self, *, options: Any) -> None: + captured_options.append(options) + + monkeypatch.setattr(temporalio.bridge.runtime, "Runtime", MockRuntime) + monkeypatch.setattr( + temporalio.bridge.metric.MetricMeter, "create", staticmethod(lambda _: None) + ) + + Runtime(telemetry=TelemetryConfig()) + Runtime(telemetry=TelemetryConfig(), disable_environment_info=True) + + assert [option.disable_environment_info for option in captured_options] == [ + False, + True, + ] + + +def test_runtime_environment_info_can_be_enabled() -> None: + Runtime(telemetry=TelemetryConfig(), disable_environment_info=False) + + def test_runtime_ref_creates_default(): ref = _RuntimeRef() assert not ref._default_runtime diff --git a/tests/test_serialization_context.py b/tests/test_serialization_context.py index 0fde2aa96..8d65d5f1f 100644 --- a/tests/test_serialization_context.py +++ b/tests/test_serialization_context.py @@ -217,19 +217,19 @@ async def test_payload_conversion_calls_follow_expected_sequence_and_contexts( workflow_context = dataclasses.asdict( WorkflowSerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=workflow_id, ) ) child_workflow_context = dataclasses.asdict( WorkflowSerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=f"{workflow_id}_child", ) ) activity_context = dataclasses.asdict( ActivitySerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=workflow_id, workflow_type=PayloadConversionWorkflow.__name__, activity_type=passthrough_activity.__name__, @@ -363,14 +363,14 @@ async def test_heartbeat_details_payload_conversion(client: Client): workflow_context = dataclasses.asdict( WorkflowSerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=workflow_id, ) ) activity_context = dataclasses.asdict( ActivitySerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=workflow_id, workflow_type=HeartbeatDetailsSerializationContextTestWorkflow.__name__, activity_type=activity_with_heartbeat_details.__name__, @@ -455,13 +455,13 @@ async def test_local_activity_payload_conversion(client: Client): workflow_context = dataclasses.asdict( WorkflowSerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=workflow_id, ) ) local_activity_context = dataclasses.asdict( ActivitySerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=workflow_id, workflow_type=LocalActivityWorkflow.__name__, activity_type=local_activity.__name__, @@ -572,11 +572,11 @@ async def test_async_activity_completion_payload_conversion( workflow_runner=UnsandboxedWorkflowRunner(), # so that we can use isinstance ): workflow_context = WorkflowSerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=workflow_id, ) activity_context = ActivitySerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=workflow_id, workflow_type=AsyncActivityCompletionSerializationContextTestWorkflow.__name__, activity_type=async_activity.__name__, @@ -649,7 +649,7 @@ def my_method(self) -> None: def test_subclassed_async_activity_handle(client: Client): activity_context = ActivitySerializationContext( - namespace="default", + namespace=client.namespace, workflow_id="workflow-id", workflow_type="workflow-type", activity_type="activity-type", @@ -742,7 +742,7 @@ async def test_signal_payload_conversion( workflow_context = dataclasses.asdict( WorkflowSerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=workflow_id, ) ) @@ -811,7 +811,7 @@ async def test_query_payload_conversion( workflow_context = dataclasses.asdict( WorkflowSerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=workflow_id, ) ) @@ -909,7 +909,7 @@ async def test_update_payload_conversion( workflow_context = dataclasses.asdict( WorkflowSerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=workflow_id, ) ) @@ -1016,13 +1016,13 @@ async def test_external_workflow_signal_and_cancel_payload_conversion( signaler_context = dataclasses.asdict( WorkflowSerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=signaler_workflow_id, ) ) target_context = dataclasses.asdict( WorkflowSerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=target_workflow_id, ) ) @@ -1157,13 +1157,13 @@ async def test_failure_converter_with_context(client: Client): workflow_context = dataclasses.asdict( WorkflowSerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=workflow_id, ) ) activity_context = dataclasses.asdict( ActivitySerializationContext( - namespace="default", + namespace=client.namespace, workflow_id=workflow_id, workflow_type=FailureConverterTestWorkflow.__name__, activity_type=failing_activity.__name__, @@ -1731,6 +1731,7 @@ async def run(self, _data: str) -> None: ) +@pytest.mark.requires_local_server async def test_nexus_payload_codec_operations_lack_context( env: WorkflowEnvironment, ): diff --git a/tests/test_service.py b/tests/test_service.py index 0cf06fae0..954c87092 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -149,6 +149,7 @@ async def test_check_health(client: Client): assert err.value.status == temporalio.service.RPCStatusCode.NOT_FOUND +@pytest.mark.requires_local_server async def test_grpc_status(client: Client, env: WorkflowEnvironment): if env.supports_time_skipping: pytest.skip( diff --git a/tests/test_tls.py b/tests/test_tls.py new file mode 100644 index 000000000..cc0eddde9 --- /dev/null +++ b/tests/test_tls.py @@ -0,0 +1,227 @@ +"""Tests for :py:attr:`temporalio.service.TLSConfig.verification_server_name`. + +Each test runs an in-process TLS server whose certificate is valid only for +``pinned.test`` while the client always dials ``localhost``, so no Temporal +server is needed. The server records each handshake's outcome and the SNI it +received. +""" + +from __future__ import annotations + +import asyncio +import datetime +import socket +import ssl +import threading +from collections.abc import AsyncIterator +from dataclasses import dataclass +from pathlib import Path + +import pytest +import pytest_asyncio +from cryptography import x509 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.x509.oid import NameOID + +import temporalio.service + +PINNED_NAME = "pinned.test" + + +def test_tls_config_verification_server_name_reaches_bridge(): + config = temporalio.service.TLSConfig(verification_server_name=PINNED_NAME) + assert config._to_bridge_config().verification_server_name == PINNED_NAME + default = temporalio.service.TLSConfig() + assert default._to_bridge_config().verification_server_name is None + + +async def test_tls_verification_server_name_requires_root_ca(): + # The check is enforced when building connection options, before any dial. + with pytest.raises(ValueError, match="server root CA cert"): + await temporalio.service.ServiceClient.connect( + temporalio.service.ConnectConfig( + target_host="localhost:1", + tls=temporalio.service.TLSConfig(verification_server_name=PINNED_NAME), + ) + ) + + +async def test_tls_default_verification_rejects_unmatched_name(tls_server: _TlsServer): + # Baseline for the tests below: the certificate is only valid for the + # pinned name, so verifying against the dialed host rejects it. + handshake = await _handshake( + tls_server, temporalio.service.TLSConfig(server_root_ca_cert=tls_server.ca_pem) + ) + assert not handshake.ok + + +async def test_tls_verification_server_name_decouples_verification_from_sni( + tls_server: _TlsServer, +): + # Verification against the pinned name succeeds, while the SNI the server + # sees is still the dialed host rather than the pinned name. + handshake = await _handshake( + tls_server, + temporalio.service.TLSConfig( + server_root_ca_cert=tls_server.ca_pem, + verification_server_name=PINNED_NAME, + ), + ) + assert handshake.ok + assert handshake.sni == "localhost" + + +async def test_tls_verification_server_name_is_enforced(tls_server: _TlsServer): + # A pinned name the certificate does not carry is still rejected; the + # option redirects verification rather than disabling it. + handshake = await _handshake( + tls_server, + temporalio.service.TLSConfig( + server_root_ca_cert=tls_server.ca_pem, + verification_server_name="wrong.test", + ), + ) + assert not handshake.ok + + +@dataclass +class _Handshake: + ok: bool + sni: str | None + + +async def _handshake( + server: _TlsServer, tls: temporalio.service.TLSConfig +) -> _Handshake: + """Attempt a connection and return the server's view of the handshake.""" + config = temporalio.service.ConnectConfig( + target_host=f"localhost:{server.port}", tls=tls + ) + # The connect always fails since this server speaks no gRPC; only whether + # the TLS handshake completed matters. + try: + await asyncio.wait_for(temporalio.service.ServiceClient.connect(config), 20) + except asyncio.TimeoutError: + raise + except Exception: + pass + else: + pytest.fail("connect unexpectedly succeeded") + return await server.next_handshake() + + +class _TlsServer: + """TLS server that records handshake outcomes and the SNI it receives.""" + + def __init__(self, certs: Path) -> None: + self.ca_pem = (certs / "ca.pem").read_bytes() + self._ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + self._ctx.load_cert_chain(certs / "srv.pem", certs / "srv.key") + self._ctx.set_alpn_protocols(["h2"]) + # Populated by the SNI callback for the connection currently + # handshaking; the accept loop is single-threaded. + self._sni: str | None = None + + def on_sni( + _sock: ssl.SSLObject, name: str | None, _ctx: ssl.SSLContext + ) -> None: + self._sni = name + + self._ctx.sni_callback = on_sni + self._handshakes: list[_Handshake] = [] + self._lock = threading.Lock() + self._stop = threading.Event() + self._sock = socket.create_server(("127.0.0.1", 0)) + self._sock.settimeout(0.1) + self.port: int = self._sock.getsockname()[1] + self._thread = threading.Thread(target=self._serve, daemon=True) + self._thread.start() + + def _serve(self) -> None: + while not self._stop.is_set(): + try: + conn, _ = self._sock.accept() + except TimeoutError: + continue + conn.settimeout(10) # A stalled handshake must not wedge this loop. + self._sni = None + try: + tls = self._ctx.wrap_socket(conn, server_side=True) + except (ssl.SSLError, OSError): + conn.close() + self._record(_Handshake(ok=False, sni=self._sni)) + continue + self._record(_Handshake(ok=True, sni=self._sni)) + tls.close() + self._sock.close() + + def _record(self, handshake: _Handshake) -> None: + with self._lock: + self._handshakes.append(handshake) + + async def next_handshake(self) -> _Handshake: + """Wait for and consume the next recorded handshake.""" + # The client can report its result before this thread has recorded + # the outcome, so wait for the observation to land. + for _ in range(200): + with self._lock: + if self._handshakes: + return self._handshakes.pop(0) + await asyncio.sleep(0.05) + raise AssertionError("server observed no TLS handshake") + + def close(self) -> None: + self._stop.set() + self._thread.join(timeout=2) + + +@pytest_asyncio.fixture # type: ignore[reportUntypedFunctionDecorator] +async def tls_server(tmp_path: Path) -> AsyncIterator[_TlsServer]: + _write_pinned_certs(tmp_path) + server = _TlsServer(tmp_path) + try: + yield server + finally: + server.close() + + +def _write_pinned_certs(path: Path) -> None: + """Write a CA and a server cert (chained to it) valid only for ``pinned.test``.""" + now = datetime.datetime.now(datetime.timezone.utc) + ca_key = ec.generate_private_key(ec.SECP256R1()) + ca_name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "test-ca")]) + ca_cert = ( + x509.CertificateBuilder() + .subject_name(ca_name) + .issuer_name(ca_name) + .public_key(ca_key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(now - datetime.timedelta(days=1)) + .not_valid_after(now + datetime.timedelta(days=7)) + .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True) + .sign(ca_key, hashes.SHA256()) + ) + server_key = ec.generate_private_key(ec.SECP256R1()) + server_cert = ( + x509.CertificateBuilder() + .subject_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, PINNED_NAME)])) + .issuer_name(ca_name) + .public_key(server_key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(now - datetime.timedelta(days=1)) + .not_valid_after(now + datetime.timedelta(days=7)) + .add_extension( + x509.SubjectAlternativeName([x509.DNSName(PINNED_NAME)]), critical=False + ) + .sign(ca_key, hashes.SHA256()) + ) + (path / "ca.pem").write_bytes(ca_cert.public_bytes(serialization.Encoding.PEM)) + (path / "srv.pem").write_bytes(server_cert.public_bytes(serialization.Encoding.PEM)) + (path / "srv.key").write_bytes( + server_key.private_bytes( + serialization.Encoding.PEM, + serialization.PrivateFormat.PKCS8, + serialization.NoEncryption(), + ) + ) diff --git a/tests/testing/test_activity.py b/tests/testing/test_activity.py index 2acf93639..71ba7f590 100644 --- a/tests/testing/test_activity.py +++ b/tests/testing/test_activity.py @@ -167,3 +167,47 @@ def my_activity() -> None: env = ActivityEnvironment(client=Mock(spec=Client)) env.run(my_activity) assert saw_error + + +async def test_activity_logger_does_not_mutate_caller_extra(): + """Regression test for https://github.com/temporalio/sdk-python/issues/503. + + The activity LoggerAdapter must not mutate the ``extra`` dict provided by + the caller; it should merge its temporal context into a fresh dict. + """ + import logging + import logging.handlers + import queue + + handler = logging.handlers.QueueHandler(queue.Queue()) + activity.logger.base_logger.addHandler(handler) + previous_level = activity.logger.base_logger.level + activity.logger.base_logger.setLevel(logging.INFO) + previous_full = activity.logger.full_activity_info_on_extra + activity.logger.full_activity_info_on_extra = True + + try: + + def log_with_extra() -> dict: + caller_extra = {"request_id": "req-1"} + activity.logger.info("hi", extra=caller_extra) + return caller_extra + + env = ActivityEnvironment() + caller_extra = env.run(log_with_extra) + finally: + activity.logger.base_logger.removeHandler(handler) + activity.logger.base_logger.setLevel(previous_level) + activity.logger.full_activity_info_on_extra = previous_full + + # The caller's dict must be untouched. + assert caller_extra == {"request_id": "req-1"} + + # But the emitted record should still carry the temporal-injected fields + # alongside the caller-provided one. + records: list[logging.LogRecord] = list(handler.queue.queue) # type: ignore[attr-defined] + assert records, "expected one log record" + record = records[-1] + assert record.__dict__["request_id"] == "req-1" + assert record.__dict__["temporal_activity"]["activity_type"] == "unknown" + assert "activity_info" in record.__dict__ diff --git a/tests/testing/test_workflow.py b/tests/testing/test_workflow.py index d5f2aae5c..b47d0ac05 100644 --- a/tests/testing/test_workflow.py +++ b/tests/testing/test_workflow.py @@ -252,6 +252,7 @@ def assert_proper_error(err: BaseException | None) -> None: assert_proper_error(err.value.cause) +@pytest.mark.requires_local_server async def test_search_attributes_on_dev_server( client: Client, env: WorkflowEnvironment ): diff --git a/tests/worker/test_activity.py b/tests/worker/test_activity.py index df85b89fb..64691a93f 100644 --- a/tests/worker/test_activity.py +++ b/tests/worker/test_activity.py @@ -382,8 +382,13 @@ async def wait_cancel() -> str: while True: await asyncio.sleep(0.3) activity.heartbeat() - except asyncio.CancelledError: - return "Got cancelled error, cancelled? " + str(activity.is_cancelled()) + except asyncio.CancelledError as err: + return ( + "Got cancelled error, cancelled? " + + str(activity.is_cancelled()) + + ", reason: " + + str(err) + ) result = await _execute_workflow_with_activity( client, @@ -394,7 +399,10 @@ async def wait_cancel() -> str: heartbeat_timeout_ms=2000, shared_state_manager=shared_state_manager, ) - assert result.result == "Got cancelled error, cancelled? True" + assert ( + result.result + == "Got cancelled error, cancelled? True, reason: Activity cancelled" + ) async def test_activity_cancel_throw( @@ -1419,6 +1427,154 @@ def some_activity() -> str: assert result.result == "context var: some value!" +@activity.defn +def post_return_cancel_activity() -> str: + return "done" + + +@activity.defn +def post_return_cancel_probe_activity() -> str: + return "probe" + + +class PostReturnCancelInterceptor(Interceptor): + def __init__( + self, after_return_started: asyncio.Event, allow_completion: asyncio.Event + ) -> None: + super().__init__() + self.after_return_started = after_return_started + self.allow_completion = allow_completion + + def intercept_activity( + self, next: ActivityInboundInterceptor + ) -> ActivityInboundInterceptor: + return PostReturnCancelActivityInbound( + next, self.after_return_started, self.allow_completion + ) + + +class PostReturnCancelActivityInbound(ActivityInboundInterceptor): + def __init__( + self, + next: ActivityInboundInterceptor, + after_return_started: asyncio.Event, + allow_completion: asyncio.Event, + ) -> None: + super().__init__(next) + self.after_return_started = after_return_started + self.allow_completion = allow_completion + + async def execute_activity(self, input: ExecuteActivityInput) -> Any: + result = await super().execute_activity(input) + if activity.info().activity_type == "post_return_cancel_activity": + self.after_return_started.set() + await self.allow_completion.wait() + return result + + +async def test_sync_activity_cancel_after_return_does_not_kill_thread_pool_worker( + client: Client, + worker: ExternalWorker, + env: WorkflowEnvironment, + shared_state_manager: SharedStateManager, +): + if env.supports_time_skipping: + pytest.skip("Test requires real worker-side timeout cancellation delivery") + + def alive_thread_count(executor: ThreadPoolExecutor) -> int: + return sum(1 for thread in list(executor._threads) if thread.is_alive()) + + after_return_started = asyncio.Event() + allow_completion = asyncio.Event() + act_task_queue = str(uuid.uuid4()) + + with ThreadPoolExecutor(max_workers=1) as executor: + with pytest.warns( + UserWarning, + match="Worker max_concurrent_activities is 2 but activity_executor's max_workers is only", + ): + act_worker = Worker( + client, + task_queue=act_task_queue, + activities=[ + post_return_cancel_activity, + post_return_cancel_probe_activity, + ], + activity_executor=executor, + interceptors=[ + PostReturnCancelInterceptor(after_return_started, allow_completion) + ], + max_concurrent_activities=2, + shared_state_manager=shared_state_manager, + ) + + async with act_worker: + try: + timed_out_workflow = asyncio.create_task( + client.execute_workflow( + "kitchen_sink", + KSWorkflowParams( + actions=[ + KSAction( + execute_activity=KSExecuteActivityAction( + name="post_return_cancel_activity", + task_queue=act_task_queue, + start_to_close_timeout_ms=200, + retry_max_attempts=1, + ) + ) + ] + ), + id=str(uuid.uuid4()), + task_queue=worker.task_queue, + ) + ) + await asyncio.wait_for(after_return_started.wait(), timeout=5) + + with pytest.raises(WorkflowFailureError) as err: + await timed_out_workflow + timeout = assert_activity_error(err.value) + assert isinstance(timeout, TimeoutError) + assert timeout.type == TimeoutType.START_TO_CLOSE + + activity_worker = act_worker._activity_worker + assert activity_worker + for _ in range(50): + if any( + running.cancelled_event and running.cancelled_event.is_set() + for running in activity_worker._running_activities.values() + ): + break + await asyncio.sleep(0.1) + else: + pytest.fail("Timed out waiting for activity cancellation") + + probe_result = await asyncio.wait_for( + client.execute_workflow( + "kitchen_sink", + KSWorkflowParams( + actions=[ + KSAction( + execute_activity=KSExecuteActivityAction( + name="post_return_cancel_probe_activity", + task_queue=act_task_queue, + start_to_close_timeout_ms=30000, + retry_max_attempts=1, + ) + ) + ] + ), + id=str(uuid.uuid4()), + task_queue=worker.task_queue, + ), + timeout=5, + ) + assert probe_result == "probe" + assert alive_thread_count(executor) == 1 + finally: + allow_completion.set() + + @activity.defn async def local_without_schedule_to_close_activity() -> str: return "some-activity" diff --git a/tests/worker/test_extstore.py b/tests/worker/test_extstore.py index e186f4e67..e8ef8edb2 100644 --- a/tests/worker/test_extstore.py +++ b/tests/worker/test_extstore.py @@ -1,8 +1,7 @@ +import contextlib import dataclasses -import logging -import re import uuid -from collections.abc import Sequence +from collections.abc import Iterator, Sequence from dataclasses import dataclass from datetime import timedelta from unittest import mock @@ -11,10 +10,10 @@ import temporalio import temporalio.bridge.client +import temporalio.bridge.proto.workflow_completion import temporalio.bridge.worker import temporalio.client import temporalio.converter -import temporalio.worker._workflow from temporalio import activity, workflow from temporalio.api.common.v1 import Payload from temporalio.client import Client, WorkflowFailureError, WorkflowHandle @@ -31,7 +30,7 @@ from temporalio.exceptions import ActivityError, ApplicationError from temporalio.testing._workflow import WorkflowEnvironment from temporalio.worker import Replayer -from tests.helpers import LogCapturer, assert_task_fail_eventually, new_worker +from tests.helpers import assert_task_fail_eventually, new_worker from tests.test_extstore import InMemoryTestDriver @@ -181,9 +180,7 @@ async def test_extstore_activity_input_no_retrieve( WorkflowFailureError wrapping an ActivityError.""" driver = BadTestDriver(no_retrieve=True) - client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + client = await env.connect_client( data_converter=dataclasses.replace( temporalio.converter.default(), external_storage=ExternalStorage( @@ -225,9 +222,7 @@ async def test_extstore_activity_result_no_store( terminates with a WorkflowFailureError wrapping an ActivityError.""" driver = BadTestDriver(no_store=True) - client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + client = await env.connect_client( data_converter=dataclasses.replace( temporalio.converter.default(), external_storage=ExternalStorage( @@ -274,9 +269,7 @@ async def test_extstore_worker_missing_driver( """ driver = InMemoryTestDriver() - far_client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + far_client = await env.connect_client( data_converter=dataclasses.replace( temporalio.converter.default(), external_storage=ExternalStorage( @@ -286,10 +279,7 @@ async def test_extstore_worker_missing_driver( ), ) - worker_client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, - ) + worker_client = await env.connect_client() async with new_worker( worker_client, ExtStoreWorkflow, activities=[ext_store_activity] @@ -315,9 +305,7 @@ async def test_extstore_payload_not_found_fails_workflow( """When a non-retryable ApplicationError is raised while retrieving workflow input, the workflow must fail terminally (not retry as a task failure). """ - client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + client = await env.connect_client( data_converter=dataclasses.replace( temporalio.converter.default(), external_storage=ExternalStorage( @@ -363,9 +351,7 @@ async def _run_extstore_workflow_and_fetch_history( activity_output_size: int = 10, ) -> WorkflowHandle: """Helper: run ExtStoreWorkflow with the given driver and return its history handle.""" - extstore_client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + extstore_client = await env.connect_client( data_converter=dataclasses.replace( temporalio.converter.default(), external_storage=ExternalStorage( @@ -515,9 +501,7 @@ async def test_extstore_chained_activities( """ driver = InMemoryTestDriver() - client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + client = await env.connect_client( data_converter=dataclasses.replace( temporalio.converter.default(), external_storage=ExternalStorage( @@ -567,9 +551,7 @@ def __init__(self, driver_name: str): driver2 = InMemoryTestDriver(driver_name="driver2") driver3 = DifferentTestDriver(driver_name="driver3") - client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + client = await env.connect_client( data_converter=dataclasses.replace( temporalio.converter.default(), external_storage=ExternalStorage( @@ -616,12 +598,32 @@ async def test_worker_storage_drivers_empty_without_external_storage( # TMPRL1104 workflow task duration logging # --------------------------------------------------------------------------- -_workflow_logger = logging.getLogger(temporalio.worker._workflow.__name__) +# The duration log itself is emitted (and tested) in sdk-core. The Python worker's part is +# attaching the external-storage metrics to the completion, so these tests capture the +# completion and assert on its fields directly rather than on core's asynchronously +# forwarded log, which would be nondeterministic to observe here. + +@contextlib.contextmanager +def _capture_completions() -> Iterator[ + list[temporalio.bridge.proto.workflow_completion.WorkflowActivationCompletion] +]: + """Capture every WorkflowActivationCompletion the worker hands to core.""" + completions: list[ + temporalio.bridge.proto.workflow_completion.WorkflowActivationCompletion + ] = [] + original = temporalio.bridge.worker.Worker.complete_workflow_activation -def _tmprl1104_records(capturer: LogCapturer) -> list[logging.LogRecord]: - """Return all TMPRL1104 log records from the capturer.""" - return capturer.find_all(lambda r: r.getMessage().startswith("[TMPRL1104]")) + async def capturing(self, completion): # type: ignore[no-untyped-def] + completions.append(completion) + return await original(self, completion) + + with mock.patch.object( + temporalio.bridge.worker.Worker, + "complete_workflow_activation", + capturing, + ): + yield completions async def _expected_payload_size( @@ -632,47 +634,33 @@ async def _expected_payload_size( return payloads[0].ByteSize() -@workflow.defn -class SimpleWorkflow: - """Minimal workflow for testing logging without external storage.""" - - @workflow.run - async def run(self) -> str: - return "done" - - async def test_tmprl1104_no_extstore(env: WorkflowEnvironment) -> None: - """Without external storage, TMPRL1104 logs contain duration but no - download/upload metrics.""" - with LogCapturer().logs_captured(_workflow_logger, level=logging.DEBUG) as capturer: - async with new_worker(env.client, SimpleWorkflow) as worker: + """Without external storage configured, completions carry no storage metrics.""" + with _capture_completions() as completions: + async with new_worker( + env.client, ExtStoreWorkflow, activities=[ext_store_activity] + ) as worker: await env.client.execute_workflow( - SimpleWorkflow.run, + ExtStoreWorkflow.run, + ExtStoreWorkflowInput( + input_data="small", + activity_input_size=10, + activity_output_size=10, + output_size=10, + ), id=f"workflow-{uuid.uuid4()}", task_queue=worker.task_queue, ) - records = _tmprl1104_records(capturer) - assert len(records) == 1 - record = records[0] - assert re.match( - r"\[TMPRL1104\] [^:]+:\d+:\d+ Workflow task duration information \(", - record.getMessage(), - ) - assert hasattr(record, "workflow_task_duration") - assert hasattr(record, "event_id") - # No external storage — download/upload fields must be absent - assert not hasattr(record, "payload_download_count") - assert not hasattr(record, "payload_download_size") - assert not hasattr(record, "payload_download_duration") - assert not hasattr(record, "payload_upload_count") - assert not hasattr(record, "payload_upload_size") - assert not hasattr(record, "payload_upload_duration") + assert completions, "expected the worker to complete at least one activation" + for c in completions: + assert not c.HasField("payload_download_metrics") + assert not c.HasField("payload_upload_metrics") async def test_tmprl1104_with_extstore_download(env: WorkflowEnvironment) -> None: - """When external storage decodes payloads, TMPRL1104 logs include download - metrics on the activation that retrieves them.""" + """When external storage retrieves payloads, the completion for the WFT that + retrieved them carries download metrics.""" driver = InMemoryTestDriver() data_converter = dataclasses.replace( temporalio.converter.default(), @@ -681,9 +669,7 @@ async def test_tmprl1104_with_extstore_download(env: WorkflowEnvironment) -> Non payload_size_threshold=512, ), ) - client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + client = await env.connect_client( data_converter=data_converter, ) @@ -695,7 +681,7 @@ async def test_tmprl1104_with_extstore_download(env: WorkflowEnvironment) -> Non ) expected_input_size = await _expected_payload_size(data_converter, wf_input) - with LogCapturer().logs_captured(_workflow_logger, level=logging.DEBUG) as capturer: + with _capture_completions() as completions: async with new_worker( client, ExtStoreWorkflow, activities=[ext_store_activity] ) as worker: @@ -706,31 +692,19 @@ async def test_tmprl1104_with_extstore_download(env: WorkflowEnvironment) -> Non task_queue=worker.task_queue, ) - records = _tmprl1104_records(capturer) - assert len(records) == 2 - - # WFT 1: retrieves the externalized workflow input - assert re.match( - r"\[TMPRL1104\] [^:]+:\d+:\d+ Workflow task duration information \(", - records[0].getMessage(), - ) - assert getattr(records[0], "payload_download_count") == 1 - assert getattr(records[0], "payload_download_size") == expected_input_size - assert getattr(records[0], "payload_download_duration") > timedelta(0) - assert not hasattr(records[0], "payload_upload_count") - - # WFT 2: activity result is small — no external storage - assert re.match( - r"\[TMPRL1104\] [^:]+:\d+:\d+ Workflow task duration information \(", - records[1].getMessage(), - ) - assert not hasattr(records[1], "payload_download_count") - assert not hasattr(records[1], "payload_upload_count") + downloads = [c for c in completions if c.HasField("payload_download_metrics")] + assert len(downloads) == 1 + m = downloads[0].payload_download_metrics + assert m.payload_count == 1 + assert m.total_size_bytes == expected_input_size + assert m.total_duration.ToTimedelta() > timedelta(0) + assert list(m.driver_names) == [driver.name()] + assert not any(c.HasField("payload_upload_metrics") for c in completions) async def test_tmprl1104_with_extstore_upload(env: WorkflowEnvironment) -> None: - """When external storage encodes payloads, TMPRL1104 logs include upload - metrics on the WFT that produces them.""" + """When external storage stores payloads, the completion for the WFT that + produced them carries upload metrics.""" driver = InMemoryTestDriver() data_converter = dataclasses.replace( temporalio.converter.default(), @@ -739,16 +713,14 @@ async def test_tmprl1104_with_extstore_upload(env: WorkflowEnvironment) -> None: payload_size_threshold=512, ), ) - client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + client = await env.connect_client( data_converter=data_converter, ) wf_output = "wo" * 1024 # 2048 bytes → stored externally expected_output_size = await _expected_payload_size(data_converter, wf_output) - with LogCapturer().logs_captured(_workflow_logger, level=logging.DEBUG) as capturer: + with _capture_completions() as completions: async with new_worker( client, ExtStoreWorkflow, activities=[ext_store_activity] ) as worker: @@ -764,33 +736,21 @@ async def test_tmprl1104_with_extstore_upload(env: WorkflowEnvironment) -> None: task_queue=worker.task_queue, ) - records = _tmprl1104_records(capturer) - assert len(records) == 2 - - # WFT 1: small input — no external storage - assert re.match( - r"\[TMPRL1104\] [^:]+:\d+:\d+ Workflow task duration information \(", - records[0].getMessage(), - ) - assert not hasattr(records[0], "payload_download_count") - assert not hasattr(records[0], "payload_upload_count") - - # WFT 2: workflow returns large result → uploaded - assert re.match( - r"\[TMPRL1104\] [^:]+:\d+:\d+ Workflow task duration information \(", - records[1].getMessage(), - ) - assert not hasattr(records[1], "payload_download_count") - assert getattr(records[1], "payload_upload_count") == 1 - assert getattr(records[1], "payload_upload_size") == expected_output_size - assert getattr(records[1], "payload_upload_duration") > timedelta(0) + uploads = [c for c in completions if c.HasField("payload_upload_metrics")] + assert len(uploads) == 1 + m = uploads[0].payload_upload_metrics + assert m.payload_count == 1 + assert m.total_size_bytes == expected_output_size + assert m.total_duration.ToTimedelta() > timedelta(0) + assert list(m.driver_names) == [driver.name()] + assert not any(c.HasField("payload_download_metrics") for c in completions) async def test_tmprl1104_with_extstore_download_and_upload( env: WorkflowEnvironment, ) -> None: - """When both download and upload happen across WFTs, TMPRL1104 logs include - both sets of metrics.""" + """When both download and upload happen across WFTs, the respective completions + carry the matching metrics.""" driver = InMemoryTestDriver() data_converter = dataclasses.replace( temporalio.converter.default(), @@ -799,9 +759,7 @@ async def test_tmprl1104_with_extstore_download_and_upload( payload_size_threshold=512, ), ) - client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + client = await env.connect_client( data_converter=data_converter, ) @@ -815,7 +773,7 @@ async def test_tmprl1104_with_extstore_download_and_upload( wf_output = "wo" * 1024 expected_output_size = await _expected_payload_size(data_converter, wf_output) - with LogCapturer().logs_captured(_workflow_logger, level=logging.DEBUG) as capturer: + with _capture_completions() as completions: async with new_worker( client, ExtStoreWorkflow, activities=[ext_store_activity] ) as worker: @@ -826,28 +784,19 @@ async def test_tmprl1104_with_extstore_download_and_upload( task_queue=worker.task_queue, ) - records = _tmprl1104_records(capturer) - assert len(records) == 2 + downloads = [c for c in completions if c.HasField("payload_download_metrics")] + assert len(downloads) == 1 + dm = downloads[0].payload_download_metrics + assert dm.payload_count == 1 + assert dm.total_size_bytes == expected_input_size + assert dm.total_duration.ToTimedelta() > timedelta(0) - # WFT 1: retrieves externalized workflow input - assert re.match( - r"\[TMPRL1104\] [^:]+:\d+:\d+ Workflow task duration information \(", - records[0].getMessage(), - ) - assert getattr(records[0], "payload_download_count") == 1 - assert getattr(records[0], "payload_download_size") == expected_input_size - assert getattr(records[0], "payload_download_duration") > timedelta(0) - assert not hasattr(records[0], "payload_upload_count") - - # WFT 2: uploads externalized workflow result - assert re.match( - r"\[TMPRL1104\] [^:]+:\d+:\d+ Workflow task duration information \(", - records[1].getMessage(), - ) - assert not hasattr(records[1], "payload_download_count") - assert getattr(records[1], "payload_upload_count") == 1 - assert getattr(records[1], "payload_upload_size") == expected_output_size - assert getattr(records[1], "payload_upload_duration") > timedelta(0) + uploads = [c for c in completions if c.HasField("payload_upload_metrics")] + assert len(uploads) == 1 + um = uploads[0].payload_upload_metrics + assert um.payload_count == 1 + assert um.total_size_bytes == expected_output_size + assert um.total_duration.ToTimedelta() > timedelta(0) # --------------------------------------------------------------------------- @@ -928,9 +877,7 @@ async def _make_tracking_client( env: WorkflowEnvironment, ) -> tuple[Client, ContextTrackingStorageDriver]: driver = ContextTrackingStorageDriver() - client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, + client = await env.connect_client( data_converter=dataclasses.replace( temporalio.converter.default(), external_storage=ExternalStorage( diff --git a/tests/worker/test_interceptor.py b/tests/worker/test_interceptor.py index 431f8280d..4a1da399d 100644 --- a/tests/worker/test_interceptor.py +++ b/tests/worker/test_interceptor.py @@ -269,6 +269,7 @@ def update_validated_validator(self, param: str) -> None: raise ApplicationError("Invalid update") +@pytest.mark.requires_local_server async def test_worker_interceptor(client: Client, env: WorkflowEnvironment): # TODO(cretz): Fix if env.supports_time_skipping: diff --git a/tests/worker/test_payload_size_limits.py b/tests/worker/test_payload_size_limits.py index 202899c38..18cc7e75a 100644 --- a/tests/worker/test_payload_size_limits.py +++ b/tests/worker/test_payload_size_limits.py @@ -1,20 +1,14 @@ -import dataclasses import logging import uuid -import warnings from dataclasses import dataclass from datetime import timedelta import pytest -import temporalio -import temporalio.converter +import temporalio.api.enums.v1 from temporalio import activity, workflow -from temporalio.client import Client, WorkflowFailureError -from temporalio.converter import PayloadLimitsConfig, PayloadSizeWarning +from temporalio.client import PayloadLimitsConfig, WorkflowFailureError from temporalio.exceptions import ( - ActivityError, - ApplicationError, TerminatedError, TimeoutError, TimeoutType, @@ -27,148 +21,68 @@ TelemetryFilter, ) from temporalio.testing._workflow import WorkflowEnvironment -from temporalio.worker._replayer import Replayer from tests import DEV_SERVER_DOWNLOAD_VERSION -from tests.helpers import LogCapturer, new_worker +from tests.helpers import LogCapturer, assert_eventually, new_worker + +# Payload/memo size-limit enforcement lives in sdk-rust. These tests only assert that the SDK's +# plumbing reaches core: oversized completions are failed proactively, the worker opt-out lets +# oversized payloads through to the server, and the connection's warn threshold produces a +# forwarded [TMPRL1103] warning. @dataclass class LargePayloadWorkflowInput: activity_input_data_size: int - activity_output_data_size: int - activity_exception_data_size: int workflow_output_data_size: int - data: str - - -@dataclass -class LargePayloadWorkflowOutput: - data: str @dataclass class LargePayloadActivityInput: - exception_data_size: int - output_data_size: int - data: str - - -@dataclass -class LargePayloadActivityOutput: data: str @activity.defn -async def large_payload_activity( - input: LargePayloadActivityInput, -) -> LargePayloadActivityOutput: - if input.exception_data_size > 0: - raise ApplicationError( - "Intentional activity failure", "e" * input.exception_data_size - ) - return LargePayloadActivityOutput(data="o" * input.output_data_size) +async def large_payload_activity(_input: LargePayloadActivityInput) -> None: + return None @workflow.defn class LargePayloadWorkflow: @workflow.run - async def run(self, input: LargePayloadWorkflowInput) -> LargePayloadWorkflowOutput: - await workflow.execute_activity( - large_payload_activity, - LargePayloadActivityInput( - exception_data_size=input.activity_exception_data_size, - output_data_size=input.activity_output_data_size, - data="i" * input.activity_input_data_size, - ), - schedule_to_close_timeout=timedelta(seconds=5), - ) - return LargePayloadWorkflowOutput(data="o" * input.workflow_output_data_size) + async def run(self, input: LargePayloadWorkflowInput) -> str: + if input.activity_input_data_size > 0: + await workflow.execute_activity( + large_payload_activity, + LargePayloadActivityInput(data="i" * input.activity_input_data_size), + schedule_to_close_timeout=timedelta(seconds=5), + ) + return "o" * input.workflow_output_data_size PAYLOAD_ERROR_LIMIT = 10 * 1024 PAYLOAD_LIMITS_EXTRA_ARGS = [ "--dynamic-config-value", f"limit.blobSize.error={PAYLOAD_ERROR_LIMIT}", - # Warn limit must be specified to have the server enforce the error limit + # The server only enforces the error limit for payloads that also exceed the warn limit, so the + # warn limit must be below the error limit. "--dynamic-config-value", f"limit.blobSize.warn={2 * 1024}", ] -async def test_payload_size_warning_workflow_input(client: Client): - config = client.config() - config["data_converter"] = dataclasses.replace( - temporalio.converter.default(), - payload_limits=PayloadLimitsConfig( - payload_size_warning=100, - ), - ) - client = Client(**config) - - with warnings.catch_warnings(record=True) as w: - async with new_worker( - client, LargePayloadWorkflow, activities=[large_payload_activity] - ) as worker: - await client.execute_workflow( - LargePayloadWorkflow.run, - LargePayloadWorkflowInput( - activity_input_data_size=0, - activity_output_data_size=0, - activity_exception_data_size=0, - workflow_output_data_size=0, - data="i" * 2 * 1024, - ), - id=f"workflow-{uuid.uuid4()}", - task_queue=worker.task_queue, +def _forwarding_runtime(logger: logging.Logger) -> Runtime: + return Runtime( + telemetry=TelemetryConfig( + logging=LoggingConfig( + filter=TelemetryFilter(core_level="WARN", other_level="ERROR"), + forwarding=LogForwardingConfig(logger=logger), ) - - assert len(w) == 1 - assert issubclass(w[-1].category, PayloadSizeWarning) - assert ( - "[TMPRL1103] Attempted to upload payloads with size that exceeded the warning limit." - in str(w[-1].message) ) - - -async def test_payload_size_warning_workflow_memo(client: Client): - config = client.config() - config["data_converter"] = dataclasses.replace( - temporalio.converter.default(), - payload_limits=PayloadLimitsConfig(memo_size_warning=128), ) - client = Client(**config) - - with warnings.catch_warnings(record=True) as w: - async with new_worker( - client, LargePayloadWorkflow, activities=[large_payload_activity] - ) as worker: - await client.execute_workflow( - LargePayloadWorkflow.run, - LargePayloadWorkflowInput( - activity_input_data_size=0, - activity_output_data_size=0, - activity_exception_data_size=0, - workflow_output_data_size=0, - data="", - ), - id=f"workflow-{uuid.uuid4()}", - task_queue=worker.task_queue, - memo={ - "key1": [0] * 64, - "key2": [0] * 64, - "key3": [0] * 64, - }, - ) - - assert len(w) == 1 - assert issubclass(w[-1].category, PayloadSizeWarning) - assert ( - "[TMPRL1103] Attempted to upload memo with size that exceeded the warning limit." - in str(w[-1].message) - ) -async def test_payload_size_error_disabled_workflow_payload(env: WorkflowEnvironment): +async def test_oversized_payload_fails_task_with_error_log(env: WorkflowEnvironment): + """An oversized workflow completion is proactively failed by worker, which logs [TMPRL1103].""" if env.supports_time_skipping: pytest.skip("Time-skipping server does not report payload limits.") @@ -176,64 +90,19 @@ async def test_payload_size_error_disabled_workflow_payload(env: WorkflowEnviron dev_server_extra_args=PAYLOAD_LIMITS_EXTRA_ARGS, dev_server_download_version=DEV_SERVER_DOWNLOAD_VERSION, ) as env: - async with new_worker( - env.client, - LargePayloadWorkflow, - activities=[large_payload_activity], - disable_payload_error_limit=True, - ) as worker: - with pytest.raises(WorkflowFailureError) as err: - await env.client.execute_workflow( - LargePayloadWorkflow.run, - LargePayloadWorkflowInput( - activity_input_data_size=PAYLOAD_ERROR_LIMIT + 1024, - activity_output_data_size=0, - activity_exception_data_size=0, - workflow_output_data_size=0, - data="", - ), - id=f"workflow-{uuid.uuid4()}", - task_queue=worker.task_queue, - execution_timeout=timedelta(seconds=3), - ) - - assert isinstance(err.value.cause, TerminatedError) - assert ( - err.value.cause.message - == "BadScheduleActivityAttributes: ScheduleActivityTaskCommandAttributes.Input exceeds size limit." - ) - - -async def test_payload_size_error_workflow_result(env: WorkflowEnvironment): - if env.supports_time_skipping: - pytest.skip("Time-skipping server does not report payload limits.") - - async with await WorkflowEnvironment.start_local( - dev_server_extra_args=PAYLOAD_LIMITS_EXTRA_ARGS, - dev_server_download_version=DEV_SERVER_DOWNLOAD_VERSION, - ) as env: - # Create worker runtime with forwarded logger worker_logger = logging.getLogger(f"log-{uuid.uuid4()}") - worker_runtime = Runtime( - telemetry=TelemetryConfig( - logging=LoggingConfig( - filter=TelemetryFilter(core_level="WARN", other_level="ERROR"), - forwarding=LogForwardingConfig(logger=worker_logger), - ) - ) + worker_client = await env.connect_client( + runtime=_forwarding_runtime(worker_logger), ) - # Create client for worker with custom runtime logging - worker_client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, - runtime=worker_runtime, - ) + def predicate(record: logging.LogRecord) -> bool: + return ( + record.levelname == "ERROR" + and "[TMPRL1103] Attempted to upload payloads with size that exceeded the error limit." + in record.msg + ) - with ( - LogCapturer().logs_captured(worker_logger) as worker_logger_capturer, - LogCapturer().logs_captured(logging.getLogger()) as root_logger_capturer, - ): + with LogCapturer().logs_captured(worker_logger) as capturer: async with new_worker( worker_client, LargePayloadWorkflow, activities=[large_payload_activity] ) as worker: @@ -241,10 +110,7 @@ async def test_payload_size_error_workflow_result(env: WorkflowEnvironment): LargePayloadWorkflow.run, LargePayloadWorkflowInput( activity_input_data_size=0, - activity_output_data_size=0, - activity_exception_data_size=0, workflow_output_data_size=PAYLOAD_ERROR_LIMIT + 1024, - data="", ), id=f"workflow-{uuid.uuid4()}", task_queue=worker.task_queue, @@ -253,69 +119,29 @@ async def test_payload_size_error_workflow_result(env: WorkflowEnvironment): with pytest.raises(WorkflowFailureError) as err: await handle.result() - assert isinstance(err.value.cause, TimeoutError) assert err.value.cause.type == TimeoutType.START_TO_CLOSE - replayer = Replayer(workflows=[LargePayloadWorkflow]) - await replayer.replay_workflow(await handle.fetch_history()) - - def worker_logger_predicate(record: logging.LogRecord) -> bool: - return ( - record.levelname == "WARNING" - and "[TMPRL1103] Attempted to upload payloads with size that exceeded the error limit." - in record.msg - ) + # Core forwards logs on a buffered interval; poll while the capturer is attached. + async def error_forwarded() -> None: + assert capturer.find(predicate) is not None - assert worker_logger_capturer.find(worker_logger_predicate) + await assert_eventually(error_forwarded) - def root_logger_predicate(record: logging.LogRecord) -> bool: - return ( - record.levelname == "WARNING" - and "[TMPRL1103] Attempted to upload payloads with size that exceeded the error limit." - in record.msg + # Confirm worker reported a WorkflowTaskFailed with cause PAYLOADS_TOO_LARGE. + history = await handle.fetch_history() + assert any( + event.event_type + == temporalio.api.enums.v1.EventType.EVENT_TYPE_WORKFLOW_TASK_FAILED + and event.workflow_task_failed_event_attributes.cause + == temporalio.api.enums.v1.WorkflowTaskFailedCause.WORKFLOW_TASK_FAILED_CAUSE_PAYLOADS_TOO_LARGE + for event in history.events ) - assert root_logger_capturer.find(root_logger_predicate) - -async def test_payload_size_warning_workflow_result(client: Client): - config = client.config() - config["data_converter"] = dataclasses.replace( - temporalio.converter.default(), - payload_limits=PayloadLimitsConfig( - payload_size_warning=1024, - ), - ) - worker_client = Client(**config) - - with warnings.catch_warnings(record=True) as w: - async with new_worker( - worker_client, LargePayloadWorkflow, activities=[large_payload_activity] - ) as worker: - await client.execute_workflow( - LargePayloadWorkflow.run, - LargePayloadWorkflowInput( - activity_input_data_size=0, - activity_output_data_size=0, - activity_exception_data_size=0, - workflow_output_data_size=2 * 1024, - data="", - ), - id=f"workflow-{uuid.uuid4()}", - task_queue=worker.task_queue, - execution_timeout=timedelta(seconds=3), - ) - - assert len(w) == 1 - assert issubclass(w[-1].category, PayloadSizeWarning) - assert ( - "[TMPRL1103] Attempted to upload payloads with size that exceeded the warning limit." - in str(w[-1].message) - ) - - -async def test_payload_size_error_activity_input(env: WorkflowEnvironment): +async def test_disable_payload_error_limit_sends_to_server(env: WorkflowEnvironment): + """With the opt-out, worker does not pre-fail; the oversized payload reaches (and is rejected by) + the server.""" if env.supports_time_skipping: pytest.skip("Time-skipping server does not report payload limits.") @@ -323,273 +149,102 @@ async def test_payload_size_error_activity_input(env: WorkflowEnvironment): dev_server_extra_args=PAYLOAD_LIMITS_EXTRA_ARGS, dev_server_download_version=DEV_SERVER_DOWNLOAD_VERSION, ) as env: - # Create worker runtime with forwarded logger - worker_logger = logging.getLogger(f"log-{uuid.uuid4()}") - worker_runtime = Runtime( - telemetry=TelemetryConfig( - logging=LoggingConfig( - filter=TelemetryFilter(core_level="WARN", other_level="ERROR"), - forwarding=LogForwardingConfig(logger=worker_logger), - ) - ) - ) - - # Create client for worker with custom runtime logging - worker_client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, - runtime=worker_runtime, - ) - - with ( - LogCapturer().logs_captured(worker_logger) as worker_logger_capturer, - LogCapturer().logs_captured(logging.getLogger()) as root_logger_capturer, - ): - async with new_worker( - worker_client, LargePayloadWorkflow, activities=[large_payload_activity] - ) as worker: - handle = await env.client.start_workflow( + async with new_worker( + env.client, + LargePayloadWorkflow, + activities=[large_payload_activity], + disable_payload_error_limit=True, + ) as worker: + with pytest.raises(WorkflowFailureError) as err: + await env.client.execute_workflow( LargePayloadWorkflow.run, LargePayloadWorkflowInput( activity_input_data_size=PAYLOAD_ERROR_LIMIT + 1024, - activity_output_data_size=0, - activity_exception_data_size=0, workflow_output_data_size=0, - data="", ), id=f"workflow-{uuid.uuid4()}", task_queue=worker.task_queue, execution_timeout=timedelta(seconds=3), ) - with pytest.raises(WorkflowFailureError) as err: - await handle.result() - - assert isinstance(err.value.cause, TimeoutError) - - replayer = Replayer(workflows=[LargePayloadWorkflow]) - await replayer.replay_workflow(await handle.fetch_history()) - - def worker_logger_predicate(record: logging.LogRecord) -> bool: - return ( - record.levelname == "WARNING" - and "[TMPRL1103] Attempted to upload payloads with size that exceeded the error limit." - in record.msg - ) - - assert worker_logger_capturer.find(worker_logger_predicate) - - def root_logger_predicate(record: logging.LogRecord) -> bool: - return ( - record.levelname == "WARNING" - and "[TMPRL1103] Attempted to upload payloads with size that exceeded the error limit." - in record.msg - ) - - assert root_logger_capturer.find(root_logger_predicate) + assert isinstance(err.value.cause, TerminatedError) + assert ( + err.value.cause.message + == "BadScheduleActivityAttributes: ScheduleActivityTaskCommandAttributes.Input exceeds size limit." + ) -async def test_payload_size_warning_activity_input(client: Client): - config = client.config() - config["data_converter"] = dataclasses.replace( - temporalio.converter.default(), - payload_limits=PayloadLimitsConfig( - payload_size_warning=1024, - ), +async def test_payload_size_warning_forwarded(env: WorkflowEnvironment): + """The connection's warn threshold produces a forwarded [TMPRL1103] warning for over-threshold payloads.""" + worker_logger = logging.getLogger(f"log-{uuid.uuid4()}") + worker_client = await env.connect_client( + runtime=_forwarding_runtime(worker_logger), + payload_limits=PayloadLimitsConfig(payloads_warn_size=1024), ) - worker_client = Client(**config) - with warnings.catch_warnings(record=True) as w: + def predicate(record: logging.LogRecord) -> bool: + return ( + record.levelname == "WARNING" + and "[TMPRL1103] Attempted to upload payloads with size that exceeded the warning limit." + in record.msg + ) + + with LogCapturer().logs_captured(worker_logger) as capturer: async with new_worker( worker_client, LargePayloadWorkflow, activities=[large_payload_activity] ) as worker: - await client.execute_workflow( + await worker_client.execute_workflow( LargePayloadWorkflow.run, LargePayloadWorkflowInput( - activity_input_data_size=2 * 1024, - activity_output_data_size=0, - activity_exception_data_size=0, - workflow_output_data_size=0, - data="", + activity_input_data_size=0, + workflow_output_data_size=2 * 1024, ), id=f"workflow-{uuid.uuid4()}", task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=5), ) - assert len(w) == 1 - assert issubclass(w[-1].category, PayloadSizeWarning) - assert ( - "[TMPRL1103] Attempted to upload payloads with size that exceeded the warning limit." - in str(w[-1].message) - ) - - -async def test_payload_size_error_activity_exception(env: WorkflowEnvironment): - if env.supports_time_skipping: - pytest.skip("Time-skipping server does not report payload limits.") - - async with await WorkflowEnvironment.start_local( - dev_server_extra_args=PAYLOAD_LIMITS_EXTRA_ARGS, - dev_server_download_version=DEV_SERVER_DOWNLOAD_VERSION, - ) as env: - # Create worker runtime with forwarded logger - worker_logger = logging.getLogger(f"log-{uuid.uuid4()}") - worker_runtime = Runtime( - telemetry=TelemetryConfig( - logging=LoggingConfig( - filter=TelemetryFilter(core_level="WARN", other_level="ERROR"), - forwarding=LogForwardingConfig(logger=worker_logger), - ) - ) - ) - - # Create client for worker with custom runtime logging - worker_client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, - runtime=worker_runtime, - ) - - with ( - LogCapturer().logs_captured( - activity.logger.base_logger - ) as activity_logger_capturer, - ): - async with new_worker( - worker_client, LargePayloadWorkflow, activities=[large_payload_activity] - ) as worker: - handle = await env.client.start_workflow( - LargePayloadWorkflow.run, - LargePayloadWorkflowInput( - activity_input_data_size=0, - activity_output_data_size=0, - activity_exception_data_size=PAYLOAD_ERROR_LIMIT + 1024, - workflow_output_data_size=0, - data="", - ), - id=f"workflow-{uuid.uuid4()}", - task_queue=worker.task_queue, - ) - - with pytest.raises(WorkflowFailureError) as err: - await handle.result() - - assert isinstance(err.value.cause, ActivityError) - assert isinstance(err.value.cause.cause, ApplicationError) + # Core forwards logs on a buffered interval; poll while the capturer is attached. + async def warning_forwarded() -> None: + assert capturer.find(predicate) is not None - replayer = Replayer(workflows=[LargePayloadWorkflow]) - await replayer.replay_workflow(await handle.fetch_history()) + await assert_eventually(warning_forwarded) - def activity_logger_predicate(record: logging.LogRecord) -> bool: - return ( - record.levelname == "ERROR" - and "[TMPRL1103] Attempted to upload payloads with size that exceeded the error limit." - in record.msg - ) - - assert activity_logger_capturer.find(activity_logger_predicate) +async def test_memo_size_warning_forwarded(env: WorkflowEnvironment): + """The connection's memo warn threshold produces a forwarded [TMPRL1103] warning for an + over-threshold memo.""" + worker_logger = logging.getLogger(f"log-{uuid.uuid4()}") + worker_client = await env.connect_client( + runtime=_forwarding_runtime(worker_logger), + payload_limits=PayloadLimitsConfig(memo_warn_size=1024), + ) -async def test_payload_size_error_activity_result(env: WorkflowEnvironment): - if env.supports_time_skipping: - pytest.skip("Time-skipping server does not report payload limits.") - - async with await WorkflowEnvironment.start_local( - dev_server_extra_args=PAYLOAD_LIMITS_EXTRA_ARGS, - dev_server_download_version=DEV_SERVER_DOWNLOAD_VERSION, - ) as env: - # Create worker runtime with forwarded logger - worker_logger = logging.getLogger(f"log-{uuid.uuid4()}") - worker_runtime = Runtime( - telemetry=TelemetryConfig( - logging=LoggingConfig( - filter=TelemetryFilter(core_level="WARN", other_level="ERROR"), - forwarding=LogForwardingConfig(logger=worker_logger), - ) - ) - ) - - # Create client for worker with custom runtime logging - worker_client = await Client.connect( - env.client.service_client.config.target_host, - namespace=env.client.namespace, - runtime=worker_runtime, + def predicate(record: logging.LogRecord) -> bool: + return ( + record.levelname == "WARNING" + and "[TMPRL1103] Attempted to upload memo with size that exceeded the warning limit." + in record.msg ) - with ( - LogCapturer().logs_captured( - activity.logger.base_logger - ) as activity_logger_capturer, - ): - async with new_worker( - worker_client, LargePayloadWorkflow, activities=[large_payload_activity] - ) as worker: - handle = await env.client.start_workflow( - LargePayloadWorkflow.run, - LargePayloadWorkflowInput( - activity_input_data_size=0, - activity_output_data_size=PAYLOAD_ERROR_LIMIT + 1024, - activity_exception_data_size=0, - workflow_output_data_size=0, - data="", - ), - id=f"workflow-{uuid.uuid4()}", - task_queue=worker.task_queue, - ) - - with pytest.raises(WorkflowFailureError) as err: - await handle.result() - - assert isinstance(err.value.cause, ActivityError) - assert isinstance(err.value.cause.cause, ApplicationError) - - assert handle is not None - replayer = Replayer(workflows=[LargePayloadWorkflow]) - await replayer.replay_workflow(await handle.fetch_history()) - - def activity_logger_predicate(record: logging.LogRecord) -> bool: - return ( - hasattr(record, "__temporal_error_identifier") - and getattr(record, "__temporal_error_identifier") - == "PayloadSizeError" - and record.levelname == "WARNING" - and "[TMPRL1103] Attempted to upload payloads with size that exceeded the error limit." - in record.msg - ) - - assert activity_logger_capturer.find(activity_logger_predicate) - - -async def test_payload_size_warning_activity_result(client: Client): - config = client.config() - config["data_converter"] = dataclasses.replace( - temporalio.converter.default(), - payload_limits=PayloadLimitsConfig( - payload_size_warning=1024, - ), - ) - worker_client = Client(**config) - - with warnings.catch_warnings(record=True) as w: + with LogCapturer().logs_captured(worker_logger) as capturer: async with new_worker( worker_client, LargePayloadWorkflow, activities=[large_payload_activity] ) as worker: - await client.execute_workflow( + await worker_client.execute_workflow( LargePayloadWorkflow.run, LargePayloadWorkflowInput( activity_input_data_size=0, - activity_output_data_size=2 * 1024, - activity_exception_data_size=0, workflow_output_data_size=0, - data="", ), id=f"workflow-{uuid.uuid4()}", task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=5), + memo={"key": "a" * 2048}, ) - assert len(w) == 1 - assert issubclass(w[-1].category, PayloadSizeWarning) - assert ( - "[TMPRL1103] Attempted to upload payloads with size that exceeded the warning limit." - in str(w[-1].message) - ) + # Core forwards logs on a buffered interval; poll while the capturer is attached. + async def warning_forwarded() -> None: + assert capturer.find(predicate) is not None + + await assert_eventually(warning_forwarded) diff --git a/tests/worker/test_replayer.py b/tests/worker/test_replayer.py index 22d771556..52fed7416 100644 --- a/tests/worker/test_replayer.py +++ b/tests/worker/test_replayer.py @@ -10,6 +10,7 @@ import pytest +import temporalio.worker._workflow_instance from temporalio import activity, workflow from temporalio.client import Client, WorkflowFailureError, WorkflowHistory from temporalio.exceptions import ApplicationError @@ -81,6 +82,24 @@ def waiting(self) -> bool: return self._waiting +_WorkflowLogicFlag = temporalio.worker._workflow_instance._WorkflowLogicFlag +_SINGLE_BATCH_WORKFLOW_LOGIC_FLAG = ( + _WorkflowLogicFlag.PROCESS_WORKFLOW_ACTIVATION_JOBS_AS_SINGLE_BATCH +) + + +def _history_uses_workflow_logic_flag( + history: WorkflowHistory, + flag: _WorkflowLogicFlag, +) -> bool: + return any( + event.HasField("workflow_task_completed_event_attributes") + and int(flag) + in event.workflow_task_completed_event_attributes.sdk_metadata.lang_used_flags + for event in history.events + ) + + @pytest.mark.skipif(sys.version_info < (3, 12), reason="Skipping for < 3.12") async def test_replayer_workflow_complete(client: Client) -> None: # This test skips for versions < 3.12 because this is flaky due to CPython reimport issue: @@ -283,6 +302,7 @@ async def test_replayer_workflow_not_registered(client: Client) -> None: assert "SayHelloWorkflow is not registered" in str(err.value) +@pytest.mark.requires_local_server async def test_replayer_multiple_from_client( client: Client, env: WorkflowEnvironment ) -> None: @@ -311,6 +331,14 @@ async def test_replayer_multiple_from_client( ) await handle.result() + async def visible_run_ids() -> set[str]: + return { + workflow.run_id + async for workflow in client.list_workflows(f"WorkflowId = '{workflow_id}'") + } + + await assert_eq_eventually(set(expected_runs_and_non_det), visible_run_ids) + # Run replayer with list iterator mapped to histories and collect results async with Replayer(workflows=[SayHelloWorkflow]).workflow_replay_iterator( client.list_workflows(f"WorkflowId = '{workflow_id}'").map_histories() @@ -427,15 +455,12 @@ async def execute_workflow(self, input: ExecuteWorkflowInput) -> Any: return res -async def test_replayer_async_ordering() -> None: - """ - This test verifies that the order that asyncio tasks/coroutines are woken up matches the - order they were before changes to apply all jobs and then run the event loop, where previously - the event loop was ran after each "batch" of jobs. - """ - histories_and_expecteds = [ - ( +@pytest.mark.parametrize( + ("history_filename", "uses_single_batch", "expected"), + [ + pytest.param( "test_replayer_event_tracing.json", + False, [ "sig-before-sync", "sig-before-1", @@ -453,9 +478,33 @@ async def test_replayer_async_ordering() -> None: "timer-1", "timer-2", ], + id="legacy-event-tracing", + ), + pytest.param( + "test_replayer_event_tracing_single_batch.json", + True, + [ + "sig-before-sync", + "sig-before-1", + "timer-sync", + "act-sync", + "sig-before-2", + "act-1", + "act-2", + "sig-1-sync", + "sig-1-1", + "sig-1-2", + "update-1-sync", + "update-1-1", + "update-1-2", + "timer-1", + "timer-2", + ], + id="single-batch-event-tracing", ), - ( + pytest.param( "test_replayer_event_tracing_double_sig_at_start.json", + False, [ "sig-before-sync", "sig-before-1", @@ -473,29 +522,72 @@ async def test_replayer_async_ordering() -> None: "timer-1", "timer-2", ], + id="legacy-double-signal-at-start", ), - ] - for history, expected in histories_and_expecteds: - with Path(__file__).with_name(history).open() as f: - history = f.read() - await Replayer( - workflows=[SignalsActivitiesTimersUpdatesTracingWorkflow], - interceptors=[WorkerWorkflowResultInterceptor()], - ).replay_workflow(WorkflowHistory.from_json("fake", history)) - assert test_replayer_workflow_res == expected + pytest.param( + "test_replayer_event_tracing_double_sig_at_start_single_batch.json", + True, + [ + "sig-before-sync", + "sig-before-1", + "sig-1-sync", + "sig-1-1", + "timer-sync", + "act-sync", + "sig-before-2", + "sig-1-2", + "act-1", + "act-2", + "update-1-sync", + "update-1-1", + "update-1-2", + "timer-1", + "timer-2", + ], + id="single-batch-double-signal-at-start", + ), + ], +) +async def test_replayer_async_ordering( + history_filename: str, + uses_single_batch: bool, + expected: list[str], +) -> None: + """ + Verify legacy and single-batch histories replay with the asyncio scheduling order that their + original executions observed. + """ + with Path(__file__).with_name(history_filename).open() as f: + history = WorkflowHistory.from_json("fake", f.read()) + assert ( + _history_uses_workflow_logic_flag(history, _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG) + is uses_single_batch + ) + await Replayer( + workflows=[SignalsActivitiesTimersUpdatesTracingWorkflow], + interceptors=[WorkerWorkflowResultInterceptor()], + ).replay_workflow(history) + assert test_replayer_workflow_res == expected -async def test_replayer_alternate_async_ordering() -> None: +async def test_replayer_unflagged_history_uses_legacy_async_ordering() -> None: with ( Path(__file__) .with_name("test_replayer_event_tracing_alternate.json") .open() as f ): - history = f.read() - await Replayer( + history = WorkflowHistory.from_json("fake", f.read()) + assert not _history_uses_workflow_logic_flag( + history, _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG + ) + replayer = Replayer( workflows=[ActivityAndSignalsWhileWorkflowDown], interceptors=[WorkerWorkflowResultInterceptor()], - ).replay_workflow(WorkflowHistory.from_json("fake", history)) + ) + replayer._set_default_workflow_logic_flag( + _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG, enabled=True + ) + await replayer.replay_workflow(history) assert test_replayer_workflow_res == [ "act-start", "sig-1", diff --git a/tests/worker/test_replayer_event_tracing_double_sig_at_start_single_batch.json b/tests/worker/test_replayer_event_tracing_double_sig_at_start_single_batch.json new file mode 100644 index 000000000..ed0eae143 --- /dev/null +++ b/tests/worker/test_replayer_event_tracing_double_sig_at_start_single_batch.json @@ -0,0 +1,433 @@ +{ + "events": [ + { + "eventId": "1", + "eventTime": "2026-07-22T17:46:09.820508351Z", + "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_STARTED", + "taskId": "1048655", + "workflowExecutionStartedEventAttributes": { + "workflowType": { + "name": "SignalsActivitiesTimersUpdatesTracingWorkflow" + }, + "taskQueue": { + "name": "tq-104f9b8b-4835-4705-8f36-3fb3f0b9bce7", + "kind": "TASK_QUEUE_KIND_NORMAL" + }, + "workflowTaskTimeout": "10s", + "originalExecutionRunId": "019f8aef-5a1c-77c0-8c6f-4c42b8984e71", + "identity": "2468547@monolith", + "firstExecutionRunId": "019f8aef-5a1c-77c0-8c6f-4c42b8984e71", + "attempt": 1, + "firstWorkflowTaskBackoff": "0s", + "workflowId": "wf-50105b28-aad3-488b-b8b5-6cd3c168369c", + "priority": {} + } + }, + { + "eventId": "2", + "eventTime": "2026-07-22T17:46:09.820553958Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_SCHEDULED", + "taskId": "1048656", + "workflowTaskScheduledEventAttributes": { + "taskQueue": { + "name": "tq-104f9b8b-4835-4705-8f36-3fb3f0b9bce7", + "kind": "TASK_QUEUE_KIND_NORMAL" + }, + "startToCloseTimeout": "10s", + "attempt": 1 + } + }, + { + "eventId": "3", + "eventTime": "2026-07-22T17:46:09.823263829Z", + "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED", + "taskId": "1048661", + "workflowExecutionSignaledEventAttributes": { + "signalName": "dosig", + "input": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "ImJlZm9yZSI=" + } + ] + }, + "identity": "2468547@monolith", + "requestId": "0f1accfe-ffda-4af1-bc26-2a4b43007ef5" + } + }, + { + "eventId": "4", + "eventTime": "2026-07-22T17:46:09.826228197Z", + "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED", + "taskId": "1048663", + "workflowExecutionSignaledEventAttributes": { + "signalName": "dosig", + "input": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "IjEi" + } + ] + }, + "identity": "2468547@monolith", + "requestId": "7c76e2c6-c5c9-4bfa-9ef8-680e5fa0fdac" + } + }, + { + "eventId": "5", + "eventTime": "2026-07-22T17:46:09.911987108Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_STARTED", + "taskId": "1048665", + "workflowTaskStartedEventAttributes": { + "scheduledEventId": "2", + "identity": "2468547@monolith", + "requestId": "eb18fba3-0020-4bee-9824-68dee535759c", + "historySizeBytes": "600", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + } + } + }, + { + "eventId": "6", + "eventTime": "2026-07-22T17:46:10.029696095Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_COMPLETED", + "taskId": "1048669", + "workflowTaskCompletedEventAttributes": { + "scheduledEventId": "2", + "startedEventId": "5", + "identity": "2468547@monolith", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + }, + "sdkMetadata": { + "coreUsedFlags": [ + 2, + 3, + 1 + ], + "langUsedFlags": [ + 2 + ], + "sdkName": "temporal-python", + "sdkVersion": "1.30.0" + }, + "meteringMetadata": {} + } + }, + { + "eventId": "7", + "eventTime": "2026-07-22T17:46:10.029723573Z", + "eventType": "EVENT_TYPE_TIMER_STARTED", + "taskId": "1048670", + "timerStartedEventAttributes": { + "timerId": "1", + "startToFireTimeout": "0.100s", + "workflowTaskCompletedEventId": "6" + } + }, + { + "eventId": "8", + "eventTime": "2026-07-22T17:46:10.029791694Z", + "eventType": "EVENT_TYPE_ACTIVITY_TASK_SCHEDULED", + "taskId": "1048671", + "activityTaskScheduledEventAttributes": { + "activityId": "1", + "activityType": { + "name": "say_hello" + }, + "taskQueue": { + "name": "tq-104f9b8b-4835-4705-8f36-3fb3f0b9bce7", + "kind": "TASK_QUEUE_KIND_NORMAL" + }, + "header": {}, + "input": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "IkVuY2hpIg==" + } + ] + }, + "scheduleToCloseTimeout": "30s", + "scheduleToStartTimeout": "30s", + "startToCloseTimeout": "30s", + "heartbeatTimeout": "0s", + "workflowTaskCompletedEventId": "6", + "retryPolicy": { + "initialInterval": "1s", + "backoffCoefficient": 2.0, + "maximumInterval": "100s" + }, + "useWorkflowBuildId": true, + "priority": {} + } + }, + { + "eventId": "9", + "eventTime": "2026-07-22T17:46:10.031520468Z", + "eventType": "EVENT_TYPE_ACTIVITY_TASK_STARTED", + "taskId": "1048679", + "activityTaskStartedEventAttributes": { + "scheduledEventId": "8", + "identity": "2468547@monolith", + "requestId": "58a7bab7-a808-49ba-b906-3c07a281f078", + "attempt": 1, + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + } + } + }, + { + "eventId": "10", + "eventTime": "2026-07-22T17:46:10.034867651Z", + "eventType": "EVENT_TYPE_ACTIVITY_TASK_COMPLETED", + "taskId": "1048680", + "activityTaskCompletedEventAttributes": { + "result": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "IkhlbGxvLCBFbmNoaSEi" + } + ] + }, + "scheduledEventId": "8", + "startedEventId": "9", + "identity": "2468547@monolith" + } + }, + { + "eventId": "11", + "eventTime": "2026-07-22T17:46:10.034876231Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_SCHEDULED", + "taskId": "1048681", + "workflowTaskScheduledEventAttributes": { + "taskQueue": { + "name": "2468547@monolith-af9e32c231d44027a7fbb997838520bf", + "kind": "TASK_QUEUE_KIND_STICKY", + "normalName": "tq-104f9b8b-4835-4705-8f36-3fb3f0b9bce7" + }, + "startToCloseTimeout": "10s", + "attempt": 1 + } + }, + { + "eventId": "12", + "eventTime": "2026-07-22T17:46:10.035716599Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_STARTED", + "taskId": "1048685", + "workflowTaskStartedEventAttributes": { + "scheduledEventId": "11", + "identity": "2468547@monolith", + "requestId": "cf2bd6a2-3498-427d-920a-daf350415c70", + "historySizeBytes": "1389", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + } + } + }, + { + "eventId": "13", + "eventTime": "2026-07-22T17:46:10.039414094Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_COMPLETED", + "taskId": "1048689", + "workflowTaskCompletedEventAttributes": { + "scheduledEventId": "11", + "startedEventId": "12", + "identity": "2468547@monolith", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + }, + "sdkMetadata": {}, + "meteringMetadata": {} + } + }, + { + "eventId": "14", + "eventTime": "2026-07-22T17:46:10.112263987Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_SCHEDULED", + "taskId": "1048695", + "workflowTaskScheduledEventAttributes": { + "taskQueue": { + "name": "2468547@monolith-af9e32c231d44027a7fbb997838520bf", + "kind": "TASK_QUEUE_KIND_STICKY", + "normalName": "tq-104f9b8b-4835-4705-8f36-3fb3f0b9bce7" + }, + "startToCloseTimeout": "10s", + "attempt": 1 + } + }, + { + "eventId": "15", + "eventTime": "2026-07-22T17:46:10.112924510Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_STARTED", + "taskId": "1048696", + "workflowTaskStartedEventAttributes": { + "scheduledEventId": "14", + "identity": "2468547@monolith", + "requestId": "6e079d38-0b99-48eb-b1e9-1b8236232fb1", + "historySizeBytes": "1598", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + } + } + }, + { + "eventId": "16", + "eventTime": "2026-07-22T17:46:10.118047317Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_COMPLETED", + "taskId": "1048697", + "workflowTaskCompletedEventAttributes": { + "scheduledEventId": "14", + "startedEventId": "15", + "identity": "2468547@monolith", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + }, + "sdkMetadata": {}, + "meteringMetadata": {} + } + }, + { + "eventId": "17", + "eventTime": "2026-07-22T17:46:10.118078114Z", + "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED", + "taskId": "1048698", + "workflowExecutionUpdateAcceptedEventAttributes": { + "protocolInstanceId": "3078b22e-fa4a-4bb3-bf21-7502464f3f8e", + "acceptedRequestMessageId": "3078b22e-fa4a-4bb3-bf21-7502464f3f8e/request", + "acceptedRequestSequencingEventId": "14", + "acceptedRequest": { + "meta": { + "updateId": "3078b22e-fa4a-4bb3-bf21-7502464f3f8e", + "identity": "2468547@monolith" + }, + "input": { + "name": "doupdate", + "args": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "IjEi" + } + ] + } + } + } + } + }, + { + "eventId": "18", + "eventTime": "2026-07-22T17:46:10.118094048Z", + "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_COMPLETED", + "taskId": "1048699", + "workflowExecutionUpdateCompletedEventAttributes": { + "meta": { + "updateId": "3078b22e-fa4a-4bb3-bf21-7502464f3f8e", + "identity": "2468547@monolith" + }, + "outcome": { + "success": { + "payloads": [ + { + "metadata": { + "encoding": "YmluYXJ5L251bGw=" + } + } + ] + } + }, + "acceptedEventId": "17" + } + }, + { + "eventId": "19", + "eventTime": "2026-07-22T17:46:10.822704848Z", + "eventType": "EVENT_TYPE_TIMER_FIRED", + "taskId": "1048702", + "timerFiredEventAttributes": { + "timerId": "1", + "startedEventId": "7" + } + }, + { + "eventId": "20", + "eventTime": "2026-07-22T17:46:10.822723284Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_SCHEDULED", + "taskId": "1048703", + "workflowTaskScheduledEventAttributes": { + "taskQueue": { + "name": "2468547@monolith-af9e32c231d44027a7fbb997838520bf", + "kind": "TASK_QUEUE_KIND_STICKY", + "normalName": "tq-104f9b8b-4835-4705-8f36-3fb3f0b9bce7" + }, + "startToCloseTimeout": "10s", + "attempt": 1 + } + }, + { + "eventId": "21", + "eventTime": "2026-07-22T17:46:10.827860954Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_STARTED", + "taskId": "1048707", + "workflowTaskStartedEventAttributes": { + "scheduledEventId": "20", + "identity": "2468547@monolith", + "requestId": "facf828f-060f-4a8b-87cf-bdbb3a3297e9", + "historySizeBytes": "2430", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + } + } + }, + { + "eventId": "22", + "eventTime": "2026-07-22T17:46:10.843601707Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_COMPLETED", + "taskId": "1048711", + "workflowTaskCompletedEventAttributes": { + "scheduledEventId": "20", + "startedEventId": "21", + "identity": "2468547@monolith", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + }, + "sdkMetadata": {}, + "meteringMetadata": {} + } + }, + { + "eventId": "23", + "eventTime": "2026-07-22T17:46:10.843661263Z", + "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED", + "taskId": "1048712", + "workflowExecutionCompletedEventAttributes": { + "result": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "WyJzaWctYmVmb3JlLXN5bmMiLCJzaWctYmVmb3JlLTEiLCJzaWctMS1zeW5jIiwic2lnLTEtMSIsInRpbWVyLXN5bmMiLCJhY3Qtc3luYyIsInNpZy1iZWZvcmUtMiIsInNpZy0xLTIiLCJhY3QtMSIsImFjdC0yIiwidXBkYXRlLTEtc3luYyIsInVwZGF0ZS0xLTEiLCJ1cGRhdGUtMS0yIiwidGltZXItMSIsInRpbWVyLTIiXQ==" + } + ] + }, + "workflowTaskCompletedEventId": "22" + } + } + ] +} + diff --git a/tests/worker/test_replayer_event_tracing_single_batch.json b/tests/worker/test_replayer_event_tracing_single_batch.json new file mode 100644 index 000000000..42bab0b4f --- /dev/null +++ b/tests/worker/test_replayer_event_tracing_single_batch.json @@ -0,0 +1,479 @@ +{ + "events": [ + { + "eventId": "1", + "eventTime": "2026-07-22T17:46:08.798556582Z", + "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_STARTED", + "taskId": "1048587", + "workflowExecutionStartedEventAttributes": { + "workflowType": { + "name": "SignalsActivitiesTimersUpdatesTracingWorkflow" + }, + "taskQueue": { + "name": "tq-7049fd3e-356e-49d5-9047-2b97b454562e", + "kind": "TASK_QUEUE_KIND_NORMAL" + }, + "workflowTaskTimeout": "10s", + "originalExecutionRunId": "019f8aef-561e-787b-8565-31e78b4a889a", + "identity": "2468547@monolith", + "firstExecutionRunId": "019f8aef-561e-787b-8565-31e78b4a889a", + "attempt": 1, + "firstWorkflowTaskBackoff": "0s", + "workflowId": "wf-2dd53446-a657-4f47-9e47-a3edeb302c5f", + "priority": {} + } + }, + { + "eventId": "2", + "eventTime": "2026-07-22T17:46:08.798633092Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_SCHEDULED", + "taskId": "1048588", + "workflowTaskScheduledEventAttributes": { + "taskQueue": { + "name": "tq-7049fd3e-356e-49d5-9047-2b97b454562e", + "kind": "TASK_QUEUE_KIND_NORMAL" + }, + "startToCloseTimeout": "10s", + "attempt": 1 + } + }, + { + "eventId": "3", + "eventTime": "2026-07-22T17:46:08.801258084Z", + "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED", + "taskId": "1048593", + "workflowExecutionSignaledEventAttributes": { + "signalName": "dosig", + "input": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "ImJlZm9yZSI=" + } + ] + }, + "identity": "2468547@monolith", + "requestId": "c78a6332-adb5-4ff1-af4b-f55e1180d4d8" + } + }, + { + "eventId": "4", + "eventTime": "2026-07-22T17:46:08.949831498Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_STARTED", + "taskId": "1048595", + "workflowTaskStartedEventAttributes": { + "scheduledEventId": "2", + "identity": "2468547@monolith", + "requestId": "1e30e279-6cc7-497a-89f5-3e032d169c49", + "historySizeBytes": "477", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + } + } + }, + { + "eventId": "5", + "eventTime": "2026-07-22T17:46:09.032870384Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_COMPLETED", + "taskId": "1048599", + "workflowTaskCompletedEventAttributes": { + "scheduledEventId": "2", + "startedEventId": "4", + "identity": "2468547@monolith", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + }, + "sdkMetadata": { + "coreUsedFlags": [ + 3, + 1, + 2 + ], + "langUsedFlags": [ + 2 + ], + "sdkName": "temporal-python", + "sdkVersion": "1.30.0" + }, + "meteringMetadata": {} + } + }, + { + "eventId": "6", + "eventTime": "2026-07-22T17:46:09.032946093Z", + "eventType": "EVENT_TYPE_TIMER_STARTED", + "taskId": "1048600", + "timerStartedEventAttributes": { + "timerId": "1", + "startToFireTimeout": "0.100s", + "workflowTaskCompletedEventId": "5" + } + }, + { + "eventId": "7", + "eventTime": "2026-07-22T17:46:09.033008651Z", + "eventType": "EVENT_TYPE_ACTIVITY_TASK_SCHEDULED", + "taskId": "1048601", + "activityTaskScheduledEventAttributes": { + "activityId": "1", + "activityType": { + "name": "say_hello" + }, + "taskQueue": { + "name": "tq-7049fd3e-356e-49d5-9047-2b97b454562e", + "kind": "TASK_QUEUE_KIND_NORMAL" + }, + "header": {}, + "input": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "IkVuY2hpIg==" + } + ] + }, + "scheduleToCloseTimeout": "30s", + "scheduleToStartTimeout": "30s", + "startToCloseTimeout": "30s", + "heartbeatTimeout": "0s", + "workflowTaskCompletedEventId": "5", + "retryPolicy": { + "initialInterval": "1s", + "backoffCoefficient": 2.0, + "maximumInterval": "100s" + }, + "useWorkflowBuildId": true, + "priority": {} + } + }, + { + "eventId": "8", + "eventTime": "2026-07-22T17:46:09.035163378Z", + "eventType": "EVENT_TYPE_ACTIVITY_TASK_STARTED", + "taskId": "1048609", + "activityTaskStartedEventAttributes": { + "scheduledEventId": "7", + "identity": "2468547@monolith", + "requestId": "bd46ae91-1b79-4c8b-9366-e24395d0da8b", + "attempt": 1, + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + } + } + }, + { + "eventId": "9", + "eventTime": "2026-07-22T17:46:09.039131108Z", + "eventType": "EVENT_TYPE_ACTIVITY_TASK_COMPLETED", + "taskId": "1048610", + "activityTaskCompletedEventAttributes": { + "result": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "IkhlbGxvLCBFbmNoaSEi" + } + ] + }, + "scheduledEventId": "7", + "startedEventId": "8", + "identity": "2468547@monolith" + } + }, + { + "eventId": "10", + "eventTime": "2026-07-22T17:46:09.039143351Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_SCHEDULED", + "taskId": "1048611", + "workflowTaskScheduledEventAttributes": { + "taskQueue": { + "name": "2468547@monolith-3a1c4b9f82644345a21e1663c393c0d0", + "kind": "TASK_QUEUE_KIND_STICKY", + "normalName": "tq-7049fd3e-356e-49d5-9047-2b97b454562e" + }, + "startToCloseTimeout": "10s", + "attempt": 1 + } + }, + { + "eventId": "11", + "eventTime": "2026-07-22T17:46:09.040109929Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_STARTED", + "taskId": "1048615", + "workflowTaskStartedEventAttributes": { + "scheduledEventId": "10", + "identity": "2468547@monolith", + "requestId": "d3d3c3c3-5c9f-4570-8d8b-cce1f5856a4f", + "historySizeBytes": "1266", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + } + } + }, + { + "eventId": "12", + "eventTime": "2026-07-22T17:46:09.043328426Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_COMPLETED", + "taskId": "1048619", + "workflowTaskCompletedEventAttributes": { + "scheduledEventId": "10", + "startedEventId": "11", + "identity": "2468547@monolith", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + }, + "sdkMetadata": {}, + "meteringMetadata": {} + } + }, + { + "eventId": "13", + "eventTime": "2026-07-22T17:46:09.146751311Z", + "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED", + "taskId": "1048621", + "workflowExecutionSignaledEventAttributes": { + "signalName": "dosig", + "input": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "IjEi" + } + ] + }, + "identity": "2468547@monolith", + "requestId": "f18a65f6-8773-425d-b8f6-d1099056f758" + } + }, + { + "eventId": "14", + "eventTime": "2026-07-22T17:46:09.146757799Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_SCHEDULED", + "taskId": "1048622", + "workflowTaskScheduledEventAttributes": { + "taskQueue": { + "name": "2468547@monolith-3a1c4b9f82644345a21e1663c393c0d0", + "kind": "TASK_QUEUE_KIND_STICKY", + "normalName": "tq-7049fd3e-356e-49d5-9047-2b97b454562e" + }, + "startToCloseTimeout": "10s", + "attempt": 1 + } + }, + { + "eventId": "15", + "eventTime": "2026-07-22T17:46:09.147668735Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_STARTED", + "taskId": "1048626", + "workflowTaskStartedEventAttributes": { + "scheduledEventId": "14", + "identity": "2468547@monolith", + "requestId": "7f9aa9f0-4bef-44a3-93f4-744ff1db8eb4", + "historySizeBytes": "1724", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + } + } + }, + { + "eventId": "16", + "eventTime": "2026-07-22T17:46:09.151221140Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_COMPLETED", + "taskId": "1048630", + "workflowTaskCompletedEventAttributes": { + "scheduledEventId": "14", + "startedEventId": "15", + "identity": "2468547@monolith", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + }, + "sdkMetadata": {}, + "meteringMetadata": {} + } + }, + { + "eventId": "17", + "eventTime": "2026-07-22T17:46:09.151638714Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_SCHEDULED", + "taskId": "1048633", + "workflowTaskScheduledEventAttributes": { + "taskQueue": { + "name": "2468547@monolith-3a1c4b9f82644345a21e1663c393c0d0", + "kind": "TASK_QUEUE_KIND_STICKY", + "normalName": "tq-7049fd3e-356e-49d5-9047-2b97b454562e" + }, + "startToCloseTimeout": "10s", + "attempt": 1 + } + }, + { + "eventId": "18", + "eventTime": "2026-07-22T17:46:09.151645790Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_STARTED", + "taskId": "1048634", + "workflowTaskStartedEventAttributes": { + "scheduledEventId": "17", + "identity": "2468547@monolith", + "requestId": "request-from-RespondWorkflowTaskCompleted", + "historySizeBytes": "1933", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + } + } + }, + { + "eventId": "19", + "eventTime": "2026-07-22T17:46:09.155070433Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_COMPLETED", + "taskId": "1048635", + "workflowTaskCompletedEventAttributes": { + "scheduledEventId": "17", + "startedEventId": "18", + "identity": "2468547@monolith", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + }, + "sdkMetadata": {}, + "meteringMetadata": {} + } + }, + { + "eventId": "20", + "eventTime": "2026-07-22T17:46:09.155126964Z", + "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED", + "taskId": "1048636", + "workflowExecutionUpdateAcceptedEventAttributes": { + "protocolInstanceId": "dd163639-396a-440a-b950-00ac2f2ee490", + "acceptedRequestMessageId": "dd163639-396a-440a-b950-00ac2f2ee490/request", + "acceptedRequestSequencingEventId": "17", + "acceptedRequest": { + "meta": { + "updateId": "dd163639-396a-440a-b950-00ac2f2ee490", + "identity": "2468547@monolith" + }, + "input": { + "name": "doupdate", + "args": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "IjEi" + } + ] + } + } + } + } + }, + { + "eventId": "21", + "eventTime": "2026-07-22T17:46:09.155192377Z", + "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_COMPLETED", + "taskId": "1048637", + "workflowExecutionUpdateCompletedEventAttributes": { + "meta": { + "updateId": "dd163639-396a-440a-b950-00ac2f2ee490", + "identity": "2468547@monolith" + }, + "outcome": { + "success": { + "payloads": [ + { + "metadata": { + "encoding": "YmluYXJ5L251bGw=" + } + } + ] + } + }, + "acceptedEventId": "20" + } + }, + { + "eventId": "22", + "eventTime": "2026-07-22T17:46:09.801318803Z", + "eventType": "EVENT_TYPE_TIMER_FIRED", + "taskId": "1048640", + "timerFiredEventAttributes": { + "timerId": "1", + "startedEventId": "6" + } + }, + { + "eventId": "23", + "eventTime": "2026-07-22T17:46:09.801325729Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_SCHEDULED", + "taskId": "1048641", + "workflowTaskScheduledEventAttributes": { + "taskQueue": { + "name": "2468547@monolith-3a1c4b9f82644345a21e1663c393c0d0", + "kind": "TASK_QUEUE_KIND_STICKY", + "normalName": "tq-7049fd3e-356e-49d5-9047-2b97b454562e" + }, + "startToCloseTimeout": "10s", + "attempt": 1 + } + }, + { + "eventId": "24", + "eventTime": "2026-07-22T17:46:09.802775220Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_STARTED", + "taskId": "1048645", + "workflowTaskStartedEventAttributes": { + "scheduledEventId": "23", + "identity": "2468547@monolith", + "requestId": "3d1c5327-09f4-4434-8880-f181a287c207", + "historySizeBytes": "2770", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + } + } + }, + { + "eventId": "25", + "eventTime": "2026-07-22T17:46:09.808121470Z", + "eventType": "EVENT_TYPE_WORKFLOW_TASK_COMPLETED", + "taskId": "1048649", + "workflowTaskCompletedEventAttributes": { + "scheduledEventId": "23", + "startedEventId": "24", + "identity": "2468547@monolith", + "workerVersion": { + "buildId": "d7dd3d4f7dae90ccfe98229c9f116c9a" + }, + "sdkMetadata": {}, + "meteringMetadata": {} + } + }, + { + "eventId": "26", + "eventTime": "2026-07-22T17:46:09.808174063Z", + "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED", + "taskId": "1048650", + "workflowExecutionCompletedEventAttributes": { + "result": { + "payloads": [ + { + "metadata": { + "encoding": "anNvbi9wbGFpbg==" + }, + "data": "WyJzaWctYmVmb3JlLXN5bmMiLCJzaWctYmVmb3JlLTEiLCJ0aW1lci1zeW5jIiwiYWN0LXN5bmMiLCJzaWctYmVmb3JlLTIiLCJhY3QtMSIsImFjdC0yIiwic2lnLTEtc3luYyIsInNpZy0xLTEiLCJzaWctMS0yIiwidXBkYXRlLTEtc3luYyIsInVwZGF0ZS0xLTEiLCJ1cGRhdGUtMS0yIiwidGltZXItMSIsInRpbWVyLTIiXQ==" + } + ] + }, + "workflowTaskCompletedEventId": "25" + } + } + ] +} + diff --git a/tests/worker/test_update_with_start.py b/tests/worker/test_update_with_start.py index 2ceb5e91b..0b3368725 100644 --- a/tests/worker/test_update_with_start.py +++ b/tests/worker/test_update_with_start.py @@ -192,6 +192,7 @@ async def _do_test( id_conflict_policy: WorkflowIDConflictPolicy, expect_error_when_workflow_exists: ExpectErrorWhenWorkflowExists, ): + workflow_id = f"{workflow_id}-{uuid.uuid4()}" await self._do_execute_update_test( client, workflow_id + "-execute-update", @@ -336,6 +337,7 @@ async def test_update_with_start_sets_first_execution_run_id( WorkflowForUpdateWithStartTest, activities=[activity_called_by_update], ) as worker: + workflow_id_prefix = f"wid-{uuid.uuid4()}" def make_start_op(workflow_id: str): return WithStartWorkflowOperation( @@ -348,7 +350,7 @@ def make_start_op(workflow_id: str): # conflict policy is FAIL # First UWS succeeds and sets the first execution run ID - start_op_1 = make_start_op("wid-1") + start_op_1 = make_start_op(f"{workflow_id_prefix}-1") update_handle_1 = await client.start_update_with_start_workflow( WorkflowForUpdateWithStartTest.my_non_blocking_update, "1", @@ -360,7 +362,7 @@ def make_start_op(workflow_id: str): # Second UWS start fails because the workflow already exists # first execution run ID is not set on the second UWS handle - start_op_2 = make_start_op("wid-1") + start_op_2 = make_start_op(f"{workflow_id_prefix}-1") for aw in [ client.start_update_with_start_workflow( @@ -375,7 +377,7 @@ def make_start_op(workflow_id: str): await aw # Third UWS start succeeds, but the update fails after acceptance - start_op_3 = make_start_op("wid-2") + start_op_3 = make_start_op(f"{workflow_id_prefix}-2") update_handle_3 = await client.start_update_with_start_workflow( WorkflowForUpdateWithStartTest.my_non_blocking_update, "fail-after-acceptance", @@ -394,7 +396,7 @@ def make_start_op(workflow_id: str): assert await wf_handle_3.result() == "workflow-result-0" # Fourth UWS is same as third, but we use execute_update instead of start_update. - start_op_4 = make_start_op("wid-3") + start_op_4 = make_start_op(f"{workflow_id_prefix}-3") with pytest.raises(WorkflowUpdateFailedError): await client.execute_update_with_start_workflow( WorkflowForUpdateWithStartTest.my_non_blocking_update, diff --git a/tests/worker/test_visitor.py b/tests/worker/test_visitor.py index bd4004625..d9606624e 100644 --- a/tests/worker/test_visitor.py +++ b/tests/worker/test_visitor.py @@ -212,6 +212,58 @@ async def test_visit_payloads_on_other_commands(): assert ur.completed.metadata["visited"] +async def test_system_nexus_envelope_is_detected_in_generic_payload_field(): + class SystemNexusVisitor(Visitor): + def __init__(self) -> None: + self.visited_payload_count = 0 + self.system_envelope_count = 0 + + async def visit_payload(self, payload: Payload) -> None: + self.visited_payload_count += 1 + await super().visit_payload(payload) + + async def visit_payloads(self, payloads: MutableSequence[Payload]) -> None: + self.visited_payload_count += len(payloads) + await super().visit_payloads(payloads) + + async def visit_system_nexus_envelope(self, payload: Payload) -> None: + _ = payload + self.system_envelope_count += 1 + + system_request = workflowservice_pb2.SignalWithStartWorkflowExecutionRequest( + input=Payloads(payloads=[Payload(data=b"workflow-input")]), + ) + payload_converter = nexus_system._get_payload_converter( + temporalio.converter.default().payload_converter + ) + system_payload = payload_converter.to_payload(system_request) + assert system_payload is not None + comp = WorkflowActivationCompletion( + run_id="3", + successful=Success( + commands=[ + WorkflowCommand( + update_response=UpdateResponse(completed=system_payload), + ) + ] + ), + ) + visitor = SystemNexusVisitor() + + await PayloadVisitor(concurrency_limit=3).visit(visitor, comp) + + completed = comp.successful.commands[0].update_response.completed + assert completed.metadata["__temporal_system_payload"] == b"true" + assert "visited" not in completed.metadata + decoded = payload_converter.from_payload(completed) + assert isinstance( + decoded, workflowservice_pb2.SignalWithStartWorkflowExecutionRequest + ) + assert decoded.input.payloads[0].metadata["visited"] == b"True" + assert visitor.visited_payload_count == 1 + assert visitor.system_envelope_count == 1 + + async def test_concurrent_throughput(): """Demonstrate that concurrent visitation is faster than serialized for I/O-bound codecs.""" N_CMDS = 10 @@ -357,7 +409,9 @@ async def _visit(self) -> None: finally: active_visits -= 1 - payload_converter = nexus_system.get_payload_converter() + payload_converter = nexus_system._get_payload_converter( + temporalio.converter.PayloadConverter.default + ) system_request = workflowservice_pb2.SignalWithStartWorkflowExecutionRequest( input=Payloads(payloads=[Payload(data=b"workflow-input")]), signal_input=Payloads(payloads=[Payload(data=b"signal-input")]), diff --git a/tests/worker/test_worker.py b/tests/worker/test_worker.py index dda754a5b..57614c21e 100644 --- a/tests/worker/test_worker.py +++ b/tests/worker/test_worker.py @@ -399,6 +399,7 @@ def my_signal(self, value: str) -> None: workflow.logger.info(f"Signal: {value}") +@pytest.mark.requires_local_server async def test_custom_slot_supplier(client: Client, env: WorkflowEnvironment): if env.supports_time_skipping: pytest.skip("Nexus tests don't work under Java test server") @@ -589,6 +590,95 @@ def release_slot(self, ctx: SlotReleaseContext) -> None: await asyncio.sleep(1) +@activity.defn +async def heartbeating_activity(beats: int) -> None: + for i in range(beats): + activity.heartbeat(i) + await asyncio.sleep(0) + + +@workflow.defn +class HeartbeatingFanOutWorkflow: + @workflow.run + async def run(self, total: int, beats: int, window: int) -> None: + in_flight = asyncio.Semaphore(window) + + async def run_one() -> None: + async with in_flight: + await workflow.execute_activity( + heartbeating_activity, + beats, + start_to_close_timeout=timedelta(minutes=1), + heartbeat_timeout=timedelta(seconds=30), + ) + + await asyncio.gather(*(run_one() for _ in range(total))) + + +async def test_custom_slot_supplier_with_heartbeating_activities(client: Client): + """Regression test for the GIL <-> core-mutex deadlock (#1642). + + Any custom slot supplier plus heartbeating activities used to wedge the + worker permanently: the heartbeat FFI held the GIL while blocking on + core's outstanding-activity lock, while an activity task start invoked + mark_slot_used (which acquires the GIL) under that same lock. The + supplier below is deliberately trivial - the deadlock was in the calling + convention, not in what the callbacks do. + + NOTE: on regression this test HANGS rather than fails - the deadlock + freezes the event loop, so no in-process timeout (including the + wait_for below) can fire, and only a CI-level job timeout kills it. + """ + + class CappedSlotSupplier(CustomSlotSupplier): + def __init__(self, max_slots: int) -> None: + self.max_slots = max_slots + self.used = 0 + + async def reserve_slot(self, ctx: SlotReserveContext) -> SlotPermit: + while True: + permit = self.try_reserve_slot(ctx) + if permit is not None: + return permit + await asyncio.sleep(0.005) + + def try_reserve_slot(self, ctx: SlotReserveContext) -> SlotPermit | None: + # Called with the GIL held, so no extra lock is needed + if self.used >= self.max_slots: + return None + self.used += 1 + return SlotPermit() + + def mark_slot_used(self, ctx: SlotMarkUsedContext) -> None: + return None + + def release_slot(self, ctx: SlotReleaseContext) -> None: + self.used = max(0, self.used - 1) + + fixed = FixedSizeSlotSupplier(100) + tuner = WorkerTuner.create_composite( + workflow_supplier=fixed, + # A small cap keeps activity starts (and thus mark_slot_used calls) + # churning against the heartbeat FFI + activity_supplier=CappedSlotSupplier(8), + local_activity_supplier=fixed, + nexus_supplier=fixed, + ) + async with new_worker( + client, + HeartbeatingFanOutWorkflow, + activities=[heartbeating_activity], + tuner=tuner, + ) as w: + wf1 = await client.start_workflow( + HeartbeatingFanOutWorkflow.run, + args=[600, 50, 150], + id=f"heartbeating-slot-supplier-{uuid.uuid4()}", + task_queue=w.task_queue, + ) + await asyncio.wait_for(wf1.result(), timeout=120) + + @workflow.defn( name="DeploymentVersioningWorkflow", versioning_behavior=VersioningBehavior.AUTO_UPGRADE, @@ -666,6 +756,7 @@ async def test_worker_with_worker_deployment_config( pytest.skip("Test Server doesn't support worker deployments") deployment_name = f"deployment-{uuid.uuid4()}" + workflow_id_prefix = f"basic-versioning-{uuid.uuid4()}" worker_v1 = WorkerDeploymentVersion(deployment_name=deployment_name, build_id="1.0") worker_v2 = WorkerDeploymentVersion(deployment_name=deployment_name, build_id="2.0") worker_v3 = WorkerDeploymentVersion(deployment_name=deployment_name, build_id="3.0") @@ -708,7 +799,7 @@ async def test_worker_with_worker_deployment_config( # Start workflow 1 which will use the 1.0 worker on auto-upgrade wf1 = await client.start_workflow( DeploymentVersioningWorkflowV1AutoUpgrade.run, - id="basic-versioning-v1", + id=f"{workflow_id_prefix}-v1", task_queue=w1.task_queue, ) assert "v1" == await wf1.query("state") @@ -720,7 +811,7 @@ async def test_worker_with_worker_deployment_config( wf2 = await client.start_workflow( DeploymentVersioningWorkflowV2Pinned.run, - id="basic-versioning-v2", + id=f"{workflow_id_prefix}-v2", task_queue=w1.task_queue, ) assert "v2" == await wf2.query("state") @@ -732,7 +823,7 @@ async def test_worker_with_worker_deployment_config( wf3 = await client.start_workflow( DeploymentVersioningWorkflowV3AutoUpgrade.run, - id="basic-versioning-v3", + id=f"{workflow_id_prefix}-v3", task_queue=w1.task_queue, ) assert "v3" == await wf3.query("state") @@ -784,9 +875,15 @@ async def test_worker_deployment_ramp(client: Client, env: WorkflowEnvironment): client, describe_resp.conflict_token, v1 ) ).conflict_token + await wait_for_worker_deployment_routing_config_propagation( + client, deployment_name, v1.build_id + ) conflict_token = ( await set_ramping_version(client, conflict_token, v2, 100) ).conflict_token + await wait_for_worker_deployment_routing_config_propagation( + client, deployment_name, v1.build_id, v2.build_id, 100 + ) # Run workflows and verify they run on v2 for i in range(3): @@ -803,6 +900,9 @@ async def test_worker_deployment_ramp(client: Client, env: WorkflowEnvironment): conflict_token = ( await set_ramping_version(client, conflict_token, v2, 0) ).conflict_token + await wait_for_worker_deployment_routing_config_propagation( + client, deployment_name, v1.build_id, v2.build_id, 0 + ) for i in range(3): wfa = await client.start_workflow( DeploymentVersioningWorkflowV1AutoUpgrade.run, @@ -815,6 +915,9 @@ async def test_worker_deployment_ramp(client: Client, env: WorkflowEnvironment): # Set ramp to 50 and eventually verify workflows run on both versions await set_ramping_version(client, conflict_token, v2, 50) + await wait_for_worker_deployment_routing_config_propagation( + client, deployment_name, v1.build_id, v2.build_id, 50 + ) seen_results = set() async def run_and_record(): @@ -1117,7 +1220,9 @@ async def test_workflows_can_use_versioning_override( ) -async def test_can_run_autoscaling_polling_worker(client: Client): +async def test_can_run_autoscaling_polling_worker( + client: Client, env: WorkflowEnvironment +): # Create new runtime with Prom server prom_addr = f"127.0.0.1:{find_free_port()}" runtime = Runtime( @@ -1125,9 +1230,7 @@ async def test_can_run_autoscaling_polling_worker(client: Client): metrics=PrometheusConfig(bind_address=prom_addr), ) ) - client = await Client.connect( - client.service_client.config.target_host, - namespace=client.namespace, + client = await env.connect_client( runtime=runtime, ) @@ -1228,6 +1331,7 @@ async def wait_for_worker_deployment_routing_config_propagation( deployment_name: str, expected_current_build_id: str, expected_ramping_build_id: str = "", + expected_ramping_percentage: float | None = None, ) -> None: """Wait for routing config to be propagated to all task queues.""" import temporalio.api.enums.v1 @@ -1250,6 +1354,11 @@ async def check() -> bool: != expected_ramping_build_id ): return False + if ( + expected_ramping_percentage is not None + and routing_config.ramping_version_percentage != expected_ramping_percentage + ): + return False state = resp.worker_deployment_info.routing_config_update_state if ( state @@ -1378,15 +1487,14 @@ def test_fork_use_worker( self.run(mp_fork_ctx) -async def test_activity_client_updates_when_worker_client_changes(client: Client): +async def test_activity_client_updates_when_worker_client_changes( + client: Client, env: WorkflowEnvironment +): """Test that activities get the updated client when worker.client is changed.""" # Create a second client (simulating a new client after cert rotation) # Must use the same runtime - client2 = await Client.connect( - client.service_client.config.target_host, - namespace=client.namespace, + client2 = await env.connect_client( data_converter=client.data_converter, - runtime=client.service_client.config.runtime, ) captured_clients: list[Client] = [] @@ -1655,6 +1763,37 @@ def test_worker_config_matches_init_params(): ) +async def test_worker_max_eager_activity_reservations_per_workflow_task_config( + client: Client, +): + worker = Worker( + client, + workflows=[SimpleWorkflow], + task_queue=f"task-queue-{uuid.uuid4()}", + max_eager_activity_reservations_per_workflow_task=7, + ) + assert worker.config().get("max_eager_activity_reservations_per_workflow_task") == 7 + + +@pytest.mark.parametrize("value", [0, -1]) +async def test_worker_rejects_non_positive_max_eager_activity_reservations( + client: Client, value: int +): + with pytest.raises( + ValueError, + match=( + "max_eager_activity_reservations_per_workflow_task must be positive; " + "use disable_eager_activity_execution=True to disable eager activity execution" + ), + ): + Worker( + client, + workflows=[SimpleWorkflow], + task_queue=f"task-queue-{uuid.uuid4()}", + max_eager_activity_reservations_per_workflow_task=value, + ) + + async def test_worker_debug_mode(client: Client): worker = Worker( client, diff --git a/tests/worker/test_workflow.py b/tests/worker/test_workflow.py index d20077cf5..cca6e779d 100644 --- a/tests/worker/test_workflow.py +++ b/tests/worker/test_workflow.py @@ -41,6 +41,7 @@ import temporalio.converter._extstore import temporalio.worker import temporalio.worker._command_aware_visitor +import temporalio.worker._workflow_instance import temporalio.workflow from temporalio import activity, workflow from temporalio.api.common.v1 import Payload, Payloads, WorkflowExecution @@ -114,6 +115,7 @@ from temporalio.worker import ( ExecuteWorkflowInput, HandleSignalInput, + Replayer, UnsandboxedWorkflowRunner, Worker, WorkflowInstance, @@ -306,6 +308,7 @@ def get_history_info(self) -> HistoryInfo: ) +@pytest.mark.requires_local_server async def test_workflow_history_info( client: Client, env: WorkflowEnvironment, continue_as_new_suggest_history_count: int ): @@ -1246,7 +1249,103 @@ async def run(self) -> str: return "cancelled" -async def test_workflow_cancel_before_run(client: Client): +_WorkflowLogicFlag = temporalio.worker._workflow_instance._WorkflowLogicFlag +_SINGLE_BATCH_WORKFLOW_LOGIC_FLAG = ( + _WorkflowLogicFlag.PROCESS_WORKFLOW_ACTIVATION_JOBS_AS_SINGLE_BATCH +) + + +def test_single_batch_workflow_activation_jobs_default_disabled() -> None: + assert ( + _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG + not in temporalio.worker._workflow_instance._DEFAULT_ENABLED_WORKFLOW_LOGIC_FLAGS + ) + + +@workflow.defn +class EnableWorkflowLogicFlagAfterReplayWorkflow: + def __init__(self) -> None: + self._ready = False + self._finish = False + + @workflow.run + async def run(self) -> str: + self._ready = True + await workflow.wait_condition(lambda: self._finish) + return "done" + + @workflow.signal + def finish(self) -> None: + self._finish = True + + @workflow.query + def ready(self) -> bool: + return self._ready + + +async def test_workflow_logic_flag_enabled_after_replay(client: Client) -> None: + task_queue = str(uuid.uuid4()) + handle = await client.start_workflow( + EnableWorkflowLogicFlagAfterReplayWorkflow.run, + id=f"workflow-{uuid.uuid4()}", + task_queue=task_queue, + ) + + async with new_worker( + client, + EnableWorkflowLogicFlagAfterReplayWorkflow, + task_queue=task_queue, + # The Java test server does not reliably reschedule an abandoned sticky + # task after its timeout, so keep events sent after this worker stops on + # the normal queue. + max_cached_workflows=0, + ): + + async def ready() -> bool: + return await handle.query(EnableWorkflowLogicFlagAfterReplayWorkflow.ready) + + await assert_eq_eventually(True, ready) + + await handle.signal(EnableWorkflowLogicFlagAfterReplayWorkflow.finish) + + runner = CustomWorkflowRunner() + worker = new_worker( + client, + EnableWorkflowLogicFlagAfterReplayWorkflow, + task_queue=task_queue, + workflow_runner=runner, + ) + worker._set_default_workflow_logic_flag( + _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG, enabled=True + ) + async with worker: + assert await handle.result() == "done" + + assert any(activation.is_replaying for activation, _ in runner._pairs) + assert any( + not activation.is_replaying + and _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG + in completion.successful.used_internal_flags + for activation, completion in runner._pairs + ) + + history = await handle.fetch_history() + workflow_task_flags = [ + event.workflow_task_completed_event_attributes.sdk_metadata.lang_used_flags + for event in history.events + if event.HasField("workflow_task_completed_event_attributes") + ] + assert _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG not in workflow_task_flags[0] + assert any( + _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG in flags for flags in workflow_task_flags[1:] + ) + await Replayer( + workflows=[EnableWorkflowLogicFlagAfterReplayWorkflow] + ).replay_workflow(history) + + +@pytest.mark.parametrize("single_batch", [False, True]) +async def test_workflow_cancel_before_run(client: Client, single_batch: bool): # Start the workflow _and_ send cancel before even starting the workflow task_queue = str(uuid.uuid4()) handle = await client.start_workflow( @@ -1256,10 +1355,248 @@ async def test_workflow_cancel_before_run(client: Client): ) await handle.cancel() # Start worker and wait for result - async with new_worker(client, TrapCancelWorkflow, task_queue=task_queue): + worker = new_worker(client, TrapCancelWorkflow, task_queue=task_queue) + if single_batch: + worker._set_default_workflow_logic_flag( + _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG, enabled=True + ) + async with worker: assert "cancelled" == await handle.result() +@workflow.defn +class CancelAtWaitConditionWorkflow: + def __init__(self) -> None: + self._ready = False + self._proceed = False + self._waiting_after_cancel = False + self._finish_after_cancel = False + + @workflow.run + async def run(self, timeout: bool) -> str: + self._ready = True + try: + await workflow.wait_condition( + lambda: self._proceed, timeout=1000 if timeout else None + ) + except asyncio.CancelledError: + # A caught cancellation must not be raised again merely because a + # later wait sees the already-recorded workflow cancellation. + self._waiting_after_cancel = True + await workflow.wait_condition(lambda: self._finish_after_cancel) + return "cancelled" + return "condition" + + @workflow.signal + def proceed(self) -> None: + self._proceed = True + + @workflow.signal + def finish_after_cancel(self) -> None: + self._finish_after_cancel = True + + @workflow.query + def ready(self) -> bool: + return self._ready + + @workflow.query + def waiting_after_cancel(self) -> bool: + return self._waiting_after_cancel + + +@pytest.mark.parametrize("timeout", [False, True]) +async def test_workflow_cancel_and_condition_ready_in_same_activation( + client: Client, timeout: bool +): + task_queue = str(uuid.uuid4()) + runner = CustomWorkflowRunner() + handle = await client.start_workflow( + CancelAtWaitConditionWorkflow.run, + timeout, + id=f"workflow-{uuid.uuid4()}", + task_queue=task_queue, + ) + + worker = new_worker( + client, + CancelAtWaitConditionWorkflow, + task_queue=task_queue, + workflow_runner=runner, + # The Java test server does not reliably reschedule an abandoned sticky + # task after its timeout, so keep events sent after this worker stops on + # the normal queue. + max_cached_workflows=0, + ) + worker._set_default_workflow_logic_flag( + _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG, enabled=True + ) + async with worker: + + async def ready() -> bool: + return await handle.query(CancelAtWaitConditionWorkflow.ready) + + await assert_eq_eventually(True, ready) + + # Keep the worker offline so the signal and cancellation are delivered in + # one activation when polling resumes. + await handle.signal(CancelAtWaitConditionWorkflow.proceed) + await handle.cancel() + + worker = new_worker( + client, + CancelAtWaitConditionWorkflow, + task_queue=task_queue, + workflow_runner=runner, + ) + worker._set_default_workflow_logic_flag( + _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG, enabled=True + ) + async with worker: + + async def waiting_after_cancel() -> bool: + return await handle.query( + CancelAtWaitConditionWorkflow.waiting_after_cancel + ) + + await assert_eq_eventually(True, waiting_after_cancel) + await handle.signal(CancelAtWaitConditionWorkflow.finish_after_cancel) + assert await handle.result() == "cancelled" + + assert any( + _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG in completion.successful.used_internal_flags + for _, completion in runner._pairs + ) + assert any( + {"signal_workflow", "cancel_workflow"}.issubset( + {job.WhichOneof("variant") for job in activation.jobs} + ) + for activation, _ in runner._pairs + ) + await Replayer(workflows=[CancelAtWaitConditionWorkflow]).replay_workflow( + await handle.fetch_history() + ) + + +@workflow.defn +class CompleteChildOnSignalWorkflow: + def __init__(self) -> None: + self._finish = False + + @workflow.run + async def run(self) -> str: + await workflow.wait_condition(lambda: self._finish) + return "child complete" + + @workflow.signal + def finish(self) -> None: + self._finish = True + + +@workflow.defn +class CancelAtChildCompletionWorkflow: + def __init__(self) -> None: + self._child_started = False + + @workflow.run + async def run(self, child_task_queue: str) -> str: + child = await workflow.start_child_workflow( + CompleteChildOnSignalWorkflow.run, + id=f"{workflow.info().workflow_id}-child", + task_queue=child_task_queue, + ) + self._child_started = True + return await child + + @workflow.query + def child_started(self) -> bool: + return self._child_started + + +async def test_workflow_cancel_and_child_completion_in_same_activation( + client: Client, +): + parent_task_queue = str(uuid.uuid4()) + child_task_queue = str(uuid.uuid4()) + workflow_id = f"workflow-{uuid.uuid4()}" + child_id = f"{workflow_id}-child" + runner = CustomWorkflowRunner() + + child_worker = new_worker( + client, + CompleteChildOnSignalWorkflow, + task_queue=child_task_queue, + ) + child_worker._set_default_workflow_logic_flag( + _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG, enabled=True + ) + async with child_worker: + handle = await client.start_workflow( + CancelAtChildCompletionWorkflow.run, + child_task_queue, + id=workflow_id, + task_queue=parent_task_queue, + ) + parent_worker = new_worker( + client, + CancelAtChildCompletionWorkflow, + task_queue=parent_task_queue, + workflow_runner=runner, + # The Java test server does not reliably reschedule an abandoned + # sticky task after its timeout, so keep events sent after this + # worker stops on the normal queue. + max_cached_workflows=0, + ) + parent_worker._set_default_workflow_logic_flag( + _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG, enabled=True + ) + async with parent_worker: + + async def child_started() -> bool: + return await handle.query(CancelAtChildCompletionWorkflow.child_started) + + await assert_eq_eventually(True, child_started) + + child_handle = client.get_workflow_handle(child_id) + await child_handle.signal(CompleteChildOnSignalWorkflow.finish) + assert await child_handle.result() == "child complete" + + async def child_completion_recorded() -> None: + async for event in handle.fetch_history_events(): + if ( + event.event_type + == EventType.EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_COMPLETED + ): + return + raise AssertionError("Child completion is not in parent history") + + await assert_eventually(child_completion_recorded) + await handle.cancel() + + parent_worker = new_worker( + client, + CancelAtChildCompletionWorkflow, + task_queue=parent_task_queue, + workflow_runner=runner, + ) + parent_worker._set_default_workflow_logic_flag( + _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG, enabled=True + ) + async with parent_worker: + with pytest.raises(WorkflowFailureError) as err: + await handle.result() + assert isinstance(err.value.cause, CancelledError) + + assert any( + {"resolve_child_workflow_execution", "cancel_workflow"}.issubset( + {job.WhichOneof("variant") for job in activation.jobs} + ) + for activation, _ in runner._pairs + ) + await Replayer(workflows=[CancelAtChildCompletionWorkflow]).replay_workflow( + await handle.fetch_history() + ) + + @activity.defn async def wait_forever() -> NoReturn: await asyncio.Future() @@ -2016,6 +2353,7 @@ def do_search_attribute_update_typed(self) -> None: ) +@pytest.mark.requires_local_server async def test_workflow_search_attributes(client: Client, env_type: str): if env_type != "local": pytest.skip("Only testing search attributes on local which disables cache") @@ -2201,6 +2539,7 @@ async def run(self) -> None: # All we need to do is complete +@pytest.mark.requires_local_server async def test_workflow_no_initial_search_attributes(client: Client, env_type: str): if env_type != "local": pytest.skip("Only testing search attributes on local which disables cache") @@ -2242,8 +2581,27 @@ def last_signal(self) -> str: return self._last_signal -async def test_workflow_logging(client: Client): - workflow.logger.full_workflow_info_on_extra = True +@pytest.mark.parametrize( + "with_workflow_info", + [True, False], +) +async def test_workflow_logging(client: Client, with_workflow_info: bool): + orig_on_message = workflow.logger.workflow_info_on_message + orig_on_extra = workflow.logger.workflow_info_on_extra + orig_full_on_extra = workflow.logger.full_workflow_info_on_extra + + try: + workflow.logger.workflow_info_on_message = with_workflow_info + workflow.logger.workflow_info_on_extra = with_workflow_info + workflow.logger.full_workflow_info_on_extra = with_workflow_info + await _do_workflow_logging_test(client, with_workflow_info) + finally: + workflow.logger.workflow_info_on_message = orig_on_message + workflow.logger.workflow_info_on_extra = orig_on_extra + workflow.logger.full_workflow_info_on_extra = orig_full_on_extra + + +async def _do_workflow_logging_test(client: Client, with_workflow_info: bool): with LogCapturer().logs_captured( workflow.logger.base_logger, activity.logger.base_logger ) as capturer: @@ -2270,31 +2628,43 @@ async def test_workflow_logging(client: Client): assert "signal 2" == await handle.query(LoggingWorkflow.last_signal) # Confirm logs were produced - assert capturer.find_log("Signal: signal 1 ({'attempt':") + assert capturer.find_log("Signal: signal 1") assert capturer.find_log("Signal: signal 2") assert capturer.find_log("Update: update 1") assert capturer.find_log("Update: update 2") assert capturer.find_log("Query called") assert not capturer.find_log("Signal: signal 3") - # Also make sure it has some workflow info and correct funcName - record = capturer.find_log("Signal: signal 1") - assert ( - record - and record.__dict__["temporal_workflow"]["workflow_type"] - == "LoggingWorkflow" - and record.funcName == "my_signal" - ) - # Since we enabled full info, make sure it's there - assert isinstance(record.__dict__["workflow_info"], workflow.Info) - # Check the log emitted by the update execution. - record = capturer.find_log("Update: update 1") - assert ( - record - and record.__dict__["temporal_workflow"]["update_id"] == "update-1" - and record.__dict__["temporal_workflow"]["update_name"] == "my_update" - and "'update_id': 'update-1'" in record.message - and "'update_name': 'my_update'" in record.message - ) + + if with_workflow_info: + record = capturer.find_log("Signal: signal 1 ({'attempt':") + assert ( + record + and record.__dict__["temporal_workflow"]["workflow_type"] + == "LoggingWorkflow" + and record.funcName == "my_signal" + ) + # Since we enabled full info, make sure it's there + assert isinstance(record.__dict__["workflow_info"], workflow.Info) + + # Check the log emitted by the update execution. + record = capturer.find_log("Update: update 1") + assert ( + record + and record.__dict__["temporal_workflow"]["update_id"] == "update-1" + and record.__dict__["temporal_workflow"]["update_name"] == "my_update" + and "'update_id': 'update-1'" in record.message + and "'update_name': 'my_update'" in record.message + ) + else: + record = capturer.find_log("Signal: signal 1") + assert record and "temporal_workflow" not in record.__dict__ + assert record and "workflow_info" not in record.__dict__ + + record = capturer.find_log("Update: update 1") + assert record and "temporal_workflow" not in record.__dict__ + assert record and "workflow_info" not in record.__dict__ + assert "'update_id': 'update-1'" not in record.message + assert "'update_name': 'my_update'" not in record.message # Clear queue and start a new one with more signals capturer.log_queue.queue.clear() @@ -3192,6 +3562,329 @@ async def waiting_signal() -> bool: ] == await post_patch_handle.result() +@workflow.defn +class PatchActivationWorkflow: + @workflow.run + async def run(self, patch_id: str, sleep_after_first: bool = False) -> list[bool]: + first = workflow.patched(patch_id) + if sleep_after_first: + await workflow.sleep(0.001) + return [first, workflow.patched(patch_id)] + + +@workflow.defn +class PatchActivationDeprecateWorkflow: + @workflow.run + async def run(self, patch_id: str) -> bool: + workflow.deprecate_patch(patch_id) + return workflow.patched(patch_id) + + +@workflow.defn(name="PatchActivationRolloutWorkflow") +class PatchActivationRolloutWorkflow: + def __init__(self) -> None: + self._ready = False + self._released = False + + @workflow.run + async def run(self) -> str: + workflow.patched("rollout-patch") + self._ready = True + await workflow.wait_condition(lambda: self._released) + return "new" if workflow.patched("rollout-patch") else "old" + + @workflow.query + def ready(self) -> bool: + return self._ready + + @workflow.signal + def release(self) -> None: + self._released = True + + +@workflow.defn(name="PatchActivationRolloutWorkflow") +class PatchActivationOldRolloutWorkflow: + def __init__(self) -> None: + self._ready = False + self._released = False + + @workflow.run + async def run(self) -> str: + self._ready = True + await workflow.wait_condition(lambda: self._released) + return "old" + + @workflow.query + def ready(self) -> bool: + return self._ready + + @workflow.signal + def release(self) -> None: + self._released = True + + +async def patch_marker_count(handle: WorkflowHandle) -> int: + count = 0 + async for event in handle.fetch_history_events(): + if event.event_type is EventType.EVENT_TYPE_MARKER_RECORDED: + count += 1 + return count + + +async def has_completed_workflow_task(handle: WorkflowHandle) -> bool: + async for event in handle.fetch_history_events(): + if event.event_type is EventType.EVENT_TYPE_WORKFLOW_TASK_COMPLETED: + return True + return False + + +def recording_patch_activation_callback( + calls: list[temporalio.worker.PatchActivationInput], decision: bool +) -> typing.Callable[[temporalio.worker.PatchActivationInput], bool]: + def callback(input: temporalio.worker.PatchActivationInput) -> bool: + calls.append(input) + return decision + + return callback + + +async def test_workflow_patch_activation_callback(client: Client): + workflow_id = f"workflow-{uuid.uuid4()}" + calls: list[temporalio.worker.PatchActivationInput] = [] + async with new_worker( + client, + PatchActivationWorkflow, + patch_activation_callback=recording_patch_activation_callback(calls, True), + ) as worker: + result = await client.execute_workflow( + PatchActivationWorkflow.run, + args=["my-patch", False], + id=workflow_id, + task_queue=worker.task_queue, + ) + + assert result == [True, True] + assert len(calls) == 1 + assert calls[0].workflow_info.workflow_id == workflow_id + assert calls[0].patch_id == "my-patch" + + +async def test_workflow_patch_activation_callback_can_decline(client: Client): + calls: list[temporalio.worker.PatchActivationInput] = [] + async with new_worker( + client, + PatchActivationWorkflow, + patch_activation_callback=recording_patch_activation_callback(calls, False), + ) as worker: + handle = await client.start_workflow( + PatchActivationWorkflow.run, + args=["my-patch", False], + id=f"workflow-{uuid.uuid4()}", + task_queue=worker.task_queue, + ) + assert await handle.result() == [False, False] + + assert len(calls) == 1 + assert await patch_marker_count(handle) == 0 + + +async def test_workflow_patch_activation_default_activates(client: Client): + async with new_worker(client, PatchActivationWorkflow) as worker: + handle = await client.start_workflow( + PatchActivationWorkflow.run, + args=["my-patch", False], + id=f"workflow-{uuid.uuid4()}", + task_queue=worker.task_queue, + ) + assert await handle.result() == [True, True] + + assert await patch_marker_count(handle) == 1 + + +async def test_workflow_patch_activation_callback_not_recalled_on_replay( + client: Client, +): + calls: list[temporalio.worker.PatchActivationInput] = [] + async with new_worker( + client, + PatchActivationWorkflow, + max_cached_workflows=0, + patch_activation_callback=recording_patch_activation_callback(calls, False), + ) as worker: + result = await client.execute_workflow( + PatchActivationWorkflow.run, + args=["my-patch", True], + id=f"workflow-{uuid.uuid4()}", + task_queue=worker.task_queue, + ) + assert result == [False, False] + + assert len(calls) == 1 + + +async def test_workflow_patch_activation_callback_bypassed_for_deprecate( + client: Client, +): + def unexpected_callback(_: temporalio.worker.PatchActivationInput) -> bool: + raise AssertionError("Patch activation callback should not be called") + + async with new_worker( + client, + PatchActivationDeprecateWorkflow, + patch_activation_callback=unexpected_callback, + ) as worker: + result = await client.execute_workflow( + PatchActivationDeprecateWorkflow.run, + "my-patch", + id=f"workflow-{uuid.uuid4()}", + task_queue=worker.task_queue, + ) + + assert result is True + + +async def test_workflow_patch_activation_callback_must_return_bool(client: Client): + def invalid_callback(_: temporalio.worker.PatchActivationInput) -> bool: + return "not a bool" # type: ignore[return-value] # pyright: ignore[reportReturnType] + + async with new_worker( + client, + PatchActivationWorkflow, + patch_activation_callback=invalid_callback, + ) as worker: + handle = await client.start_workflow( + PatchActivationWorkflow.run, + args=["my-patch", False], + id=f"workflow-{uuid.uuid4()}", + task_queue=worker.task_queue, + ) + await assert_task_fail_eventually( + handle, + message_contains="Patch activation callback must return true or false", + ) + + +async def test_workflow_patch_activation_callback_is_read_only(client: Client): + def make_command(_: temporalio.worker.PatchActivationInput) -> bool: + workflow.upsert_memo({"foo": "bar"}) + return True + + def schedule_task(_: temporalio.worker.PatchActivationInput) -> bool: + asyncio.get_running_loop().call_soon(lambda: None) + return True + + def wait_condition(_: temporalio.worker.PatchActivationInput) -> bool: + coroutine = workflow.wait_condition(lambda: True) + try: + coroutine.send(None) + finally: + coroutine.close() + return True + + def use_random(_: temporalio.worker.PatchActivationInput) -> bool: + workflow.random().random() + return True + + def register_random_seed_callback( + _: temporalio.worker.PatchActivationInput, + ) -> bool: + workflow.register_random_seed_callback(lambda _seed: None) + return True + + def create_new_random(_: temporalio.worker.PatchActivationInput) -> bool: + workflow.new_random() + return True + + callbacks = [ + (make_command, "action attempted: add command"), + (schedule_task, "action attempted: schedule task"), + (wait_condition, "action attempted: wait condition"), + (use_random, "action attempted: random"), + ( + register_random_seed_callback, + "action attempted: register random seed callback", + ), + (create_new_random, "action attempted: register random seed callback"), + ] + for callback, message in callbacks: + async with new_worker( + client, + PatchActivationWorkflow, + patch_activation_callback=callback, + ) as worker: + handle = await client.start_workflow( + PatchActivationWorkflow.run, + args=["my-patch", False], + id=f"workflow-{uuid.uuid4()}", + task_queue=worker.task_queue, + ) + await assert_task_fail_eventually(handle, message_contains=message) + + +async def test_workflow_declined_patch_rolls_out_to_old_worker(client: Client): + task_queue = f"tq-{uuid.uuid4()}" + calls: list[temporalio.worker.PatchActivationInput] = [] + async with new_worker( + client, + PatchActivationRolloutWorkflow, + task_queue=task_queue, + max_cached_workflows=0, + patch_activation_callback=recording_patch_activation_callback(calls, False), + ): + handle = await client.start_workflow( + PatchActivationRolloutWorkflow.run, + id=f"workflow-{uuid.uuid4()}", + task_queue=task_queue, + ) + await assert_eq_eventually(True, lambda: has_completed_workflow_task(handle)) + + assert len(calls) == 1 + async with new_worker( + client, + PatchActivationOldRolloutWorkflow, + task_queue=task_queue, + max_cached_workflows=0, + ): + await handle.signal("release") + assert await handle.result() == "old" + + +async def test_workflow_activated_patch_ignores_declining_worker(client: Client): + task_queue = f"tq-{uuid.uuid4()}" + activated_calls: list[temporalio.worker.PatchActivationInput] = [] + declining_calls: list[temporalio.worker.PatchActivationInput] = [] + async with new_worker( + client, + PatchActivationRolloutWorkflow, + task_queue=task_queue, + max_cached_workflows=0, + patch_activation_callback=recording_patch_activation_callback( + activated_calls, True + ), + ): + handle = await client.start_workflow( + PatchActivationRolloutWorkflow.run, + id=f"workflow-{uuid.uuid4()}", + task_queue=task_queue, + ) + await assert_eq_eventually(True, lambda: has_completed_workflow_task(handle)) + + assert len(activated_calls) == 1 + async with new_worker( + client, + PatchActivationRolloutWorkflow, + task_queue=task_queue, + max_cached_workflows=0, + patch_activation_callback=recording_patch_activation_callback( + declining_calls, False + ), + ): + await handle.signal(PatchActivationRolloutWorkflow.release) + assert await handle.result() == "new" + + assert not declining_calls + + @workflow.defn class UUIDWorkflow: def __init__(self) -> None: @@ -3241,6 +3934,73 @@ async def test_workflow_uuid(client: Client): assert handle2_query_result == await handle2.query(UUIDWorkflow.result) +@workflow.defn +class UUID7Workflow: + def __init__(self) -> None: + self._result = "" + self._time_ms = -1 + + @workflow.run + async def run(self) -> None: + self._time_ms = workflow.time_ns() // 1_000_000 + self._result = str(workflow.uuid7()) + + @workflow.query + def result(self) -> str: + return self._result + + @workflow.query + def time_ms(self) -> int: + return self._time_ms + + +async def test_workflow_uuid7(client: Client): + task_queue = str(uuid.uuid4()) + async with new_worker( + client, UUID7Workflow, task_queue=task_queue, max_cached_workflows=0 + ): + # Get two handle UUID results. Need to disable workflow cache since we + # restart the worker and don't want to pay the sticky queue penalty. + handle1 = await client.start_workflow( + UUID7Workflow.run, id=f"workflow-{uuid.uuid4()}", task_queue=task_queue + ) + await handle1.result() + handle1_query_result = await handle1.query(UUID7Workflow.result) + + handle2 = await client.start_workflow( + UUID7Workflow.run, + id=f"workflow-{uuid.uuid4()}", + task_queue=task_queue, + ) + await handle2.result() + handle2_query_result = await handle2.query(UUID7Workflow.result) + + # Confirm they aren't equal to each other but they are equal to retries + # of the same query + assert handle1_query_result != handle2_query_result + assert handle1_query_result == await handle1.query(UUID7Workflow.result) + assert handle2_query_result == await handle2.query(UUID7Workflow.result) + + # Confirm RFC 9562 shape: version 7, RFC variant, and the leading 48 + # bits are the workflow time in milliseconds at generation + for handle, query_result in ( + (handle1, handle1_query_result), + (handle2, handle2_query_result), + ): + result_uuid = uuid.UUID(query_result) + assert result_uuid.version == 7 + assert result_uuid.variant == uuid.RFC_4122 + workflow_time_ms = await handle.query(UUID7Workflow.time_ms) + assert int(result_uuid) >> 80 == workflow_time_ms + + # Now confirm those results are the same even on a new worker + async with new_worker( + client, UUID7Workflow, task_queue=task_queue, max_cached_workflows=0 + ): + assert handle1_query_result == await handle1.query(UUID7Workflow.result) + assert handle2_query_result == await handle2.query(UUID7Workflow.result) + + @activity.defn(name="custom-name") class CallableClassActivity: def __init__(self, orig_field1: str) -> None: @@ -3591,8 +4351,16 @@ def check_condition(self) -> bool: return True -async def test_workflow_query_does_not_run_condition(client: Client): - async with new_worker(client, QueryAffectConditionWorkflow) as worker: +@pytest.mark.parametrize("single_batch", [False, True]) +async def test_workflow_query_does_not_run_condition( + client: Client, single_batch: bool +): + worker = new_worker(client, QueryAffectConditionWorkflow) + if single_batch: + worker._set_default_workflow_logic_flag( + _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG, enabled=True + ) + async with worker: handle = await client.start_workflow( QueryAffectConditionWorkflow.run, id=f"workflow-{uuid.uuid4()}", @@ -4175,6 +4943,8 @@ async def bad_query(self, bad_thing: str) -> str: workflow.set_query_handler("some-handler", lambda: "whatever") elif bad_thing == "patch": workflow.patched("some-patch") + elif bad_thing == "register_random_seed_callback": + workflow.register_random_seed_callback(lambda _seed: None) elif bad_thing == "signal_external_handle": await workflow.get_external_workflow_handle("some-id").signal("some-signal") return "should never get here" @@ -4203,6 +4973,7 @@ async def assert_bad_query(bad_thing: str) -> None: await assert_bad_query("random") await assert_bad_query("set_query_handler") await assert_bad_query("patch") + await assert_bad_query("register_random_seed_callback") await assert_bad_query("signal_external_handle") @@ -4256,7 +5027,7 @@ async def run(self) -> None: ) -async def test_workflow_custom_metrics(client: Client): +async def test_workflow_custom_metrics(client: Client, env: WorkflowEnvironment): # Run worker with default runtime which is noop meter just to confirm it # doesn't fail async with new_worker( @@ -4282,9 +5053,7 @@ async def test_workflow_custom_metrics(client: Client): assert str(err.value).startswith("Invalid value type for key") # New client with the runtime - client = await Client.connect( - client.service_client.config.target_host, - namespace=client.namespace, + client = await env.connect_client( runtime=runtime, ) @@ -4361,7 +5130,7 @@ async def test_workflow_custom_metrics(client: Client): ) -async def test_workflow_buffered_metrics(client: Client): +async def test_workflow_buffered_metrics(client: Client, env: WorkflowEnvironment): # Create runtime with metric buffer buffer = MetricBuffer(10000) runtime = Runtime( @@ -4422,9 +5191,7 @@ async def test_workflow_buffered_metrics(client: Client): assert runtime_updates2[1].value == 400 # Create a new client on the runtime and execute the custom metric workflow - client = await Client.connect( - client.service_client.config.target_host, - namespace=client.namespace, + client = await env.connect_client( runtime=runtime, ) async with new_worker( @@ -4485,12 +5252,10 @@ async def test_workflow_buffered_metrics(client: Client): ) -async def test_workflow_metrics_other_types(client: Client): +async def test_workflow_metrics_other_types(env: WorkflowEnvironment): async def do_stuff(buffer: MetricBuffer) -> None: runtime = Runtime(telemetry=TelemetryConfig(metrics=buffer)) - new_client = await Client.connect( - client.service_client.config.target_host, - namespace=client.namespace, + new_client = await env.connect_client( runtime=runtime, ) async with new_worker(new_client, HelloWorkflow) as worker: @@ -5510,6 +6275,7 @@ async def run(self) -> None: await asyncio.sleep(0.1) +@pytest.mark.requires_local_server async def test_workflow_replace_worker_client(client: Client, env: WorkflowEnvironment): if env.supports_time_skipping: pytest.skip("Only testing against two real servers") @@ -5565,11 +6331,11 @@ async def any_task_completed(handle: WorkflowHandle) -> bool: await handle2.terminate() -async def test_workflow_replace_worker_client_diff_runtimes_fail(client: Client): +async def test_workflow_replace_worker_client_diff_runtimes_fail( + client: Client, env: WorkflowEnvironment +): other_runtime = Runtime(telemetry=TelemetryConfig()) - other_client = await Client.connect( - client.service_client.config.target_host, - namespace=client.namespace, + other_client = await env.connect_client( runtime=other_runtime, ) async with new_worker(client, HelloWorkflow) as worker: @@ -5732,12 +6498,6 @@ async def test_workflow_current_update(client: Client): ) -def skip_unfinished_handler_tests_in_older_python(): - # These tests reliably fail or timeout in 3.9 - if sys.version_info < (3, 10): - pytest.skip("Skipping unfinished handler tests in Python < 3.10") - - @workflow.defn class UnfinishedHandlersWarningsWorkflow: def __init__(self): @@ -5795,7 +6555,6 @@ async def test_unfinished_update_handler(client: Client): async def test_unfinished_signal_handler(client: Client): - skip_unfinished_handler_tests_in_older_python() async with new_worker(client, UnfinishedHandlersWarningsWorkflow) as worker: test = _UnfinishedHandlersWarningsTest(client, worker, "signal") await test.test_wait_all_handlers_finished_and_unfinished_handlers_warning() @@ -6010,6 +6769,9 @@ async def my_dynamic_signal(self, _name: str, _args: Sequence[RawValue]) -> None await workflow.wait_condition(lambda: self.handlers_may_finish) +# Cloud cancels in-flight dynamic handlers before SDK teardown can emit the +# unfinished-handler warning this test asserts. +@pytest.mark.requires_local_server @pytest.mark.parametrize("handler_type", ["-signal-", "-update-"]) @pytest.mark.parametrize( "handler_registration", ["-late-registered-", "-not-late-registered-"] @@ -6024,7 +6786,6 @@ async def my_dynamic_signal(self, _name: str, _args: Sequence[RawValue]) -> None ) async def test_unfinished_handler_on_workflow_termination( client: Client, - env: WorkflowEnvironment, handler_type: Literal["-signal-", "-update-"], handler_registration: Literal["-late-registered-", "-not-late-registered-"], handler_dynamism: Literal["-dynamic-", "-not-dynamic-"], @@ -6035,11 +6796,6 @@ async def test_unfinished_handler_on_workflow_termination( "-cancellation-", "-failure-", "-continue-as-new-" ], ): - if env.supports_time_skipping: - pytest.skip( - "Issues with update: https://github.com/temporalio/sdk-python/issues/826" - ) - skip_unfinished_handler_tests_in_older_python() await _UnfinishedHandlersOnWorkflowTerminationTest( client, handler_type, @@ -6131,13 +6887,24 @@ async def _run_workflow_and_get_warning(self) -> bool: if self.handler_waiting == "-wait-all-handlers-finish-": await update_task else: - with pytest.raises(WorkflowUpdateFailedError) as err_info: + with pytest.raises( + (WorkflowUpdateFailedError, RPCError) + ) as err_info: await update_task update_err = err_info.value - assert isinstance(update_err.cause, ApplicationError) - assert ( - update_err.cause.type == "AcceptedUpdateCompletedWorkflow" - ) + if isinstance(update_err, WorkflowUpdateFailedError): + assert isinstance(update_err.cause, ApplicationError) + assert ( + update_err.cause.type + == "AcceptedUpdateCompletedWorkflow" + ) + else: + assert isinstance(update_err, RPCError) + assert update_err.status == RPCStatusCode.NOT_FOUND + assert ( + str(update_err) + == "workflow execution already completed" + ) with pytest.raises(WorkflowFailureError) as err: await handle.result() @@ -6977,8 +7744,8 @@ async def run_act(self): async def test_async_loop_ordering(client: Client, env: WorkflowEnvironment): - """This test mostly exists to generate histories for test_replayer_async_ordering. - See that test for more.""" + """This test mostly exists to generate PROCESS_WORKFLOW_ACTIVATION_JOBS_AS_SINGLE_BATCH + histories for test_replayer_async_ordering. See that test for more.""" if env.supports_time_skipping: pytest.skip("This test doesn't work right with time skipping for some reason") @@ -6990,12 +7757,16 @@ async def test_async_loop_ordering(client: Client, env: WorkflowEnvironment): ) await handle.signal(SignalsActivitiesTimersUpdatesTracingWorkflow.dosig, "before") - async with new_worker( + worker = new_worker( client, SignalsActivitiesTimersUpdatesTracingWorkflow, activities=[say_hello], task_queue=task_queue, - ): + ) + worker._set_default_workflow_logic_flag( + _SINGLE_BATCH_WORKFLOW_LOGIC_FLAG, enabled=True + ) + async with worker: await asyncio.sleep(0.2) await handle.signal(SignalsActivitiesTimersUpdatesTracingWorkflow.dosig, "1") await handle.execute_update( @@ -8699,6 +9470,7 @@ async def run(self, name: str) -> str: return f"Hello from child, {name}" +@pytest.mark.requires_local_server async def test_search_attribute_codec(client: Client, env_type: str): if env_type != "local": pytest.skip("Only testing search attributes on local which disables cache") @@ -9162,3 +9934,65 @@ async def test_workflow_uncancel_shield_signal_external(client: Client): assert shielded_err is None, ( f"Unexpected 'exception in shielded future' log: {shielded_err}" ) + + +class _SlowActivity: + def __init__(self) -> None: + self.started = asyncio.Event() + + @activity.defn(name="slow_activity") + async def slow_activity(self) -> None: + self.started.set() + await asyncio.sleep(60) + + +@workflow.defn +class _CancelInFlightActivityWorkflow: + @workflow.run + async def run(self) -> None: + await asyncio.gather( + *( + workflow.execute_activity( + "slow_activity", + start_to_close_timeout=timedelta(minutes=2), + ) + for _ in range(4) + ) + ) + + +@pytest.mark.asyncio +async def test_workflow_cancel_no_shielded_future_log( + client: Client, caplog: pytest.LogCaptureFixture +): + activity_inst = _SlowActivity() + + with caplog.at_level(logging.ERROR): + async with new_worker( + client, + _CancelInFlightActivityWorkflow, + activities=[activity_inst.slow_activity], + ) as worker: + handle = await client.start_workflow( + _CancelInFlightActivityWorkflow.run, + id=f"workflow-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(minutes=5), + ) + + # Wait for activities to start + await asyncio.wait_for(activity_inst.started.wait(), timeout=10) + + # Ignore worker startup logs + caplog.clear() + + await handle.cancel() + + try: + await handle.result() + except WorkflowFailureError as err: + assert isinstance(err.cause, CancelledError) + + assert not any( + "exception in shielded future" in record.message for record in caplog.records + ) diff --git a/tests/worker/workflow_sandbox/test_restrictions.py b/tests/worker/workflow_sandbox/test_restrictions.py index bd1aaf749..ec001ba19 100644 --- a/tests/worker/workflow_sandbox/test_restrictions.py +++ b/tests/worker/workflow_sandbox/test_restrictions.py @@ -75,6 +75,21 @@ def test_restricted_proxy_dunder_methods(): assert f"{restricted_path_obj}" == expected_path +def test_restricted_proxy_in_place_operations_preserve_proxy(): + restricted_list = _RestrictedProxy( + "list", + list, + RestrictionContext(), + SandboxMatcher(), + ) + values = restricted_list([1]) + + values += [2] + + assert type(values) is _RestrictedProxy + assert RestrictionContext.unwrap_if_proxied(values) == [1, 2] + + def test_workflow_sandbox_restricted_proxy(): obj_class = _RestrictedProxy( "RestrictableObject", diff --git a/tests/worker/workflow_sandbox/test_runner.py b/tests/worker/workflow_sandbox/test_runner.py index 288da0861..024ab7273 100644 --- a/tests/worker/workflow_sandbox/test_runner.py +++ b/tests/worker/workflow_sandbox/test_runner.py @@ -197,6 +197,10 @@ async def test_workflow_sandbox_restrictions(client: Client): if sys.version_info < (3, 14): invalid_code_to_check.append("import os.path\nos.path.abspath('foo')") # type: ignore[reportUnreachable] + # uuid7 was only added to the stdlib in 3.14 + if sys.version_info >= (3, 14): + invalid_code_to_check.append("import uuid\nuuid.uuid7()") # type: ignore[reportUnreachable] + for code in invalid_code_to_check: with pytest.raises(WorkflowFailureError) as err: await client.execute_workflow( diff --git a/uv.lock b/uv.lock index 5df24adfa..f64424dca 100644 --- a/uv.lock +++ b/uv.lock @@ -9,12 +9,9 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-06-16T15:22:43.641437Z" +exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. exclude-newer-span = "P2W" -[options.exclude-newer-package] -openai-agents = false - [[package]] name = "aioboto3" version = "15.5.0" @@ -62,11 +59,11 @@ wheels = [ [[package]] name = "aiohappyeyeballs" -version = "2.6.2" +version = "2.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591, upload-time = "2026-05-20T15:12:24.631Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/f4/eec0465c2f67b2664688d0240b3212d5196fd89e741df67ddb81f8d35658/aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d", size = 24757, upload-time = "2026-07-01T17:11:55.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062, upload-time = "2026-05-20T15:12:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472", size = 15038, upload-time = "2026-07-01T17:11:54.055Z" }, ] [[package]] @@ -237,21 +234,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/00/b7/e3bf5133d697a08128598c8d0abc5e16377b51465a33756de24fa7dee953/aiosqlite-0.22.1-py3-none-any.whl", hash = "sha256:21c002eb13823fad740196c5a2e9d8e62f6243bd9e7e4a1f87fb5e44ecb4fceb", size = 17405, upload-time = "2025-12-23T19:25:42.139Z" }, ] -[[package]] -name = "alembic" -version = "1.18.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mako" }, - { name = "sqlalchemy" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/94/13/8b084e0f2efb0275a1d534838844926f798bd766566b1375174e2448cd31/alembic-1.18.4.tar.gz", hash = "sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc", size = 2056725, upload-time = "2026-02-10T16:00:47.195Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/29/6533c317b74f707ea28f8d633734dbda2119bbadfc61b2f3640ba835d0f7/alembic-1.18.4-py3-none-any.whl", hash = "sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a", size = 263893, upload-time = "2026-02-10T16:00:49.997Z" }, -] - [[package]] name = "annotated-doc" version = "0.0.4" @@ -270,6 +252,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] +[[package]] +name = "anthropic" +version = "0.117.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "docstring-parser" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/0d/8f71d535edb0d438f023bd825fb65f67c14fa88a2bd6b75f292a58a63de4/anthropic-0.117.0.tar.gz", hash = "sha256:98107f2b76439641e0ae2a1754087534b8f178dbab99d6eb1bc4b7bc8c744496", size = 989933, upload-time = "2026-07-16T19:36:13.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/4c/917d21d6619a4475cdafc6d13a69fdb3b901ddac57e76caca5a25c117b6d/anthropic-0.117.0-py3-none-any.whl", hash = "sha256:451a0a6905f11dff7663d13e4ee5dbf909eb8942b1d049803c7b937a13ac47ec", size = 998327, upload-time = "2026-07-16T19:36:11.225Z" }, +] + [[package]] name = "antlr4-python3-runtime" version = "4.13.2" @@ -281,16 +282,16 @@ wheels = [ [[package]] name = "anyio" -version = "4.13.0" +version = "4.14.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, + { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, ] [[package]] @@ -347,7 +348,7 @@ wheels = [ [[package]] name = "aws-sam-translator" -version = "1.110.0" +version = "1.111.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "boto3" }, @@ -355,9 +356,9 @@ dependencies = [ { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6e/2f/adeed2ce2bc62eca7ead7b3ae70fdd2cf84eecd582cd69a9529e6da89876/aws_sam_translator-1.110.0.tar.gz", hash = "sha256:466ee0e8200992c51b7fd5ede5e56ca2e8dd5473cc551e8495c14f2f4d636127", size = 368671, upload-time = "2026-05-19T21:21:06.959Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/31/4e6d6f0b9d4ead8eaa1c13a14d86834e7691acf5726fb49de98f8e195028/aws_sam_translator-1.111.0.tar.gz", hash = "sha256:6884d94e28dc20384e5e0396e9386a456fe59303d706924deb2646329b4d97d3", size = 374368, upload-time = "2026-07-02T00:31:39.601Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/6f/286e3e49d3b6b181473fefa5d9fc02e10d98ccc417e0de74e396db951fd9/aws_sam_translator-1.110.0-py3-none-any.whl", hash = "sha256:69b09aacf2d305ac747037b7b913224cb8a9d653f47a0306509c1d20e420b670", size = 431671, upload-time = "2026-05-19T21:21:05.26Z" }, + { url = "https://files.pythonhosted.org/packages/c1/e3/505c9db9c4a4270ad12bac621f370cb02eabb224886c1b81960c658d9baa/aws_sam_translator-1.111.0-py3-none-any.whl", hash = "sha256:510d0ad8cd40b245a62004f2dc974ddbb6ff0d496bdec0bc7d9cd209b46d5fad", size = 440579, upload-time = "2026-07-02T00:31:38.077Z" }, ] [[package]] @@ -467,11 +468,11 @@ wheels = [ [[package]] name = "bracex" -version = "2.6" +version = "3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/63/9a/fec38644694abfaaeca2798b58e276a8e61de49e2e37494ace423395febc/bracex-2.6.tar.gz", hash = "sha256:98f1347cd77e22ee8d967a30ad4e310b233f7754dbf31ff3fceb76145ba47dc7", size = 26642, upload-time = "2025-06-22T19:12:31.254Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/f5/4473ad9b48cd0420a2d762a3750fa0e078e23e060b1af72662e5987e5530/bracex-3.0.tar.gz", hash = "sha256:b73f718d6bd98d8419e45df02426c86e9967c179949f779340d6c3a8c83b9111", size = 43162, upload-time = "2026-06-30T00:43:35.279Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/2a/9186535ce58db529927f6cf5990a849aa9e052eea3e2cfefe20b9e1802da/bracex-2.6-py3-none-any.whl", hash = "sha256:0b0049264e7340b3ec782b5cb99beb325f36c3782a32e36e876452fd49a09952", size = 11508, upload-time = "2025-06-22T19:12:29.781Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2e/68781b78e764e5ccc4af1e3d27e060069c73af90234853fa80000e7ee79d/bracex-3.0-py3-none-any.whl", hash = "sha256:3833e61c2f092d5aa0468fa2e6c6e990a306185abf763b6d122f0158e59c58a5", size = 11738, upload-time = "2026-06-30T00:43:34.196Z" }, ] [[package]] @@ -494,98 +495,126 @@ filecache = [ [[package]] name = "certifi" -version = "2026.5.20" +version = "2026.6.17" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, + { url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" }, ] [[package]] name = "cffi" -version = "2.0.0" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, - { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, - { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, - { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, - { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, - { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, - { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, - { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, - { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, - { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, - { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, - { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, - { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, - { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, - { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, - { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, - { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, - { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, - { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, - { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, - { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/e9/6d7724983b3d5a0908dbf74f64038ade77c18646ff6636ec7894fd392ce1/cffi-2.1.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:b65f590ef2a44640f9a05dbb548a429b4ade77913ce683ac8b1480777658a6c0", size = 183837, upload-time = "2026-07-06T21:32:09.655Z" }, + { url = "https://files.pythonhosted.org/packages/69/aa/24580a278de21fd7322635556334d9b535f1cbc00b0a3919447cdf464c65/cffi-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164bff1657b2a74f0b6d54e11c9b375bc97b931f2ca9c43fcf875838da1570dd", size = 184226, upload-time = "2026-07-06T21:32:11.196Z" }, + { url = "https://files.pythonhosted.org/packages/88/a9/02cae418ec4beb282ace11958d9d4737793439d561fadc7e6d56f2e2b354/cffi-2.1.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:c941bb58d5a6e1c3892d86e42927ed6c180302f07e6d395d08c416e594b98b46", size = 211107, upload-time = "2026-07-06T21:32:12.328Z" }, + { url = "https://files.pythonhosted.org/packages/3b/30/c806937ed5e4c2c7ac30d9d6b76b5dc57ff8b75d83800d9bb11a8253cf2a/cffi-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a016194dbe13d14ee9556e734b772d8d67b947092b268d757fd4290e3ba2dfc2", size = 218733, upload-time = "2026-07-06T21:32:13.67Z" }, + { url = "https://files.pythonhosted.org/packages/f9/cf/398272b8bbfd58aa314fda5a7f1cdbb26d1d78ae324a11211521315dd1f0/cffi-2.1.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:03e9810d18c646077e501f661b682fbf5dee4676048527ca3cffe66faa9960dd", size = 205543, upload-time = "2026-07-06T21:32:15.148Z" }, + { url = "https://files.pythonhosted.org/packages/45/ca/f91641185cdd90c36d317a9dc7f85e88ef8682d8b300977baff5e23c35d8/cffi-2.1.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:19c54ac121cad98450b4896fa9a43ee0180d57bc4bc911a33db6cab1efab6cd3", size = 205460, upload-time = "2026-07-06T21:32:16.479Z" }, + { url = "https://files.pythonhosted.org/packages/38/66/04781a77b411f0bb5b234d62c1814754ab75ebe455ccff1b08e8d7aae98f/cffi-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d433a51f1870e43a13b6732f92aaf540ff77c2015097c78556f75a2d6c030e0", size = 218760, upload-time = "2026-07-06T21:32:17.98Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9a/bb1d5ed9c3fcae158e9f6391bf309c95d98c2ac37ed56573228471d0af5e/cffi-2.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3d7f118b5adbfdfead90c25822690b02bc8074fba949bb7858bec4ebd55adb43", size = 221230, upload-time = "2026-07-06T21:32:19.407Z" }, + { url = "https://files.pythonhosted.org/packages/41/aa/3c1409cdd26094efacd1c36c66e0a6eb9d4296e4fd4f9901b8b2042f4323/cffi-2.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5f5df567f6eb216de69be06ce55c8b714090fae02b18a3b40da8163b8c5fa9c", size = 213524, upload-time = "2026-07-06T21:32:20.828Z" }, + { url = "https://files.pythonhosted.org/packages/fa/75/74dfb7c3fc6ebbd408038476bd4c1d7e925c62614e7b9c534ecc34218288/cffi-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:11b3fb55f4f8ad92274ed26705f65d8f91457de71f5380061eb6d125a768fecd", size = 220341, upload-time = "2026-07-06T21:32:21.9Z" }, + { url = "https://files.pythonhosted.org/packages/70/b6/9003c33a3e7d2c1306f5962e646457dcfe5a8cd8fce6bbe02d7af25db783/cffi-2.1.0-cp310-cp310-win32.whl", hash = "sha256:9d72af0cf10a76a600a9690078fe31c63b9588c8e86bf9fd353f713c84b5db0f", size = 174578, upload-time = "2026-07-06T21:32:23.073Z" }, + { url = "https://files.pythonhosted.org/packages/8a/26/710688310447531c7a22f857c7f79d9855ec18b03e04494ced723fb37e2f/cffi-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb62edb5bb52cca65fab91a63afa7561607120d26090a7e8fda6fb9f064726da", size = 185071, upload-time = "2026-07-06T21:32:24.671Z" }, + { url = "https://files.pythonhosted.org/packages/d3/67/85c89a59ba36a671e79638f44d466749f08179266a57e4f2ffdf92174072/cffi-2.1.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:02cb7ff33ded4f1532476731f89ede53e2e488a8e6205515a82144246ffa7dcc", size = 183845, upload-time = "2026-07-06T21:32:26.32Z" }, + { url = "https://files.pythonhosted.org/packages/ea/dd/e3b0baa2d3d6a857ac72b7efbf18e32e487c9cdafcc13049ad765495b15e/cffi-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f5bce581e6b8c235e566a14768a943b172ada3ed73537bb0c0be1edee312d4e7", size = 184186, upload-time = "2026-07-06T21:32:28.025Z" }, + { url = "https://files.pythonhosted.org/packages/65/68/9f3ef890cf3c6ab97bd531c5677f67613d302165d16f8142b2811782a614/cffi-2.1.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:30b65779d598c370374fefabf138d456fd6f3216bfa7bedfab1ba82025b0cd93", size = 211892, upload-time = "2026-07-06T21:32:29.565Z" }, + { url = "https://files.pythonhosted.org/packages/22/d7/1a74539db16d8bfd839ff1515948948efbb162e574650fd3d846896eea95/cffi-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88023dfe18799507b73f1dbb0d14326a17465de1bc9c9c7655c22845e9ddc3a2", size = 218793, upload-time = "2026-07-06T21:32:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ec/d1/9a5b7169499e8e8d8e636de70b97ac7c9447104d2ff1a2cd94790cea5162/cffi-2.1.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0a96b74cda968eebbad56d973efe5098974f0a9fb323865bf99ea1fd24e3e64c", size = 205737, upload-time = "2026-07-06T21:32:32.216Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b0/e131a9c41f10607926278453d9596163594fe1c4ebc46efe3b5e5b34eb84/cffi-2.1.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a5781494d4d400a3f47f8f1da94b324f6e6b440a53387774002890a2a2f4b50f", size = 204909, upload-time = "2026-07-06T21:32:33.655Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d2/4398416cd699b35167947c6e22aca52c47e69ad5695073c9f1f2c52e04aa/cffi-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aa7a1b53a2a4452ada2d1b5dade9960b2522f1e61293a811a077439e39029565", size = 217883, upload-time = "2026-07-06T21:32:35.173Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a5/d4fe77b589e5e82d43ebc809bf2e6474afe8e48e32ea050b9357645b6471/cffi-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d8272c0e483b024e1b9ad029821470ed8ec65631dbd90217469da0e7cd89f1c", size = 221251, upload-time = "2026-07-06T21:32:36.527Z" }, + { url = "https://files.pythonhosted.org/packages/22/f0/a2fc43084c0433caf7f461bccc013e28f848d04ee1c5ed7fce71423cf4d9/cffi-2.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7762faa47e8ff7eb80bd261d9a7d8eea2d8baa69de5e95b70c1f338bbe712f02", size = 214250, upload-time = "2026-07-06T21:32:37.852Z" }, + { url = "https://files.pythonhosted.org/packages/04/8c/b925975448cf20634a9fbd5efceb807219db452653648d2897c0989cab2d/cffi-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:89095c1968b4ba8285840e131bf2891b09ae137fe2146905acae0354fbce1b5e", size = 219441, upload-time = "2026-07-06T21:32:39.146Z" }, + { url = "https://files.pythonhosted.org/packages/eb/da/5c4918a2d61d86fa927d716cb3d8e4626ef8dc8f605a599d32f33897f59a/cffi-2.1.0-cp311-cp311-win32.whl", hash = "sha256:64c753a0f87a256020004f37a1c8c02c480e725f910f0b2a0f3f07debd1b2479", size = 174496, upload-time = "2026-07-06T21:32:40.467Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c8/6c2de1d55cf35ef8b92885d5ef280790f0fb9634d87ea1cc315176aecd61/cffi-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:4f26194e3d95e06501b942642855aed4f953d55e95d7d01b7c4483db3ecff458", size = 185113, upload-time = "2026-07-06T21:32:41.761Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4e/e8d7cb5783f1841a3c8fb3a7735838d7484d08ec08c9f984b14cac1ac0e9/cffi-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:35aaea0c7ee0e58a5cd8c2fd1a48fdf7ece0d2699b7ecdda08194e9ce5dd9b3d", size = 179927, upload-time = "2026-07-06T21:32:42.961Z" }, + { url = "https://files.pythonhosted.org/packages/1e/85/990925db5df586ec90beb97529c853497e7f85ba0234830447faf41c3057/cffi-2.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:df2b82571a1b30f58a87bf4e5a9e78d2b1eff6c6ce8fd3aa3757221f93f0863f", size = 184829, upload-time = "2026-07-06T21:32:44.324Z" }, + { url = "https://files.pythonhosted.org/packages/4b/92/e7bb136ad6b5352603732cf907ef862ca103f20f2031c1735a46300c20c9/cffi-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78474632761faa0fb96f30b1c928c84ebcf68713cbb80d15bab09dfe61640fde", size = 184728, upload-time = "2026-07-06T21:32:45.683Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c0/d1ec30ffb370f748f2fb54425972bfef9871e0132e82fb589c46b6676049/cffi-2.1.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5972433ad71a9e46516584ef60a0fda12d9dc459938d1539c3ddecf9bdc1368d", size = 214815, upload-time = "2026-07-06T21:32:48.557Z" }, + { url = "https://files.pythonhosted.org/packages/1b/dc/5620cf930688be01f2d673804291de757a934c90b946dbdc3d84130c2ea4/cffi-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6422532152adf4e59b110cb2808cee7a033800952f5c036b4af047ee43199e7", size = 222429, upload-time = "2026-07-06T21:32:49.848Z" }, + { url = "https://files.pythonhosted.org/packages/4b/a4/77b53abbf7a1e0beb9637edbef2a94d15f9c822f591e85d439ffd91519a6/cffi-2.1.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:46b1c8db8f6122420f32d02fffb924c2fe9bc772d228c7c711748fff56aabb2b", size = 210315, upload-time = "2026-07-06T21:32:51.221Z" }, + { url = "https://files.pythonhosted.org/packages/58/0c/f528df19cc94b675087324d4760d9e6d5bfae97d6217aa4fac43de4f5fcc/cffi-2.1.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9fafc5aa2e2a39aaf7f8cc0c1f044a9b07fca12e558dca53a3cc5c654ad67a7", size = 208859, upload-time = "2026-07-06T21:32:52.512Z" }, + { url = "https://files.pythonhosted.org/packages/62/f2/c9522a81c32132799a1972c39f5c5f8b4c8b9f00488a23feaa6c06f07741/cffi-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e9f50d192a3e525b15a75ab5114e442d83d657b7ec29182a991bc9a88fd3a66", size = 221844, upload-time = "2026-07-06T21:32:53.704Z" }, + { url = "https://files.pythonhosted.org/packages/6e/28/bd53988b9833e8f8ad539d26f4c07a6b3f6bcb1e9e02e7ca038250b3428d/cffi-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98fff996e983a36d3aa2eca83af40c5821202e7e6f32d13ae94e3d2286f10cfe", size = 225287, upload-time = "2026-07-06T21:32:54.907Z" }, + { url = "https://files.pythonhosted.org/packages/79/99/0d0fd37f055224085f42bbb2c022d002e17dde4a97972822327b07d84101/cffi-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:379de10ce1ba048b1448599d1b37b24caee16309d1ac98d3982fc997f768700b", size = 223681, upload-time = "2026-07-06T21:32:56.329Z" }, + { url = "https://files.pythonhosted.org/packages/b0/80/c138990aa2a70b1a269f6e06348729836d733d6f970867943f61d367f8cc/cffi-2.1.0-cp312-cp312-win32.whl", hash = "sha256:9b8f0f26ca4e7513c534d351eca551947d053fac438f2a04ac96d882909b0d3a", size = 175269, upload-time = "2026-07-06T21:32:57.777Z" }, + { url = "https://files.pythonhosted.org/packages/a8/eb/f636456ff21a83fc13c032b58cc5dde061691546ac79efa284b2989b7982/cffi-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:c97f080ea627e2863524c5af3836e2270b5f5dfff1f104392b959f8df0c5d384", size = 185881, upload-time = "2026-07-06T21:32:59.253Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/400ea43e721727dca8a65c4521390e9196757caba4a45643acb2b63271b8/cffi-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:6d194185eabd279f1c05ebe3504265ddfc5ad2b58d0714f7db9f01da592e9eb6", size = 180088, upload-time = "2026-07-06T21:33:02.278Z" }, + { url = "https://files.pythonhosted.org/packages/96/88/a996879e2eeccb815f6e3a5967b12a308257412acec882039d386bd2aa7b/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:10537b1df4967ca26d21e5072d7d54188354483b91dc75058968d3f0cf13fbda", size = 194331, upload-time = "2026-07-06T21:33:03.697Z" }, + { url = "https://files.pythonhosted.org/packages/58/85/7ae00d5c8dd6266f4e944c3db630f3c5c9a98b61d469c714d848b1d8138a/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a95b05f9baf29b91171b3a8bd2020b028835243e7b0ff6bb23e2a3c228518b1b", size = 196966, upload-time = "2026-07-06T21:33:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e9/45c3a76ad8d43ad9261f4c95436da61128d3ca545d72b9612c0ab5be0b1c/cffi-2.1.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:15faec4adfff450819f3aee0e2e02c812de6edb88203aa58807955db2003472a", size = 184795, upload-time = "2026-07-06T21:33:06.699Z" }, + { url = "https://files.pythonhosted.org/packages/84/4c/82f132cb4418ee6d953d982b19191e87e2a6372c8a4ce36e50b69d6ade4a/cffi-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:716ff8ec22f20b4d988b12884086bcef0fc99737043e503f7a3935a6be99b1ea", size = 184746, upload-time = "2026-07-06T21:33:08.071Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1c/4ed5a0e5bdca6cbc275556de3328dd1b76fd0c11cc13c88fe66d1d8715f2/cffi-2.1.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:63960549e4f8dc41e31accb97b975abaecfc44c03e396c093a6436763c2ea7db", size = 214747, upload-time = "2026-07-06T21:33:09.671Z" }, + { url = "https://files.pythonhosted.org/packages/3a/a6/e879bb68cc23a2bc9ba8f4b7d8019f0c2694bad2ab6c4a3701d429439f58/cffi-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff067a8d8d880e7809e4ac88eb009bb848870115317b306666502ccad30b147f", size = 222392, upload-time = "2026-07-06T21:33:10.896Z" }, + { url = "https://files.pythonhosted.org/packages/88/f6/01890cfd63c08f8eb96a8319b0443690197d240a8bd6346048cf7bde9190/cffi-2.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3b926723c13eba9f81d2ef3820d63aeceec3b2d4639906047bf675cb8a7a500d", size = 210285, upload-time = "2026-07-06T21:33:12.251Z" }, + { url = "https://files.pythonhosted.org/packages/a6/cf/2b684132056f438567b61e19d690dd31cd0921ace051e0a458be6074369e/cffi-2.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:47ff3a8bfd8cb9da1af7524b965127095055654c177fcfc7578debcb015eecd0", size = 208801, upload-time = "2026-07-06T21:33:13.617Z" }, + { url = "https://files.pythonhosted.org/packages/6f/08/f2e7d62c460faae0926f2d6e423694aa409ced3bc1fe2927a0a6e5f05416/cffi-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:799416bae98336e400981ff6e532d67d5c709cfb30afb79865a1315f94b0e224", size = 221808, upload-time = "2026-07-06T21:33:15.466Z" }, + { url = "https://files.pythonhosted.org/packages/38/37/04f54b8e63a02f3d908332c9effbf8c366167c6f733ed8a3d4f79b7e2a1e/cffi-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:961be50688f7fba2fa65f63712d3b9b341a22311f5253460ce933f52f0de1c8c", size = 225241, upload-time = "2026-07-06T21:33:16.869Z" }, + { url = "https://files.pythonhosted.org/packages/a9/d6/c72eecca433cd3e681c65ed313ab4835d9d4a379704d0f628a6a05f51c2e/cffi-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf5c6cf48238b0eb4c086978c492ad1cbc22373fc5b2d7353b3a598ce6db887a", size = 223588, upload-time = "2026-07-06T21:33:18.239Z" }, + { url = "https://files.pythonhosted.org/packages/c6/4b/e706f67279140f92939da3475ad610df18bfd52d50f14953a8e5fede71d5/cffi-2.1.0-cp313-cp313-win32.whl", hash = "sha256:db3eb7d46527159a878ec3460e9d40615bc25ba337d477db681aea6e4f05c5d2", size = 175248, upload-time = "2026-07-06T21:33:19.799Z" }, + { url = "https://files.pythonhosted.org/packages/5a/47/59eb7975cb0e4ef0afa764ea945b29a5bb4537a9f771cb7d6c8a5dd74c95/cffi-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:8e74a6135550c4748af665b1b1118b6aab33b1fc6a16f9aff630af107c3b4512", size = 185717, upload-time = "2026-07-06T21:33:21.47Z" }, + { url = "https://files.pythonhosted.org/packages/5a/af/34fee85c48f8d94efc8597bc09470c9dd274c145f1c12e0fbc6ab6d38d74/cffi-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:2282cd5e38aa8accd03e99d1256af8411c84cdbee6a89d841b563fdbd1f3e50f", size = 180114, upload-time = "2026-07-06T21:33:22.515Z" }, + { url = "https://files.pythonhosted.org/packages/d8/f0/81478e482afa03f6d18dc8f2afb5edc45b3080853b634b5ed91961be0998/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d2117334c3af3bdcb9a88522b844a2bdb5efdc4f71c6c822df55486ae1c3347a", size = 194142, upload-time = "2026-07-06T21:33:23.657Z" }, + { url = "https://files.pythonhosted.org/packages/7d/95/8de304305cd9204974b0ca051b86d307cafca13aa575a0ef1b44d92c0d8c/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:702c436735fbe99d59ada02a1f65cfc0d31c0ee8b7290912f8fbc5cd1e4b16c3", size = 196819, upload-time = "2026-07-06T21:33:25.007Z" }, + { url = "https://files.pythonhosted.org/packages/20/71/7c8372d30e42415602ed9f268f7cfd66f1b855fed881ecd168bcb45dbc0b/cffi-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1ff3456eab0d889592d1936d6125bbfbc7ae4d3354a700f8bd80450a66445d4d", size = 184965, upload-time = "2026-07-06T21:33:26.605Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5c/584e626835f0375c928176c04137c96927165cb8733cdb3150ec04e5ee5e/cffi-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c4165821e131d6d4ca444347c2b694e2311bcfa3fe5a861cc72968f28867beac", size = 184952, upload-time = "2026-07-06T21:33:27.823Z" }, + { url = "https://files.pythonhosted.org/packages/2e/d2/065fcae1c73979fac8e054462478d0ff8a29c40cdc2ed7ea5676a061df53/cffi-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:276f20fffd7b396e12516ba8edf9509210ac248cbbc5acbc39cd512f9f59ebe6", size = 222353, upload-time = "2026-07-06T21:33:29.178Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a5/e8bbb1ce5b3ac2f53ad6a10bde44318a5a8d99d4f4a000d44a6e39aeb3e4/cffi-2.1.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7d5980a3433d4b71a5e120f9dd551403d7824e31e2e67124fe2769c404c06913", size = 210051, upload-time = "2026-07-06T21:33:30.534Z" }, + { url = "https://files.pythonhosted.org/packages/28/ed/c127d3ac36e899c965e3361357c3befacd6578c03f40125183e41c3b219e/cffi-2.1.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6ca4919c6e4f89aa99c42510b42cf54596892c00b3f9077f6bdd1505e24b9c8d", size = 208630, upload-time = "2026-07-06T21:33:31.753Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d7/97d3136f81db489ec8d1d67748c110d6c994268fd7528014aa9f2b085e4e/cffi-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d53d10f7da99ae46f7373b9150393e9c5eab9b224909982b43832668de4779f5", size = 221593, upload-time = "2026-07-06T21:33:33.044Z" }, + { url = "https://files.pythonhosted.org/packages/d3/27/93195977168ee63aed233a1a0993a2178798654d1f4bddcdd321d6fd3b21/cffi-2.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c351efb95e832a853a29361675f33a7ce53de1a109cd73fd47af0712213aa4ce", size = 225146, upload-time = "2026-07-06T21:33:34.224Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c1/6dbd291ee2ae5a50a034aa057207081f545923bbf15dad4511e985aafff5/cffi-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7c7a88e2bac086f06d14577332760bdeecc42bdec8ac4077f6260557d9326", size = 223240, upload-time = "2026-07-06T21:33:35.57Z" }, + { url = "https://files.pythonhosted.org/packages/0f/6f/ade5ce9863a57992a6ea3d0d10d7e29b8749fc127204b3d493d667b2815f/cffi-2.1.0-cp314-cp314-win32.whl", hash = "sha256:1854b724d00f6654c742097d5387569021be12d3a0f770eae1df8f8acfcc6acd", size = 177723, upload-time = "2026-07-06T21:33:51.626Z" }, + { url = "https://files.pythonhosted.org/packages/41/de/92b9eeed4ae4a21d6fd9b2a2c8505cbed573299902ea73981cc13f7ff62c/cffi-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:1b96bfe2c4bd825681b7d311ad6d9b7280a091f43e8f63da5729638083cd3bfb", size = 187937, upload-time = "2026-07-06T21:33:53.403Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1a/cc6ae6c2913a03aab8898eee57963cf1035b8df5872ed8b9115fcc7e2be8/cffi-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:7d28dff1db6764108bc30788d85d61c876beff416d9a49cb9dd7c5a9f34f5804", size = 183001, upload-time = "2026-07-06T21:33:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/14/f0/134c00ce0779ec86dea2aa1aac69339c2741a8045072676763512363a2ea/cffi-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7ea6b3e2c4250ff1de21c630fe72d0f63eb95c2c32ffbf64a358cf4a8836d714", size = 188538, upload-time = "2026-07-06T21:33:36.792Z" }, + { url = "https://files.pythonhosted.org/packages/50/d8/3b86aba791cb610d24e8a3e1b2cd529e71fa15096b04e4d4e360049d4a4c/cffi-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6af371f3767faeffc6ac1ef57cdfd25844403e9d3f476c5537caee499de96376", size = 188230, upload-time = "2026-07-06T21:33:38.011Z" }, + { url = "https://files.pythonhosted.org/packages/14/d0/117dcd9209255ad8571fbc8c92ef32593a1d294dcec91ddc4e4db50606f2/cffi-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb4e8997a49aa2c08a3e43c9045d224448b8941d88e7ac163c7d383e560cbf98", size = 223899, upload-time = "2026-07-06T21:33:39.514Z" }, + { url = "https://files.pythonhosted.org/packages/b6/3d/f20f8b886b254e3ad10e15cd4186d3aed49f3e6a35ab37aab9f8f25f7c03/cffi-2.1.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bf01d8c84cbea96b944c73b22182e6c7c432b3475632b8111dbfdc95ddad6e13", size = 211652, upload-time = "2026-07-06T21:33:40.851Z" }, + { url = "https://files.pythonhosted.org/packages/28/3b/fad54de07260b93ddeef4b96d0131d57ea900675df1d410ae1deee52d7a6/cffi-2.1.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:33eb1ad83ebe8f313e0df035c406227d55a79456704a863fad9842136af5ad7d", size = 210755, upload-time = "2026-07-06T21:33:42.183Z" }, + { url = "https://files.pythonhosted.org/packages/cc/82/3d5c705acb7abbba9bbd7d79b8e62e0f25b6120eb7ae6ac49f1b721722fe/cffi-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ac0f1a2d0cfa7eea3f2aaf006ab6e70e8feeb16b75d65b7e5939982ca2f11056", size = 223933, upload-time = "2026-07-06T21:33:43.603Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d0/47e338384ab6b1004241002fa616301020cea4fc95f283506565d252f276/cffi-2.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c16914df9fb7f500e440e6875fa23ff5e0b31db01fa9c06af98d59a91f0dc2e4", size = 226749, upload-time = "2026-07-06T21:33:45.046Z" }, + { url = "https://files.pythonhosted.org/packages/70/25/65bd5b58ea4bfdfc15cde02cb5365f89ef8ab8b2adfb8fe5c4bd4233382f/cffi-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5ecbd0499275d57506d397eebe1981cee87b47fcd9ef5c22cab7ed7644a39a94", size = 225703, upload-time = "2026-07-06T21:33:46.374Z" }, + { url = "https://files.pythonhosted.org/packages/dc/78/aa01ac599a8a4322533d45a1f9bc93b338276d2d59dabbe7c6d92a775c81/cffi-2.1.0-cp314-cp314t-win32.whl", hash = "sha256:7d034dcffa09e9a46c93fa3a3be402096cb5354ac6e41ab8e5cc9cd8b642ad76", size = 182857, upload-time = "2026-07-06T21:33:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/b9/26/d00496b22de4d4228f32dde94ad996f350c8aad676d63bcca0743c8dea4d/cffi-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0582a58f3051372229ca8e7f5f589f9e5632678208d8636fea3676711fdf7fe5", size = 194065, upload-time = "2026-07-06T21:33:48.953Z" }, + { url = "https://files.pythonhosted.org/packages/d5/dd/0c7dbf815a579ff005008a2d815a55d6bb047c349eef536d9dc53d3f0a8d/cffi-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:510aeeeac94811b138077451da1fb18b308a5feab47dd2b603af55804155e1c8", size = 186404, upload-time = "2026-07-06T21:33:50.309Z" }, + { url = "https://files.pythonhosted.org/packages/55/c7/8c8c50cb11c6750051daf12164098a9a6f027ac4356967fd4d800a07f242/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:2e9dabb9abcb7ad15938c7196ad5c1718a4e6d33cc79b4c0209bdb64c4a54a5c", size = 194121, upload-time = "2026-07-06T21:33:56.109Z" }, + { url = "https://files.pythonhosted.org/packages/99/e2/67680bf19a6b60d2bb7ff83baefa2a4c3d2d7dc0f3277034b802e1fc504c/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:37f525a7e7e50c017fdebe58b787be310ad59357ae43a053943a6e1a6c526001", size = 196820, upload-time = "2026-07-06T21:33:57.288Z" }, + { url = "https://files.pythonhosted.org/packages/ed/da/4bbe583a3b3a5c8c60892124fe17f3fa3656523faf0d3484eae90f091853/cffi-2.1.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:95f2954c2c9473d892eca6e0409f3568b37ab62a8eedb122461f73cc273476e3", size = 184936, upload-time = "2026-07-06T21:33:58.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/4b/1f4c36ab273980d7aa75bb126ea4f8971f24a96108acad3a0a084028c57b/cffi-2.1.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:cdf2448aab5f661c9315308ec8b93f4e8a1a67a3c733f8631067a2b67d5913dc", size = 185045, upload-time = "2026-07-06T21:34:00.085Z" }, + { url = "https://files.pythonhosted.org/packages/ef/c3/ad299dc38f3583f8d916b299f028af418a9ec98bc695fcbebeae7420691c/cffi-2.1.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:90bec57cf82089383bd06a605b3eb8daebf7e5a668520beaf6e327a83a947699", size = 222342, upload-time = "2026-07-06T21:34:01.814Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d8/df4543cc087245044ed02ef3ad8e0a26619d0075ac7a77a12dc81177851b/cffi-2.1.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6274dcb2d15cef48daa73ed1be5a40d501d74dccd0cd6db364776d12cb6ba022", size = 210073, upload-time = "2026-07-06T21:34:03.255Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/fac738d73728c6cea2a88a2883dca54892496cbba88a1dc1f2909cb8a6f5/cffi-2.1.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b71d409cccee78310ab5dec549aed052aaea483346e282c7b02362596e01bb0", size = 208551, upload-time = "2026-07-06T21:34:04.433Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3f/0b04a700dd64f465c93020253a793a82c9b4dff9961f48facd0df945d9b8/cffi-2.1.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d3538f9c0e50670f4deb93dbb696576e60590369cae2faf7de681e597a8a1f1", size = 221649, upload-time = "2026-07-06T21:34:06.157Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7c/b7379a5704c79eda57ce075869ba70a0368d1c850f803b3c0d078d39dcaf/cffi-2.1.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:8f9ec95b8a043d3dfbc74d9abc6f7baf524dd27a8dc160b0a32ff9cdab650c28", size = 225203, upload-time = "2026-07-06T21:34:07.489Z" }, + { url = "https://files.pythonhosted.org/packages/5a/02/d5e6c43ea85c41bda2a184a3418f195fe7cf602967a8d2b94e085b83deef/cffi-2.1.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:af5e2915d41fe6c961694d7bfdc8562942638200f3ce2765dfb8b745cf997629", size = 223263, upload-time = "2026-07-06T21:34:08.712Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d8/772b8259bf75749adffb1c546828978381fb516f60cf701f6c83daf60c85/cffi-2.1.0-cp315-cp315-win32.whl", hash = "sha256:0a42c688d19fca6e095a53c6a6e2295a5b050a8b289f109adab02a9e61a25de6", size = 177696, upload-time = "2026-07-06T21:34:26.355Z" }, + { url = "https://files.pythonhosted.org/packages/2f/dd/afa2191fc6d57fedd26e5844a2fe2fcc0bbfa00961bbaa5a41e4921e7cca/cffi-2.1.0-cp315-cp315-win_amd64.whl", hash = "sha256:bccbbb5ee76a61f9d99b5bf3846a51d7fca4b6a732fe46f89295610edaf41853", size = 187914, upload-time = "2026-07-06T21:34:27.58Z" }, + { url = "https://files.pythonhosted.org/packages/05/ef/6cd4f8c671517162379dc79cfae5aea9106bc38abb89628d5c16adf6a838/cffi-2.1.0-cp315-cp315-win_arm64.whl", hash = "sha256:8d35c139744adb3e727cd51b1a18324bbe44b8bd41bf8322bca4d41289f48eda", size = 183004, upload-time = "2026-07-06T21:34:28.905Z" }, + { url = "https://files.pythonhosted.org/packages/11/b6/12fc55092817a5faa26fb8c40c7f9d662e11a46ee248c137aafc42517d92/cffi-2.1.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:f9912624a0c0b834b7520d7769b3644453aabc0a7e1c839da7359f050750e9bc", size = 188378, upload-time = "2026-07-06T21:34:09.926Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2e/cdac88979f295fde5daa69622c7d2111e56e7ceb94f211357fbe452339e4/cffi-2.1.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:df92f2aba50eb4d96718b68ef76f2e57a57b54f2fa62333496d16c6d585a85ca", size = 188319, upload-time = "2026-07-06T21:34:11.101Z" }, + { url = "https://files.pythonhosted.org/packages/e0/27/1d0b408497e41a74795af122d7b603c418c5fed0171450f899afd04e594f/cffi-2.1.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0520e1f4c35f44e209cbbb421b67eec42e6a157f59444dfb6058874ff3610e5d", size = 223904, upload-time = "2026-07-06T21:34:12.606Z" }, + { url = "https://files.pythonhosted.org/packages/8b/31/e115c985105dd7ffb32444505f18ceb874bb42d992af05d5dced7ecf1980/cffi-2.1.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3681e031db29958a7502f5c0c9d6bbc4c36cb20f7b104086fa642d1799631ff8", size = 211554, upload-time = "2026-07-06T21:34:13.987Z" }, + { url = "https://files.pythonhosted.org/packages/5a/67/9e6e09409336d9e515c58367e7cfcf4f89df06ad25252675595a58eb59d5/cffi-2.1.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:762f99479dcb369f60ab9017ad4ab97a36a1dd7c1ee5a3b15db0f4b8659120cd", size = 210795, upload-time = "2026-07-06T21:34:15.972Z" }, + { url = "https://files.pythonhosted.org/packages/19/e5/d3cc82a4a0be7902af279c04181ad038449c096734464a5ae1de3e1401bd/cffi-2.1.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0611e7ebf90573a535ebdc33ae9da222d037853983e13359f580fab781ca017f", size = 223843, upload-time = "2026-07-06T21:34:17.509Z" }, + { url = "https://files.pythonhosted.org/packages/b9/65/b434abc97ce7cecc2c640fde160507c0ecc7e21544b483ba3325d2e2ea17/cffi-2.1.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:86cf8755a791f72c85dc287128cc62d4f24d392e3f1e15837245623f4a33cccc", size = 226773, upload-time = "2026-07-06T21:34:19.05Z" }, + { url = "https://files.pythonhosted.org/packages/b5/9f/d4dc66ca651eb1145a133314cda721abf13cfac3d28c4a0402263ae6ad75/cffi-2.1.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:ba00f661f8ba35d075c937174e27c2c421cec3942fd2e0ea3e66996757c0fdd9", size = 225719, upload-time = "2026-07-06T21:34:20.576Z" }, + { url = "https://files.pythonhosted.org/packages/68/5a/e536c528bc8057496c360c0978559a2dc45653f89dd6151078aa7d8fca1a/cffi-2.1.0-cp315-cp315t-win32.whl", hash = "sha256:cb96698e3c7413d906ce83f8ffd245ec1bd94707541f299d0ce4d6b0193e982b", size = 182760, upload-time = "2026-07-06T21:34:22.059Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0b/0ffe8b82d3875bced5fa1e7986a7a46b748262a40ab7f60b475eb9fb1bb3/cffi-2.1.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f146d154428a2523f9cc7936c02353c2459b8f6cf07d3cd1ee1c0a611109c5d5", size = 193769, upload-time = "2026-07-06T21:34:23.589Z" }, + { url = "https://files.pythonhosted.org/packages/a0/17/1073b53b68c9b5ca6914adf5f8bf55aacc2d3be102418c90700160ea8605/cffi-2.1.0-cp315-cp315t-win_arm64.whl", hash = "sha256:cbb7640ce37159548d2147b5b8c241f962143d4c71231431820783f4dc78f210", size = 186405, upload-time = "2026-07-06T21:34:24.857Z" }, ] [[package]] name = "cfn-lint" -version = "1.51.4" +version = "1.53.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aws-sam-translator" }, @@ -597,114 +626,96 @@ dependencies = [ { name = "sympy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/7d/77cb6921776aff87b261a48610b977b1f3d790c2caee9d6d8c6d251329d1/cfn_lint-1.51.4.tar.gz", hash = "sha256:d37c48645e03abecfd826b8588103b06991abd838fe05c641f2853812289c021", size = 4156267, upload-time = "2026-06-03T15:17:06.006Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/93/36cea246a0ec155eaa16444386675260bbfd38d259cfa26deffc8f417628/cfn_lint-1.53.0.tar.gz", hash = "sha256:dcf285939dea3c15e06bcb23a817bbdecb6c3e0ab19d98147877f04e02b5b07c", size = 4517403, upload-time = "2026-07-09T18:09:16.298Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/7f/a541df327c5c25c4e59e8bc35961f6c244837f9d0f3f2f22f94272e5fd11/cfn_lint-1.51.4-py3-none-any.whl", hash = "sha256:4897321a7d90c6e48859fde0c7c7c3c919815a947ddc85d0584dc12ad5bc544c", size = 6162327, upload-time = "2026-06-03T15:17:03.659Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/f9e2ce6888b5e77072169d884afcd957e69d3f293b03b4eb4dfad48fe358/cfn_lint-1.53.0-py3-none-any.whl", hash = "sha256:8a91d1f0d8d18614410a0c1676213df8cd0ac55fa462f3f6369fb9fdaa7ae28d", size = 5049598, upload-time = "2026-07-09T18:09:14.108Z" }, ] [[package]] name = "charset-normalizer" -version = "3.4.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" }, - { url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" }, - { url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" }, - { url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" }, - { url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" }, - { url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" }, - { url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" }, - { url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" }, - { url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" }, - { url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" }, - { url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" }, - { url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" }, - { url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" }, - { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, - { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, - { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, - { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, - { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, - { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, - { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, - { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, - { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, - { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, - { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, - { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, - { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, - { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, - { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, - { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, - { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, - { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, - { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, - { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, - { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, - { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, - { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, - { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, - { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, - { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, - { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, - { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, - { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, - { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, - { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, - { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, - { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, - { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, - { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, - { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, - { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, - { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, - { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, - { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, - { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, - { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, - { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, - { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +version = "3.4.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/2a/23f34ec9d04624958e137efdc394888716353190e75f25dd22c7a2c7a8aa/charset_normalizer-3.4.9.tar.gz", hash = "sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b", size = 152439, upload-time = "2026-07-07T14:34:58.454Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/81/8e983840c6e5b93b33c2ba81aa3d52c2e42f0e9a690ce7607a2e61da4a5c/charset_normalizer-3.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd6280cf040f233bd7d3407b743b4b4c74f70e8e1c4199cb112a62c941c0772a", size = 322240, upload-time = "2026-07-07T14:32:36.236Z" }, + { url = "https://files.pythonhosted.org/packages/de/d1/b4319dc3229d8272fba305e206fc0a148e2de8d4087917ce62ae6382f359/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa99adc8f081b475a12843953db36831eaf83ec33eb46a90629ca6a5de45a616", size = 216475, upload-time = "2026-07-07T14:32:38.142Z" }, + { url = "https://files.pythonhosted.org/packages/80/33/6c99c1b3e6b8bf730e1bc809b9a2608f224145069114c479a2e9e1494346/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c1225416b463483160e4af85d5fc3a9690ccb53fd4b1865a6437825f5ede3209", size = 238670, upload-time = "2026-07-07T14:32:39.658Z" }, + { url = "https://files.pythonhosted.org/packages/7f/f4/ffbb83546e1f198ecc70ecd372b65cf2b50f9068b380abd67640f17a8e18/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:16d10d789dd9bcca1173c95af82c58433122564b7bc39385124be735a35cbe99", size = 233476, upload-time = "2026-07-07T14:32:41.155Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5f/b98b8da398637b551e427e7be922bdec19177dc54d6811dcdaa503f23aac/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bb41182d93ea91f60b4bc8fbf4c820c69ef8a12ab2d917f3f1834f1acad07e8", size = 223817, upload-time = "2026-07-07T14:32:42.592Z" }, + { url = "https://files.pythonhosted.org/packages/36/31/a276bb2e66243072a3fd06fdcab9cbb61a305b02143d70d2bda21d888fa8/charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:bcf74c1df76758a395bf0af608c04c82257523f55c9868b334f06270d0f2112b", size = 207974, upload-time = "2026-07-07T14:32:44.258Z" }, + { url = "https://files.pythonhosted.org/packages/5e/be/7ee4453d7e88dfbc4104ccd34900b9f2c7c17dac22881865fe0e82424a25/charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b5314963fce9b0b12743891de876e724997864ee22aa496f903f426c7e2fa5b2", size = 221655, upload-time = "2026-07-07T14:32:45.64Z" }, + { url = "https://files.pythonhosted.org/packages/1d/85/181c652953eb5276d198f375b1dd641047392050098100a3a02d6534f657/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e9701d0049d92c16703a42771b98d560b95248949f23f8cf7b4eddd201814fb9", size = 219229, upload-time = "2026-07-07T14:32:47.376Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e7/aaf6da33fc9f4691cda8f7efbc9f69179d3d39ec8a4799baf273ee1d8db0/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:65a7ff3f705e57d392f7261b6d0550fe137c3019477431f1c355e0db0a7d3e15", size = 209704, upload-time = "2026-07-07T14:32:48.855Z" }, + { url = "https://files.pythonhosted.org/packages/63/01/f2fb3bd3a73be48b173ee0c6aa8d2497af97d5663a8c4c4b491de4c62f7a/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79580094b00d1789d1f93ea55bc43cb2f611910c72235b7657f3482ddcc1b22d", size = 226243, upload-time = "2026-07-07T14:32:50.239Z" }, + { url = "https://files.pythonhosted.org/packages/c4/02/c57a22739fe05246b0b5783b3bfb6afaac4eebb46f3ececdfb2f048f780e/charset_normalizer-3.4.9-cp310-cp310-win32.whl", hash = "sha256:432786d3561e69aeeae6c7e8648964ce0ad05736120135601f87ac26b9c83381", size = 150935, upload-time = "2026-07-07T14:32:51.676Z" }, + { url = "https://files.pythonhosted.org/packages/37/8d/ca39a7559a4797505530d084fd3a49a2c959efbbbff146302fb7be4e3b35/charset_normalizer-3.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:8c041122946b7ba21bb32c45b1aa57b1be35527690aeb3c5c234521085632eee", size = 162314, upload-time = "2026-07-07T14:32:53.193Z" }, + { url = "https://files.pythonhosted.org/packages/01/da/a44bd7a13d426e69e4894557106cd58669097bfad4a8681123b618fbfc5d/charset_normalizer-3.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:375b83ed0aecfce76c16d198fbc21f3b11b337d68662bea0a995046682a11419", size = 153075, upload-time = "2026-07-07T14:32:54.554Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e3/85ec501f206fb049259288c1f3506e53876937fb00edb47009348e66756b/charset_normalizer-3.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e94703ec9684807f20cfb5eed95c70f67f2a8f21ad620146d7b5a13677b93e5", size = 317075, upload-time = "2026-07-07T14:32:56.021Z" }, + { url = "https://files.pythonhosted.org/packages/c3/69/2a5385192e67175f7d8bd5ce4f57c24bc956439adeae5c13a99aa28a53d1/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a441ea71902098ffe78c5abe6c494f44160b4af614ed16c3d9a3b1d17fd8ee2", size = 213837, upload-time = "2026-07-07T14:32:57.78Z" }, + { url = "https://files.pythonhosted.org/packages/b3/46/03ddc7da576d814fe0a36dd1f0fd3258e95404b4b2e3c026b7923d7e133f/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:304b13570067b2547562e308af560b3963857b1fa90bd6afd978130130fe2d6a", size = 235503, upload-time = "2026-07-07T14:32:59.205Z" }, + { url = "https://files.pythonhosted.org/packages/4e/6e/de0229a7ef40f6f9d28a837eebf4ec47bdca5dab4e900c84f22919af636a/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4773092f8019072343a7447203308b176e10199920eb02d6195e81bbb3274c29", size = 229944, upload-time = "2026-07-07T14:33:00.803Z" }, + { url = "https://files.pythonhosted.org/packages/a5/34/49b9060e8418b14fb5cba9cf6bfb383111e2538a03a1fb18e66a95aeb3d5/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:04ce310cb89c15df659582aee80a0603788732a5e017d5bd5c81158106ce249c", size = 221276, upload-time = "2026-07-07T14:33:02.199Z" }, + { url = "https://files.pythonhosted.org/packages/44/95/80282cce0fae9c3061203d723ee87da996aed79679e65d8935050ee7ca1f/charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:c0323c9daef75ef2e5083624b4585018a0c9d5e3b40f607eed81a311270b934b", size = 205260, upload-time = "2026-07-07T14:33:03.698Z" }, + { url = "https://files.pythonhosted.org/packages/0c/74/2f62c8821b969ea3bd67cc2e6976834f48ca5d12664d2559ebcd9bcfbed7/charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:871ff67ea1aad4dfd91736464934d56b32dac49f9fbe16cddba36198a7b3a0db", size = 217786, upload-time = "2026-07-07T14:33:05.12Z" }, + { url = "https://files.pythonhosted.org/packages/d9/8d/feabb82cb49fcad14515b1d7d1ca4787b0da7fc723a212bf89bc9e0fac52/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:67830fc78e67501f47bb950471b2dcb9b35b140084429318e862895a8e89c993", size = 216798, upload-time = "2026-07-07T14:33:06.629Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ff/c946d63bc3786d5b84d960b0f7ab7e25b828486a946b5aa997625bcaf6a6/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3d92613ec25e43b05f042302531ec0f00b8445190e43325880cbd6ab7c2581da", size = 206429, upload-time = "2026-07-07T14:33:08.006Z" }, + { url = "https://files.pythonhosted.org/packages/af/ba/5e5007c370702f85d2ef75791fac7943ed41e080364a673b20142e430e3e/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:280081916dc341820640489a66e4696049401ef1cf6dd672f672e70ad915aca3", size = 223066, upload-time = "2026-07-07T14:33:09.783Z" }, + { url = "https://files.pythonhosted.org/packages/83/d5/9096aa3cf532dfad237861544eb47a0f20d5adbf1039760fed8eaae935d9/charset_normalizer-3.4.9-cp311-cp311-win32.whl", hash = "sha256:ac351b3b8014eead140e77e9717e2992c6bbe30b63bc3422422eb84865412e3d", size = 150456, upload-time = "2026-07-07T14:33:11.217Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a1/e29995109e455dc8eff8d0fac6ae509be39561318a7cfeac5d33ad029213/charset_normalizer-3.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:6366a16e1a25018694d6a5d784d09b046edc9eac40ea2b54065c3052672516a1", size = 161410, upload-time = "2026-07-07T14:33:12.743Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8d/1569f4d0032d6ba2a4fe4591c35bf87868c600c41a71eb5c2e1ffa8464c2/charset_normalizer-3.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:1d22856ffbe153a602df38e4a5464f0b748a54002e0d69ac6d2ad0a197cc99ec", size = 152649, upload-time = "2026-07-07T14:33:14.173Z" }, + { url = "https://files.pythonhosted.org/packages/70/4a/ecbd131485c07fcdfad54e28946d513e3da22ef3b4bd854dcafae54ec739/charset_normalizer-3.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45b0cc4e3556cd875e09102988d1ab8356c998b596c9fced84547c8138b487a0", size = 319300, upload-time = "2026-07-07T14:33:15.666Z" }, + { url = "https://files.pythonhosted.org/packages/ec/96/5d9364e3342d69f3a045e1777bc47c85c383e6e9466d561b33fdb419d1f9/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b2aff1c7b3884512b9512c3eaadd9bab39fb45042ffaaa1dd08ff2b9f8109d9", size = 215802, upload-time = "2026-07-07T14:33:17.031Z" }, + { url = "https://files.pythonhosted.org/packages/4b/4c/5361f9aa7f2cb58d94f2ab831b3d493f69efb1d239654b4744e3c09527cb/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9104ed0bd76a429d46f9ec0dbc9b08ad1d2dcdf2b00a5a0daa1c145329b35b44", size = 237171, upload-time = "2026-07-07T14:33:18.576Z" }, + { url = "https://files.pythonhosted.org/packages/50/78/ce342ca4ff30b2eb49fe6d9578df85974f90c67d294113e94efdd9664cbd/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b86a2b16095d250c6f58b3d9b2eee6f4147754344f3dab0922f7c9bf7d226c9", size = 233075, upload-time = "2026-07-07T14:33:20.084Z" }, + { url = "https://files.pythonhosted.org/packages/01/c4/4fa4c8b3097a11f3c5f09a35b72ed6855fb1d332469504962ab7bafcc702/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e226f6218febc71f6c1fc2fafb91c226f75bdc1d8fb12d66823716e891608fd", size = 224256, upload-time = "2026-07-07T14:33:21.747Z" }, + { url = "https://files.pythonhosted.org/packages/87/3a/ad914516df7e358a81aae018caa5e0470ba827fa6d763b1d2e87d920a5f6/charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:90c44bc373b7687f6948b693cceaea1348ae0975d7474746559494468e3c1d84", size = 208784, upload-time = "2026-07-07T14:33:23.313Z" }, + { url = "https://files.pythonhosted.org/packages/d7/74/3c12f9755717dfe5c5c87da63f35d765fa0c00382ec26bf23f7fae34f2ba/charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cdef90ae47919cae358d8ab15797a800ed41da7aba5d72419fb510729e2ed4b", size = 219928, upload-time = "2026-07-07T14:33:24.814Z" }, + { url = "https://files.pythonhosted.org/packages/33/9a/895095b83e7907abd6d3d99aad3a38ad0d9686cc186cb0c94c24320fe63e/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:60f44ade2cf573dad7a277e6f8ca9a51a21dda572b13bd7d8539bb3cd5dbedde", size = 218489, upload-time = "2026-07-07T14:33:26.42Z" }, + { url = "https://files.pythonhosted.org/packages/a1/34/ef5c05f412f42520d7709b7d3784d19640839eb7366ded1755511585429f/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a1786910334ed46ab1dd73222f2cd1e05c2c3bb39f6dddb4f8b36fc382058a39", size = 210267, upload-time = "2026-07-07T14:33:27.952Z" }, + { url = "https://files.pythonhosted.org/packages/83/dc/9b29fa4412b318bf3bfea985c35d67eb55e04b59a7c3f2237168b0e0be6f/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03d07803992c6c7bbc976327f34b18b6160327fc81cb82c9d504720ac0be3b62", size = 226030, upload-time = "2026-07-07T14:33:29.397Z" }, + { url = "https://files.pythonhosted.org/packages/0e/42/6dbc00b8cd16011691203e33570fa42ed5746599a2e878112d16eab403a3/charset_normalizer-3.4.9-cp312-cp312-win32.whl", hash = "sha256:78841cccf1af7b40f6f716338d50c0902dbe88d9f800b3c973b7a9a0a693a642", size = 151185, upload-time = "2026-07-07T14:33:30.781Z" }, + { url = "https://files.pythonhosted.org/packages/80/cc/f920afd1a23c58ccd53c1d36085a71893a4737ff5e66e0371efab6809850/charset_normalizer-3.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:4b3dac63058cc36820b0dd072f89898604e2d39686fe05321729d00d8ac185a0", size = 162557, upload-time = "2026-07-07T14:33:32.176Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/0386d43a261ff4e4b30c5857af7df877254b46bec7b9d1b74b6bf969a90b/charset_normalizer-3.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:78fa18e436a1a0e58dbd7e02fc4473f3f32cceb12df9dfca542d075961c307d2", size = 152665, upload-time = "2026-07-07T14:33:33.711Z" }, + { url = "https://files.pythonhosted.org/packages/b2/06/97ec2aeae780b31d742b6352218b43841a6871e2564578ca522dce4a45c3/charset_normalizer-3.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:440eede837960000d74978f0eba527be106b5b9aee0daf779d395276ed0b0614", size = 317688, upload-time = "2026-07-07T14:33:35.408Z" }, + { url = "https://files.pythonhosted.org/packages/d0/39/8ff066c672434225f8d25f8b739f992af250944392173dcc88362681c9bf/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21e764fd1e70b6a3e205a0e46f3051701f98a8cb3fad66eeb80e48bb502f8698", size = 214982, upload-time = "2026-07-07T14:33:36.996Z" }, + { url = "https://files.pythonhosted.org/packages/92/8f/3a47a3667c83c2df9483d91644c6c107de3bf8874aa1793da9d3012eb986/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e4fd89cc178bced6ad29cb3e6dd4aa63fa5017c3524dbd0b25998fb64a87cc8b", size = 236460, upload-time = "2026-07-07T14:33:38.536Z" }, + { url = "https://files.pythonhosted.org/packages/f1/60/b22cdbee7e4013dab8b0d7647fc6181120fbbbc8f7025c226d15bd5a47fc/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bd47ba7fc3ca94896759ea0109775132d3e7ab921fbf54038e1bab2e46c313c9", size = 232003, upload-time = "2026-07-07T14:33:40.059Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f8/72eb13dcabe7257035cea8aefd922caad2f110d252bf9f67c4c2ca763aee/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84fd18bcc17526fc2b3c1af7d2b9217d32c9c04448c16ec693b9b4f1985c3d33", size = 223149, upload-time = "2026-07-07T14:33:41.631Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3e/faee8f9de92b14ee1198e9163252bb15efee7301b31256a3b6d9ebfdd0dd/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:5b10cd92fc5c498b35a8635df6d5a100207f88b63a4dc1de7ef9a548e1e2cd63", size = 207901, upload-time = "2026-07-07T14:33:43.209Z" }, + { url = "https://files.pythonhosted.org/packages/3a/25/45f30093ae27dd7b92a793b61882a38685f993700113ca36e0c9c14965e1/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4fbdde9dd4a9ce5fd52c2b3a347bb50cc89483ef783f1cb00d408c13f7a96c0", size = 219176, upload-time = "2026-07-07T14:33:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/48/18/c8f397329c35e32f6a837e488986f4ae03bd2abebc453b48714991630c2f/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:416c229f77e5ea25b3dfd4b582f8d73d7e43c22320302b9ab128a2d3a0b38efe", size = 217356, upload-time = "2026-07-07T14:33:46.192Z" }, + { url = "https://files.pythonhosted.org/packages/86/7e/5ce0bba863470fd1902d5e5843968951bddf38abe4742fc97116ef4598b3/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:75286256590a6320cf106a0d28970d3560aad9ee09aa7b34fb40524792436d35", size = 209614, upload-time = "2026-07-07T14:33:47.705Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ef/2473d3c4d869155be4af1191111d59c4d5c4e0173026f7e85b176e23bf65/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69b157c5d3292bcd443faca052f3096f637f1e074b98212a933c074ae23dc3b8", size = 224991, upload-time = "2026-07-07T14:33:49.238Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a3/53ddae3db108a088156aa8ddfafd411ebbc1340f48c5573f697b27f69a39/charset_normalizer-3.4.9-cp313-cp313-win32.whl", hash = "sha256:51307f5c71007673a2bf8232ad973483d281e74cb99c8c5a990af1eefa6277d9", size = 150622, upload-time = "2026-07-07T14:33:50.711Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ef/6953a77c7cf2c2ff9998e6f575ab3e380119f100223381565a4f94c1f836/charset_normalizer-3.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:fe2c7201c642b7c308f1675355ad7ff7b66acfe3541625efe5a3ad38f29d6115", size = 161947, upload-time = "2026-07-07T14:33:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/6e/fb/d560d1d1555debbfe7849d9cac6145c1b537709d79576bf22557ed803b82/charset_normalizer-3.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:611057cc5d5c0afc743ba8be6bd828c17e0aaa8643f9d0a9b9bb7dea80eb8012", size = 152594, upload-time = "2026-07-07T14:33:53.486Z" }, + { url = "https://files.pythonhosted.org/packages/7e/8d/496817fa0944239ecae662dd57ea765cfeaec6a735f9f025d4b7b72e7143/charset_normalizer-3.4.9-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0327fcd59a935777d83410750c50600ee9571af2846f71ce40f25b13da1ef380", size = 317253, upload-time = "2026-07-07T14:33:54.994Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/ef4a69ea338ad3c0deceea0f5f7d2380ae8b52132b06d652cb0d2cd86706/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a79d9f4d8001473a30c163556b3c3bfebec837495a412dde78b51672f6134f9", size = 215898, upload-time = "2026-07-07T14:33:56.334Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e7/5ddfd76fc061eb52de219658a4aa431cbacadf0a0219c8854f00da50d289/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33bdcc2a32c0a0e861f60841a512c8acc658c87c2ac59d89e3a46dacf7d866e4", size = 236718, upload-time = "2026-07-07T14:33:57.9Z" }, + { url = "https://files.pythonhosted.org/packages/49/ba/768fa3f36048d81c477a0ce61f813bc1454d80917ccfe550abd9f44f5e24/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f840ed6d8ecba8255df8c42b87fadeda98ddfc6eeec05e2dc66e26d46dd6f58a", size = 232519, upload-time = "2026-07-07T14:33:59.811Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c4/b3e049d2aa3766180c78507110543d9d50894cc97f57de543f1be521dcdc/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c25fe15c70c59eb7c5ce8c06a1f3fa1da0ecc5ea1e7a5922c40fd2fa9b0d5046", size = 223143, upload-time = "2026-07-07T14:34:01.517Z" }, + { url = "https://files.pythonhosted.org/packages/19/79/55c32d06d76ae4feafe053f061f3e3ab70bcf19f4007797ce8c3efda7830/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:f7fb7d750cfa0a070d2c24e831fd3481019a60dd317ea2b39acbcebc08b6ed81", size = 206742, upload-time = "2026-07-07T14:34:03.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/e0/47c079dd82d217c807479cd59ffd30af56307ea31c108b75758970459ad3/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1c96a7a18b9690a4d46df09e3e3382406ae3213727cd1019ebade1c4a81917", size = 219191, upload-time = "2026-07-07T14:34:04.657Z" }, + { url = "https://files.pythonhosted.org/packages/42/ab/b9bc2e77d6b44a7e46ef62ec5cac1c9a6ba7b9135a5d560f002696ec9995/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a4cfde78a9f2880208d16a93b795726a3017d5977e08d1e162a7a31322479c41", size = 218328, upload-time = "2026-07-07T14:34:06.115Z" }, + { url = "https://files.pythonhosted.org/packages/f1/78/c9c71d599f5aa2d42bcdd35cbbd46d7f535351a57e40ff7d8e5a7e219401/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4d6fcde76f94f5cb9e43e9e9a61f16dacefd228cbbf6f1a09bd9b219a92f1a1", size = 207406, upload-time = "2026-07-07T14:34:07.554Z" }, + { url = "https://files.pythonhosted.org/packages/f6/39/c914445c321a845097ce4f6ac7de9a18228a77b766272125a1ce00d851eb/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:898f0e9068ca27d37f8e83a5b962821df851532e6c4a7d615c1c033f9da6eedf", size = 225157, upload-time = "2026-07-07T14:34:09.061Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f2/c0d4b8508565a36bc5c624e88ed297f5b0b1095011034d7f5b83a69908b5/charset_normalizer-3.4.9-cp314-cp314-win32.whl", hash = "sha256:c1c948747b03be832dceed96ca815cef7360de9aa19d37c730f8e3f6101aca48", size = 151095, upload-time = "2026-07-07T14:34:10.901Z" }, + { url = "https://files.pythonhosted.org/packages/49/fd/a1d26144398c67486422a72bf5812cda22cb4ccfcd95a290fb41ceb4b8e2/charset_normalizer-3.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:16b65ea0f2465b6fb52aa22de5eca612aa964ddfec00a912e26f4656cbef890b", size = 162796, upload-time = "2026-07-07T14:34:12.47Z" }, + { url = "https://files.pythonhosted.org/packages/20/95/d75e82f8ce9fd323ebf059c16c9aadefb22a1ecde13b7840b35835e4886c/charset_normalizer-3.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:40a126142a56b2dfc0aacbad1de8310cbf60da7656db0e6b16eebd48e3e93519", size = 153334, upload-time = "2026-07-07T14:34:14.044Z" }, + { url = "https://files.pythonhosted.org/packages/00/5e/17398df3a139985ba9d11ed072531986f408c8fca952835ef1ab1820c02b/charset_normalizer-3.4.9-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:609b3ba8fcc0fb5ab7af00719d0fb6ad0cb518e48e7712d12fd68f1327951198", size = 338848, upload-time = "2026-07-07T14:34:15.688Z" }, + { url = "https://files.pythonhosted.org/packages/cd/91/7253a32e86b7e1d1239b1b36ba6dd0f021a21107ab33054b53119cc083b9/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51447e9aa2684679af07ca5021c3db526e0284347ebf4ffcec1154c3350cfe32", size = 223022, upload-time = "2026-07-07T14:34:17.248Z" }, + { url = "https://files.pythonhosted.org/packages/cb/32/2e64bd2be10e89c61e57ebe6a93fd98ae88eb7ebe414b5121f22c96c69eb/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cc1b0fff8ead343dae06305f954eb8468ba0ec1a97881f42489d198e4ce3c632", size = 241590, upload-time = "2026-07-07T14:34:18.813Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ef/d96ec496cfea0c21db43b0ad03891308b02388d054cc902cf0e5a1ad6a88/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa36ec09ef71d158186bc79e359ff5fdd6e7996fe8ab638f00d6b93139ba4fcf", size = 239584, upload-time = "2026-07-07T14:34:20.52Z" }, + { url = "https://files.pythonhosted.org/packages/d4/ce/9af95f7876194bd7a14e3dfe4a4de2e0bff02666a3910d72beafd06cc297/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df115d4d83168fdf2cae48ef1ff6d1cb4c466364e30861b37121de0f3bf1b990", size = 230224, upload-time = "2026-07-07T14:34:22.189Z" }, + { url = "https://files.pythonhosted.org/packages/52/94/af74dde74a3996bd959c350709bfe50e297823d70a8c1cbd54b838880863/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f86c6358749bd4fda175388691e3ba8c46e24c5347d0afd20f9b7edfc9faf07d", size = 212667, upload-time = "2026-07-07T14:34:23.857Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f0/f1c4fe746c395922961b5916ed1d7d6e7d4c84851d19ed43cc89980ec953/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:32286a2c8d167e897177b673176c1e3e00d4057caf5d2b64eef9a3666b03018e", size = 227179, upload-time = "2026-07-07T14:34:25.586Z" }, + { url = "https://files.pythonhosted.org/packages/e4/56/6c745619ac397e8871e2bcd3cea1eec86b877488f33888b3aef5c3ed506e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:83aed2c10721ddd90f68140685391b50811a880af20654c59af6b6c66c40513c", size = 225372, upload-time = "2026-07-07T14:34:27.212Z" }, + { url = "https://files.pythonhosted.org/packages/78/ad/98aae8630ac71f16711968e38a5acfecce41b778bf2f0312851020f565a8/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cd6c3d4b783c556fa00bf540854e42f135e2f256abd29669fcd0da0f2dec79c2", size = 215222, upload-time = "2026-07-07T14:34:28.774Z" }, + { url = "https://files.pythonhosted.org/packages/f7/40/9593d54209765207a7f11073c06494c1721e4ca4a0a426c597679bf7f91e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ee2f2a527e3c1a6e6411eb4209642e138b544a2d72fe5d0d76daf77b24063534", size = 231958, upload-time = "2026-07-07T14:34:30.345Z" }, + { url = "https://files.pythonhosted.org/packages/b1/27/693ee5e8a18191eb38647360c51cd505013e2bd3b366aa43fd5344c21e3c/charset_normalizer-3.4.9-cp314-cp314t-win32.whl", hash = "sha256:0d861473f743244d349b50f850d10eb87aeb22bbdcc8e64f79273c94af5a8226", size = 155580, upload-time = "2026-07-07T14:34:31.884Z" }, + { url = "https://files.pythonhosted.org/packages/80/3f/bd97d3d9c613013d07cb7733d299385b41df37f0471310f5a73dc359f0b8/charset_normalizer-3.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:9b8e0f3107e2200b76f6054de99016eac3ee6762713587b36baaa7e4bd2ae177", size = 167620, upload-time = "2026-07-07T14:34:33.438Z" }, + { url = "https://files.pythonhosted.org/packages/3d/c6/eee9dca4439b1061f76373f06ea855678cc4a64c1c3c90b50e479edbb8eb/charset_normalizer-3.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:19ac87f93086ce37b86e098888555c4b4bc48102279bae3350098c0ed664b501", size = 158037, upload-time = "2026-07-07T14:34:35.018Z" }, + { url = "https://files.pythonhosted.org/packages/98/2b/f97f1c193fb855c345d678f5077d6926034db0722df74c8f057020e05a25/charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5", size = 64538, upload-time = "2026-07-07T14:34:56.993Z" }, ] [[package]] @@ -729,23 +740,14 @@ wheels = [ [[package]] name = "click" -version = "8.4.1" +version = "8.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007, upload-time = "2026-05-22T04:08:37.769Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639, upload-time = "2026-05-22T04:08:35.26Z" }, -] - -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, ] [[package]] @@ -777,115 +779,100 @@ wheels = [ [[package]] name = "coverage" -version = "7.14.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/fd/0ab2772530e946e1be1abd0bc09e647ec9b02e88f0867857601fefca8953/coverage-7.14.1.tar.gz", hash = "sha256:30c08f7d90415aa98b3c990385dea2939b0da55f38515e5b369b83655f8523be", size = 920132, upload-time = "2026-05-26T20:41:36.783Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/69/0d2ef01ff4b8fcecd4cba920d11e92fa4f96ae412441d3b56a90a258e69b/coverage-7.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3e3680291c4a1d0dadfa84a2c459576a4af5133abb617905714339a0c73138cf", size = 219722, upload-time = "2026-05-26T20:38:14.002Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ae/9afdeaa31b9d9ce98124b6abf8bb49119bf71aecae04f8567c189d91299f/coverage-7.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a5274669f37f2343635a347b91a60777621341ab3378e9c6ac9335eee704bddf", size = 220240, upload-time = "2026-05-26T20:38:17.424Z" }, - { url = "https://files.pythonhosted.org/packages/51/69/c998589871df7ea7dba865cc5ee32b5a3e1d47ba6c68ef91104c7c46fa5e/coverage-7.14.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cfe5a5fec635799ef33428f1e5e61bafa45a92a96190ba731561ba558ccc214d", size = 246981, upload-time = "2026-05-26T20:38:19.266Z" }, - { url = "https://files.pythonhosted.org/packages/fc/10/1c7d04c13040dac531d21b712bbe08f902e6dd9b58f5d77875c4d030f8f2/coverage-7.14.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:62a9f70b52e0b5a95cfef4a5c5641b06983cadc5e538a3feeb5c00211f523ac2", size = 248812, upload-time = "2026-05-26T20:38:20.75Z" }, - { url = "https://files.pythonhosted.org/packages/c1/65/2a38a4607ef27cadcfbcee034dba5830ae2569f90144a0f4c7dbf47d30b0/coverage-7.14.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c18ebc343e15be53049b3a2dce38fe82d58f37e20ab9094b3a39c0aa4f6bb47", size = 250675, upload-time = "2026-05-26T20:38:22.159Z" }, - { url = "https://files.pythonhosted.org/packages/c9/a2/a446ed9752a4a59b79e0fb6cbb319f6facb2183045c0725462625e66f87e/coverage-7.14.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b84ffdf877644e7096aa936991efeed873f7f3df57b9cd001312b7668ab08550", size = 252590, upload-time = "2026-05-26T20:38:23.63Z" }, - { url = "https://files.pythonhosted.org/packages/9e/fd/e81fbd7ba752365546e9842b1cbdaad3d6919d2a522c590aef16a281ec5e/coverage-7.14.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e854312c4103f2ad4c0dc023b69b77ebfd2c89db5f86c4c94dc2353f9a92167e", size = 247691, upload-time = "2026-05-26T20:38:25.057Z" }, - { url = "https://files.pythonhosted.org/packages/53/35/f3c26fdaae9ea937d154ca4d372e5ea0a4167ff70d36c6074ac2eacb2f83/coverage-7.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c643734307300234fafa36bf2a040a7235f8f177ea1fd6ec1423aea6fb7b929f", size = 248716, upload-time = "2026-05-26T20:38:26.406Z" }, - { url = "https://files.pythonhosted.org/packages/2e/14/940b6c49551fd343e8507ee2b0ba7af5d0aa04ed5bf768285cb7c72a9884/coverage-7.14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:84ac9499e48700399a5dd0ea7085b5091961fec52c68d66b4ec0d3cf7f4441b1", size = 246721, upload-time = "2026-05-26T20:38:28.282Z" }, - { url = "https://files.pythonhosted.org/packages/aa/2c/40fc0634186c28292a662dff578866b3913983d6c375a3c2a74020938719/coverage-7.14.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7f02d09f70776579b926d889a4c9c235070a1f47c40458aeaca563fae5acfdb5", size = 250533, upload-time = "2026-05-26T20:38:29.753Z" }, - { url = "https://files.pythonhosted.org/packages/de/e3/2c26bf1e811f9df991ff2a9bdddebdd13ee0665d564df7d05979f9146297/coverage-7.14.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ce66d8e46da2bb5ee313a745cbd2e391d319176c1f7a9451bfcd3a2fb920859b", size = 246990, upload-time = "2026-05-26T20:38:31.516Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b0/060260ef56bd92363ebdce0c7095ce422b06e69aae71828efeca473ab1ca/coverage-7.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c912c259304cfb5ee584481cfb7ce1ff932b4d61e6c9140b8f19cb7b5ed82332", size = 247593, upload-time = "2026-05-26T20:38:33.065Z" }, - { url = "https://files.pythonhosted.org/packages/63/f3/501502046efeb0d6d94b5ca54941d95f1184183dd6bdb7f283985783bb4a/coverage-7.14.1-cp310-cp310-win32.whl", hash = "sha256:1238cb94638e610e972c60dac68e813f868dc7d6e982535270558443058d9d59", size = 222330, upload-time = "2026-05-26T20:38:35.36Z" }, - { url = "https://files.pythonhosted.org/packages/a0/5d/1bf99f2c558f128faf7906817ccbdb576ba815d3b41ce2ac1719b70a3663/coverage-7.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:fc459e5d73be2d6332fcfe8dbf3d8994671fe33c700f4565988ecfa511547253", size = 223261, upload-time = "2026-05-26T20:38:37.196Z" }, - { url = "https://files.pythonhosted.org/packages/7d/d7/477ad149490e6cb849f28abea1dabb9c823cea72e7500c81b4240ce619c0/coverage-7.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:478b5bcd63c2e1357c5c7e16c070690df7b07f676b1c114d7b93e533c664309f", size = 219848, upload-time = "2026-05-26T20:38:38.715Z" }, - { url = "https://files.pythonhosted.org/packages/91/82/a5eb47257c50601bb7b9a9d2857c67b7a3a85ad74180eb2c98bb1fbe0ce5/coverage-7.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a24a81f9715ee42ef59a316cc11611c98fe23920f7c81861315c9f3ff4a230f4", size = 220354, upload-time = "2026-05-26T20:38:40.232Z" }, - { url = "https://files.pythonhosted.org/packages/43/8b/78419b5391a5cb706b6544390507e469d83ffc9a8248b02c4011aceb9365/coverage-7.14.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:196a13319ad88d6d8ef5ab489ec4f44ddde2143c0c7d5b27786f6c3ffd56a7e1", size = 250771, upload-time = "2026-05-26T20:38:41.782Z" }, - { url = "https://files.pythonhosted.org/packages/77/63/e77aaacd491182210d639636b7a8bba23ffffa9b82aa3762da9431855fa9/coverage-7.14.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d452fd08b5c72c5167c93e6867b5c08500bd40f2a21e1e854a500550b6cc36f", size = 252683, upload-time = "2026-05-26T20:38:43.305Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/a022e3cfbec2ac241640003cb3a817e161d9c7f5aa9b49173756cdc03204/coverage-7.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23bf7fa51ac02e07fc7c96849b82946da47ae862dc8f86d183b2a4864fc38129", size = 254791, upload-time = "2026-05-26T20:38:45.361Z" }, - { url = "https://files.pythonhosted.org/packages/61/d6/967e408aca4c1ceb88cb0cc677169110ae7f5995fb5eaf5fb1f5a1bb8f5d/coverage-7.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcaa50684dcaadfa599ac48f81103c756d791cfd85c97203d2217c593d48b860", size = 256748, upload-time = "2026-05-26T20:38:46.91Z" }, - { url = "https://files.pythonhosted.org/packages/b8/be/869188f7fe28638078ec479331ace6dc5f7b40b7153eb616f47ab79404d8/coverage-7.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4ea1c034f95c9b056e856b794630b17f9fa3d57e4800ff1e503d3be0f9c9078c", size = 250907, upload-time = "2026-05-26T20:38:48.493Z" }, - { url = "https://files.pythonhosted.org/packages/07/aa/adb7d3b4278d690e68703abcd76ab1b948242e3668d921711551b78f9ddb/coverage-7.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c7e057326434e441306226fbeb5d1aaf14a2637efe97ba668306635835f32ad7", size = 252483, upload-time = "2026-05-26T20:38:50.074Z" }, - { url = "https://files.pythonhosted.org/packages/43/61/331c74103c62dcb0c4b9b3a0de9a61aca016208b0a90f109592a9f9ecc28/coverage-7.14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:59baf88468dbc8d63b1887afd92bda52e40bb1561696e5819670601403810cec", size = 250545, upload-time = "2026-05-26T20:38:51.613Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b6/c5dae3c104d89be04828f61810e6b3473825482e4c288cc4ed04553e08ae/coverage-7.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d34d75f892b3ab73ba11cab5442cce7b3e168fd64162b16f0e1e0d09c508edef", size = 254310, upload-time = "2026-05-26T20:38:53.503Z" }, - { url = "https://files.pythonhosted.org/packages/ad/a1/2b9d5863e3b83c01ad8199e3c597802fbb3a9dc90b058885804c20296d31/coverage-7.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3a56abc20a472baf0304c455721bc601477440d28ecfde8a03dde79ede07e0df", size = 250266, upload-time = "2026-05-26T20:38:55.414Z" }, - { url = "https://files.pythonhosted.org/packages/7f/5e/0e511fbdb269359be26fe678a1c3fa1f2aa2a01573cc3f54268c8d6d4797/coverage-7.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6a3cb83d1552c0cd1b4906655b6a33fd4a8473229633a901c6b73bf86914dee9", size = 251174, upload-time = "2026-05-26T20:38:57.141Z" }, - { url = "https://files.pythonhosted.org/packages/85/10/e55307b622b3dd9671cb321824502dc10f93e72f2802b9946159a8edadeb/coverage-7.14.1-cp311-cp311-win32.whl", hash = "sha256:10274a1fbeb8ec5d72966e17bb198a3104257aca4ac09d98667c5f8aca8c8548", size = 222354, upload-time = "2026-05-26T20:38:58.727Z" }, - { url = "https://files.pythonhosted.org/packages/71/cf/107421693cfb71e4f1ca5bf70443f64d4161878068d07a3e51c7ad21d17b/coverage-7.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:87ebdf787d4888e3f3f2d523eadc6e18c6d18c6d0eb173801a189641627fb37e", size = 223290, upload-time = "2026-05-26T20:39:00.413Z" }, - { url = "https://files.pythonhosted.org/packages/b8/1d/3e3644585eb29e9dafefb19555078529a4d7cce12bd21929664eea989277/coverage-7.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:dd34767fa19848d35659ffc0a75314f58c7af3f1cd87ec521e8292a1238398a3", size = 221953, upload-time = "2026-05-26T20:39:02.159Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b7/bdbb725ba02c5b42825b200c940f38b7a54fcad24627b7192f78f8110d76/coverage-7.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a06c76364a9360e33d6d23769aefdf7f66f38e2ffb60ceb1baaa4989d83b695c", size = 220022, upload-time = "2026-05-26T20:39:03.702Z" }, - { url = "https://files.pythonhosted.org/packages/72/81/fdc0898a55c6219223291ec1a1fe89966ef212ce82276aa0899df84b5de0/coverage-7.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c", size = 220379, upload-time = "2026-05-26T20:39:05.381Z" }, - { url = "https://files.pythonhosted.org/packages/de/72/de048c4a25e13bce59ac6a339351c10bdf2515e07459afcdaf04dc3143a2/coverage-7.14.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:84b535f00655ecafe1d929d1fb00ed5d6fa3051ea643ab2c161a3887b86f294b", size = 251888, upload-time = "2026-05-26T20:39:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/28/30/300c343f68beb9d4cbb64ec81e58c5b6b80b56927f72d2b38654ac26e013/coverage-7.14.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b6b0853b895fe0e98cbfc580d1ec3393d9302b4b1e96a77b3f5c91fdab899e6", size = 254624, upload-time = "2026-05-26T20:39:09.037Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ed/7b25642496e8170b6bac14adce00537c6e5fa2d586159401a4de3e8b49e6/coverage-7.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:442cc9c952b2df400cda54bb04ab87330cf2cd08a8692cbbea36773531eb6f37", size = 255739, upload-time = "2026-05-26T20:39:10.889Z" }, - { url = "https://files.pythonhosted.org/packages/7f/a2/abd210b8c4e29c24e4624916db97bb519097a91034aaeb767f937e7da794/coverage-7.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8270544c361ed405a27a060dbc9ed2c124b084d96dfdc2d9a2510482aef981ad", size = 257998, upload-time = "2026-05-26T20:39:12.722Z" }, - { url = "https://files.pythonhosted.org/packages/7f/24/7c50beed3792fe62f6ce0545c6686ce83379719e2c0276179333d97eae92/coverage-7.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:48b283b1dd6372e8de2a7a9a4c4d5dc06f4d4fd209b876f3c88a7a205a0c8f84", size = 252296, upload-time = "2026-05-26T20:39:14.259Z" }, - { url = "https://files.pythonhosted.org/packages/15/05/0f874628ebcbfc77ead559ff210281ef06a97db08481832e7dd39274a135/coverage-7.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5b0c99ba93a07d56f6df340bb79be53202a082b2fdb81bfe6190b741a3470d54", size = 253658, upload-time = "2026-05-26T20:39:15.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/6f/ca6ad067364b337ef997802115e7ecad2abd2248b05471464b0dea02b4d4/coverage-7.14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e471bc5769ff073b058cfadb0d736b56ce067c8560eabeb0da88462df98c23e7", size = 251803, upload-time = "2026-05-26T20:39:17.537Z" }, - { url = "https://files.pythonhosted.org/packages/c0/30/b9b4d377cd9f40baf228068f5a81faf8450c6228503011bd499708483a50/coverage-7.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f497a1ea81d4cd7c10ddcaa685135b9aabd291af3d55775a9ddf3cb7a364cdd9", size = 255873, upload-time = "2026-05-26T20:39:19.414Z" }, - { url = "https://files.pythonhosted.org/packages/3c/21/7c721a9e5e6bb88547d30a787aefb97512d3f54c1324c7488d9b3743f7f9/coverage-7.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2222be86d0b54f5dd5a38f45f17f315f737245e857bf0bdedc70734f84a13c02", size = 251372, upload-time = "2026-05-26T20:39:21.169Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f8ae5a2200130e1503cd7661a6cd3b2b7bacef98277fbf3571fb13f8b766/coverage-7.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:85e85586565842f6932abebd4c18bcb1074223dc0b3576e7d173ca710622813a", size = 253245, upload-time = "2026-05-26T20:39:23.097Z" }, - { url = "https://files.pythonhosted.org/packages/34/62/70a9024672a5f6910517d9628c52c9afbdd3cf8f46426af52bb148a56fff/coverage-7.14.1-cp312-cp312-win32.whl", hash = "sha256:4a28fd227808366b196a75476dced2eb35b351d6766ba9c858dc93319e87f4f1", size = 222567, upload-time = "2026-05-26T20:39:24.868Z" }, - { url = "https://files.pythonhosted.org/packages/f6/81/8b7cd386839b039ebe1855733b9f9449a8dec5d79564018234f185a7fa70/coverage-7.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:54acdb6674a4661768d7bf7db32dfb9f46ab1d764f8aba6df75ce1a6a088724e", size = 223372, upload-time = "2026-05-26T20:39:26.603Z" }, - { url = "https://files.pythonhosted.org/packages/ae/ba/b44d472022f620d289d95fa830143235c0c36461c6f2437ea8d51e5481ed/coverage-7.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:99cd41ff91afd94896fea3bc002706b6ae4ce95727d06e4a0f39c0a8d8bd8b1a", size = 221989, upload-time = "2026-05-26T20:39:28.242Z" }, - { url = "https://files.pythonhosted.org/packages/8a/9e/5f6d56327c62b185225d145191c607e07515294a0aa6338e58805cd4a5ac/coverage-7.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:be9f2c802dcfce3f71298303aa5dad0dce440a76c52f2f60dacd8656dab78793", size = 220044, upload-time = "2026-05-26T20:39:29.902Z" }, - { url = "https://files.pythonhosted.org/packages/75/92/e82aca356744cbbc0f77a0b623e38918c1872361963413a3bab5d0340393/coverage-7.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6223a72fd0e4c7156353ec0f08a5f93623e1d3034d0e2683b9bb8ea674131b1d", size = 220412, upload-time = "2026-05-26T20:39:31.561Z" }, - { url = "https://files.pythonhosted.org/packages/27/c9/385bde0bf7ed0f4bf3a7ee5367060a86b5d218718cfd6fb943c0f836b34f/coverage-7.14.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7279d2110a28cebc738b6459ecda2771735a4c18465fbbd36b3288fe5ed92247", size = 251412, upload-time = "2026-05-26T20:39:33.337Z" }, - { url = "https://files.pythonhosted.org/packages/51/8c/23faf6a2343a0d17f960a4bd56c43bc7eb4cf312f774dd6ceebd82c7d8fc/coverage-7.14.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9eeb3fcbc13ba40dfbdb22d01d196a28e9cef9ed4c29b60061a1e0e823a9929d", size = 254008, upload-time = "2026-05-26T20:39:35.009Z" }, - { url = "https://files.pythonhosted.org/packages/42/06/36f4aa9ca8a815e6036156e80706a67828bb97bd826948244f6996dda957/coverage-7.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f0cfc27c539f07cf5c0a4cfe211d0b6cae039f8f40526dbaa71944e64b50a7b", size = 255241, upload-time = "2026-05-26T20:39:36.71Z" }, - { url = "https://files.pythonhosted.org/packages/ca/79/95266316352f90f6b1c6736bb413302edfde2453fb32422d3911642691b3/coverage-7.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:221c70f316241a78e77e607c227cefc8808d4e08f28d99c04f35694690e940be", size = 257373, upload-time = "2026-05-26T20:39:38.412Z" }, - { url = "https://files.pythonhosted.org/packages/e3/9c/58316d1f66c488b5fca8a0eb3e98348807813efa8a0d0833b9021be27488/coverage-7.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:da028256b04ec30e5e0114b6f76172938c313991f0a2d3d894271315cf5d5e43", size = 251635, upload-time = "2026-05-26T20:39:40.268Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5a/ca2398a568e16fed7bb713e84ba3603a7164fb65779abe645c565ec890d5/coverage-7.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76a085d7005236a767e3426148b2c407e53ad61695c562f8a81da2d373324901", size = 253373, upload-time = "2026-05-26T20:39:42.145Z" }, - { url = "https://files.pythonhosted.org/packages/6e/2c/0396562c32deaebe7be51d865b3a41e9a87d7561acafe1a28f53b07e019a/coverage-7.14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b553d04b5e778a8e56d57eb134aff42a92718ecba45e79c4764ecfa40efd92ff", size = 251341, upload-time = "2026-05-26T20:39:43.907Z" }, - { url = "https://files.pythonhosted.org/packages/fd/8f/a94f9221184c9cae1ee115820e3798e48b6b17777a9f19e46fb9a0c8dc74/coverage-7.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:46f714d2fb8ae2f4f29f23ada7f1e79b759fff5a70f94a1dac23af204c3ec9e4", size = 255497, upload-time = "2026-05-26T20:39:46.166Z" }, - { url = "https://files.pythonhosted.org/packages/71/69/505d70e47db1eaebcd002c39759707621ef184cd6b1ae084d9f41293f323/coverage-7.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1896f5e19ff3f0431c7ce2172adc54890fd97f86b59ced8ca1649145d9ffe35d", size = 251159, upload-time = "2026-05-26T20:39:48.03Z" }, - { url = "https://files.pythonhosted.org/packages/e0/aa/58681c383aa33a9d2ed40a02d7a22fbf780d1fa4d575396365777828198c/coverage-7.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:62fd185ef9df3c33d1c8178c5af105f762afbad96038de9a4ae100aa6297ca33", size = 252934, upload-time = "2026-05-26T20:39:49.872Z" }, - { url = "https://files.pythonhosted.org/packages/eb/fd/11c928cd6bdffc7074bb5965c173d9ebf517fb00205e1da524b98d29ef92/coverage-7.14.1-cp313-cp313-win32.whl", hash = "sha256:ab4af6352741a604c431c6072fce5bee33bf0f20dc7a56618d6bf6bb89e9810c", size = 222584, upload-time = "2026-05-26T20:39:51.68Z" }, - { url = "https://files.pythonhosted.org/packages/6f/92/fb416fc26d340dcba19518c418d6048e913186e17243982c5e435e41fa7a/coverage-7.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af486dabe8954d03b087f0021540897afe084f04e16ff5579e08cc46f871416", size = 223394, upload-time = "2026-05-26T20:39:53.472Z" }, - { url = "https://files.pythonhosted.org/packages/73/c6/02d56e3867972f77d5036de924643f26c056e848f00452cafb4dbc3c29b4/coverage-7.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:2224f89ffd0c5605ccce1ed7a584da162bc7c55f601ab1c946bc9de31a486b42", size = 222015, upload-time = "2026-05-26T20:39:55.374Z" }, - { url = "https://files.pythonhosted.org/packages/4d/9e/fcc77914050df73f7662fa1f00902774c79c075a8388ab334074574bf77e/coverage-7.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de286598cc65d2b489411174b1faec2f5a7775fb3201fd925db2a76b4030f37d", size = 220733, upload-time = "2026-05-26T20:39:57.189Z" }, - { url = "https://files.pythonhosted.org/packages/f7/67/2963cbdaf5cbadec44efa3a1e39eaa1f02df4079585f05387607a221e126/coverage-7.14.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:042c46ded7c288aeb07cf14a28b6c1e10b78fcba40171c3fa1e939377eeef0b5", size = 221086, upload-time = "2026-05-26T20:39:59.019Z" }, - { url = "https://files.pythonhosted.org/packages/c8/c5/8701645574e11881f2f47d8930f98bc48b5d43b25eb5b4430dfc4a2f9f48/coverage-7.14.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f4ddbe407477f04c45115d1a4e5bc480f753553b534d338d4c3358b1cdd0ea52", size = 262381, upload-time = "2026-05-26T20:40:00.822Z" }, - { url = "https://files.pythonhosted.org/packages/7c/28/7a64d73598263e0c5abd5084211a8474488d31b3c552ff531c719dfcff62/coverage-7.14.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d13e6725992e2d2fd7d81d4f5241952d13740121dfd501da09201be39b2c003a", size = 264458, upload-time = "2026-05-26T20:40:02.506Z" }, - { url = "https://files.pythonhosted.org/packages/fa/d8/4969179db9f7eb4df218e69540adf829d1c835f59452513d065d15446802/coverage-7.14.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f747dc8edcfe740130f28f32f3995e955494285717e86ee25af51db2219df08a", size = 266884, upload-time = "2026-05-26T20:40:04.421Z" }, - { url = "https://files.pythonhosted.org/packages/a6/78/a45d5794dbc9bafd97afc96a4377c86c7820d78b6cf51b89bc1d4e919275/coverage-7.14.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced2f09ef276fd58611a1ef502164ad266d2b75174e5a40cabbdb4033f9f6cf2", size = 268022, upload-time = "2026-05-26T20:40:06.298Z" }, - { url = "https://files.pythonhosted.org/packages/21/cb/4f5e354e9e3e67af96bd4e57113e6db6b22298c7168b13eec408a549903d/coverage-7.14.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b84800013769a78ccb9ef4659402e26d06867e337b61ec365f77ad008adea80e", size = 261631, upload-time = "2026-05-26T20:40:08.226Z" }, - { url = "https://files.pythonhosted.org/packages/ec/49/eced49af4cb996d5d8b7e94e736175c513e4facd3398507b89892b4326d8/coverage-7.14.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ea8cd6ca0ee9f616aaef3afc6882e32c2cbf18b00d96313ffd76af650574034d", size = 264443, upload-time = "2026-05-26T20:40:10.137Z" }, - { url = "https://files.pythonhosted.org/packages/f1/d8/5603a88a7c5913a6b54f6cb1a8c46f7b39cbb30f27cd3f492908da09b2d7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:aa5e304a873fabddc11e484e9b6b738bd38bd7bed17b09aa84eecf5332e8b8bb", size = 262069, upload-time = "2026-05-26T20:40:11.999Z" }, - { url = "https://files.pythonhosted.org/packages/f0/59/2ae3cb79da554a06c8619d6c88ea19dd1e4aed4b834b6a83bb1fa243bdc5/coverage-7.14.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5a1c5215be81035e629d5bc756650634d0bf31991038db7a0eccb90f025ce16d", size = 265780, upload-time = "2026-05-26T20:40:13.858Z" }, - { url = "https://files.pythonhosted.org/packages/af/5f/b130c1dc999031f2648bd25317fbce505ad8d5562079b4ed81e736a84967/coverage-7.14.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:79058c47dae6788504b5effb319961bcd72d7240551464b91d474bc0ed186d69", size = 260970, upload-time = "2026-05-26T20:40:16.142Z" }, - { url = "https://files.pythonhosted.org/packages/87/d1/ec13ccddeb48ec963bdfa72a11224bac2584bd045ba13beca82f8113e9c7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:370c5afae3fa0658e11694a32b24c2778f6bc2d17718121f94ee185e69f26b54", size = 263157, upload-time = "2026-05-26T20:40:18.382Z" }, - { url = "https://files.pythonhosted.org/packages/cf/c2/cd91ead503045161092d3845f7bb95ea2f25131ce96d3e314dd835d91b9c/coverage-7.14.1-cp313-cp313t-win32.whl", hash = "sha256:3758dd0a7f1fa57365ef2e781df0f0731d38b6e3772259d13dae4bd8a958d4b1", size = 223259, upload-time = "2026-05-26T20:40:20.381Z" }, - { url = "https://files.pythonhosted.org/packages/71/9f/1e28d97e6bd2c76b07f38b7c02870f1371255ff6717f54eca578fcbbdd0e/coverage-7.14.1-cp313-cp313t-win_amd64.whl", hash = "sha256:6ff665fb023a77386fe11685190cee1f60a7d635994a30d9b0a061533d470fce", size = 224320, upload-time = "2026-05-26T20:40:22.316Z" }, - { url = "https://files.pythonhosted.org/packages/a9/e0/d936e908f0e1efa55e52b91e01b52f1055cef5e1ab2718493390ed8e2fb8/coverage-7.14.1-cp313-cp313t-win_arm64.whl", hash = "sha256:17a5a241e5997621a956a7f402a7433ef4221e5152809b785bec79e2323799f1", size = 222577, upload-time = "2026-05-26T20:40:24.894Z" }, - { url = "https://files.pythonhosted.org/packages/d6/34/fc2f101b151af3799a101f0550b0454aa008afdc0add677394ec4aa8ea10/coverage-7.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d5ed429d0b8edaac649e889b4ffcedb6c80b06629a3f93050e3dddfb99235bee", size = 220091, upload-time = "2026-05-26T20:40:27.249Z" }, - { url = "https://files.pythonhosted.org/packages/3d/a7/1ebae2ab5b961b5c79bb09fe7b3ac99edb190d8be4a8c510b2cf66f46468/coverage-7.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8011224a62280e50dab346960c03cf47aca1a1e09e608c0fb33fd6e0cc8e9500", size = 220421, upload-time = "2026-05-26T20:40:30.084Z" }, - { url = "https://files.pythonhosted.org/packages/5e/90/92aca9cf0acc95123c96cd1eb1f08917897a7f5dee01e15738922971ec31/coverage-7.14.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:12c42ec1e14f553c4f817e989365982e646e27211f10a0f717855b94a79c8906", size = 251466, upload-time = "2026-05-26T20:40:32.542Z" }, - { url = "https://files.pythonhosted.org/packages/26/2b/78048cbe3b999f6cbf9cc0d90abba6a88a3e0863a8c1c6cbc762f3f8802f/coverage-7.14.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06144cd511cf2624873a035c5069cf297144f6e77a73ee3d7a55b605ec5efb42", size = 253973, upload-time = "2026-05-26T20:40:34.473Z" }, - { url = "https://files.pythonhosted.org/packages/8e/21/c2e33b29d1cfde484a19d437afc343c6cd30b08d78cbbf9f5aff14e57b2b/coverage-7.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a311d8e1da24be5c1ccf85cbfb06315dbaa1703d5a1eab3f6432c72b837917c8", size = 255318, upload-time = "2026-05-26T20:40:38.154Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ee/aad2f108d63b769121005302f16bf66db8625c88ceaba466942e09a2607e/coverage-7.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c79cead5b5bc584d9c71451cb984d0e3a84e0c0937379c8efcbf27c8d661b851", size = 257633, upload-time = "2026-05-26T20:40:40.164Z" }, - { url = "https://files.pythonhosted.org/packages/c2/f8/11a2c29b4fd76d9849f81d0bb812ec0017a9396df3217214e38934a8c837/coverage-7.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dcbf65f1f66a26cdd88c35cf68fb4729c5d1cd2e88added72420541dfb212034", size = 251488, upload-time = "2026-05-26T20:40:42.631Z" }, - { url = "https://files.pythonhosted.org/packages/c9/b8/9a5820de4b8ac2b71d85e3b5fb49108d7469c665f0e2ad0dd7569023e305/coverage-7.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fd86572566fb40189a8260446158235159bc7a82dfbc87a3b39cf4fb57fcec1c", size = 253329, upload-time = "2026-05-26T20:40:45.208Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ff/f33e4823667e27548e8fd8df44217515303f9808d0ff29817db56f87d990/coverage-7.14.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7771b601718fdde84832c3a434ca9bbf4ae9adbc49d84198b4110700c3c77c36", size = 251291, upload-time = "2026-05-26T20:40:47.502Z" }, - { url = "https://files.pythonhosted.org/packages/68/9b/489db0ebb209054766b90a9014a45f6d26eb724c02ec21311c3733b5a644/coverage-7.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:39b21e212c55af06fa375e3dbf90a8a8e38792f3a910c580066d23563830ddd5", size = 255564, upload-time = "2026-05-26T20:40:49.372Z" }, - { url = "https://files.pythonhosted.org/packages/27/b5/16bc2d4c2409b23c7737edb68c83bc89e345f378050549fe1d75ac7d34d5/coverage-7.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f2302660e32562a532b442480121aef8aa61a5bdb20b30bf0adab29f10a5a4b4", size = 251107, upload-time = "2026-05-26T20:40:51.677Z" }, - { url = "https://files.pythonhosted.org/packages/7d/0c/2629997469a00cd069d588a41c9dc887610f2775ae89d250c4791e65272a/coverage-7.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:03a6f93c1ec3b7f2e77b5dbcc5573a2c21f12529a5c6bbe0f16f72303cc2fa4d", size = 252764, upload-time = "2026-05-26T20:40:54.267Z" }, - { url = "https://files.pythonhosted.org/packages/d2/ee/f78d63c8f079e0d7211c7e2401fa17e311514534ba61bae03e4b287ce4ab/coverage-7.14.1-cp314-cp314-win32.whl", hash = "sha256:8a3ce026d73290f42f08dafecbd82c193a74df280461fbf97300fec51fd133ee", size = 222837, upload-time = "2026-05-26T20:40:56.496Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b9/be539854f93a70dfbeec69117f33ec70dc42ff0b65b5b07ab8d40d04228e/coverage-7.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:114c95ef29302423b87d159075805f4ab973254a2638a5d7d046c94887cc87d7", size = 223650, upload-time = "2026-05-26T20:40:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/fe/9e/24e2842fef40f35ac82ba3a7719c8023d011bf3bf652d0675316a9d088a1/coverage-7.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:a07891c3f4805442b31b71e84ba3cf29ed1aa9a428284e06deeb4b23e5b46343", size = 222218, upload-time = "2026-05-26T20:41:00.321Z" }, - { url = "https://files.pythonhosted.org/packages/0a/1d/ac0a9df5fe31c1e8bdd658074905fc12844a05c1a7e3fdb8417e97c31e23/coverage-7.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1101a5ebb083aecb625ebb6209d4105b58f647b093cb2dc8122d7b33f743cfe1", size = 220822, upload-time = "2026-05-26T20:41:02.281Z" }, - { url = "https://files.pythonhosted.org/packages/32/cf/f964fd9aff20323f9f1a726c97135f8a76bcd87b92dad141a456a43f3c64/coverage-7.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:851b9e1e4e8a4608e77c79714b2e77c0970d2ed7202a05e92ae407817481887b", size = 221084, upload-time = "2026-05-26T20:41:04.593Z" }, - { url = "https://files.pythonhosted.org/packages/d8/5e/7e5ef2aba844de2b80d678619fcf0841b42e3f37f16411226f3fe4c1016f/coverage-7.14.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d5b89cdfb2ee051b71e8c3c70bd81a9eff81100f736a269136fe1a68efe00474", size = 262454, upload-time = "2026-05-26T20:41:06.641Z" }, - { url = "https://files.pythonhosted.org/packages/64/62/75809bded87015cc4935524218a2a8ed8dd1a8498bfed30a2f4f7a4b4d34/coverage-7.14.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0177614a0370f227888b4e436a7c55686d6a9f90eb1ade2b624ba685a1686e86", size = 264578, upload-time = "2026-05-26T20:41:08.556Z" }, - { url = "https://files.pythonhosted.org/packages/f3/42/d33392dc14633525012d2d504fa1a33b05538bf535f5c1d64675e5754b78/coverage-7.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d69af5dea2de76fc485a83032a630523f985198b7e25be901ec60181587b01e", size = 266981, upload-time = "2026-05-26T20:41:10.824Z" }, - { url = "https://files.pythonhosted.org/packages/2a/49/0157c4428c2aca7f1e09d5565930586fd5ae36f1655f08b0daa7cf1fcae1/coverage-7.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35ab22d91de736e8966b980dc355cbcdd2c6dbbcfe275f9a2991bc8a91b3df65", size = 268112, upload-time = "2026-05-26T20:41:12.966Z" }, - { url = "https://files.pythonhosted.org/packages/96/26/86b9ce71f4092b1ed325ce1421698081df1286b833400b6836912834d6e0/coverage-7.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:357d4e32935c36588aaba057d734fa32428c360c9fc2e4442afbf1b646beee6e", size = 261558, upload-time = "2026-05-26T20:41:15Z" }, - { url = "https://files.pythonhosted.org/packages/20/4c/c311210c5472cf5401d8422b0d7812cdd520f24417673afabda6c323faca/coverage-7.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:51bd64741cc6fa065abd300ede1afe5a5291ece9c31da8b24884deda48bcc3f8", size = 264447, upload-time = "2026-05-26T20:41:17.369Z" }, - { url = "https://files.pythonhosted.org/packages/fb/71/59513f8710ed3e6b0ac0a050a5b7e977bb9c9e880354863b5d00d8809256/coverage-7.14.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9132cd363a68a4c3daa7c8704a654b1e39d3360f6f5b8ddd470608a945236c07", size = 262048, upload-time = "2026-05-26T20:41:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/84/8d/bceed32dc494f5bbf50f775cd2e78ca814953942b5ea28d3c1c3ac316f14/coverage-7.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07c6290b1697b862c0478eab545eec949a0d0e4d6d03497f446d706da3b4f2de", size = 265781, upload-time = "2026-05-26T20:41:21.559Z" }, - { url = "https://files.pythonhosted.org/packages/e7/c5/9348fe40dbfd4991aaf78df2c6c3098bfb2cc834d1fd362a64b4efef855a/coverage-7.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5ea0c297e27133853b4d8a3eb799bff5a2dbd9f2f41537a240d337ac9b4df890", size = 260896, upload-time = "2026-05-26T20:41:23.428Z" }, - { url = "https://files.pythonhosted.org/packages/ca/92/1ea0f03929da7cf87206b1fa24f4c8e9c158be0455481af29ec0a1f3503f/coverage-7.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:01b7733daad0237daa01ef80fe2dfceffc911e6a17fa7b55d14aa8214eaaaecd", size = 263214, upload-time = "2026-05-26T20:41:25.419Z" }, - { url = "https://files.pythonhosted.org/packages/f6/a9/b2493c054c0e01a643266742ab45e15744e60743f9260cd930c7142b1124/coverage-7.14.1-cp314-cp314t-win32.whl", hash = "sha256:6adc5a36984624a70bf11d7184e20fa0a49aa7c47ffab43804106a1a695ea22e", size = 223624, upload-time = "2026-05-26T20:41:27.795Z" }, - { url = "https://files.pythonhosted.org/packages/fc/bd/3e1e6a57fccd2d7c83fcdf338e93ba98eb85c6e877dd34731ac585375490/coverage-7.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ddf799247318f34dbcd2efa8c95a8d0642674e926bb1774cf9b63dfd2a389d1c", size = 224728, upload-time = "2026-05-26T20:41:30.098Z" }, - { url = "https://files.pythonhosted.org/packages/bb/d7/31066cf1d2f0c6c797fce911bcfa01dd35642dc6da992a950256097c5860/coverage-7.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:145986fe66647eb489f18d9a997567a3fd358584c4b5a808769113abc07466af", size = 222752, upload-time = "2026-05-26T20:41:32.123Z" }, - { url = "https://files.pythonhosted.org/packages/8a/3c/1a983b9a745d7f83d53f057bcc5bf79ba6a2bbc08266b3f0c7d6fe630c9b/coverage-7.14.1-py3-none-any.whl", hash = "sha256:a252f21c27e38347e60111a3266b03827422a7d5525951aceee313aa68bab1d2", size = 211815, upload-time = "2026-05-26T20:41:34.078Z" }, +version = "7.15.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/d0/55fe630f4cf94e3fcba868240fad8c8cdd1f764e2a932f8926347e6ec4cd/coverage-7.15.2.tar.gz", hash = "sha256:3df60dc267f0a2ca23cb7a9ab1109c62b9335ffbf519fcfe167157c28c09b81d", size = 927741, upload-time = "2026-07-15T18:56:19.558Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/03/060ce69008ac97bbc01b1411b3e55b61f6f015659400b46749b662107831/coverage-7.15.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9b5bd92ff1ec22e535eab0de75fa6db021992791f461a2aceb7822c625a1187d", size = 221284, upload-time = "2026-07-15T18:53:29.52Z" }, + { url = "https://files.pythonhosted.org/packages/fc/a3/d936e8b53edd9684100a6aefaf3fcabaa54728fe33324436c8d279c047aa/coverage-7.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:44826758cfe73fcd0e6af5deb4ba6d5417cc1d13df3acb35c93484a11160f846", size = 221799, upload-time = "2026-07-15T18:53:31.708Z" }, + { url = "https://files.pythonhosted.org/packages/ae/a3/ca234b06aec7ee28226f11d39a696b4481fe5eddfce8e03bf39979bb8ffb/coverage-7.15.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:09f5c6ec5901f667bd97dd140b5b9a2586b10efec66f46fb1e6d8135f8b95bdf", size = 248544, upload-time = "2026-07-15T18:53:33.212Z" }, + { url = "https://files.pythonhosted.org/packages/2b/89/dda79527bb7573ba91828b2fb91b3105d87378d6a2749ca0c0924ce0addd/coverage-7.15.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1d16e3a7104ea84f03e614611b3edbf6fb6892554b3ab0fe7fbb3f2b2ef04376", size = 250374, upload-time = "2026-07-15T18:53:34.683Z" }, + { url = "https://files.pythonhosted.org/packages/67/c6/c33755a34572f81f49a8c0cdf6b622f35ccb3238b136e1909daf0cdd4319/coverage-7.15.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d46e62cb35d91e6e2589fda6d28074426b0e276422b5d2ebef2c6b11dc60dbfd", size = 252239, upload-time = "2026-07-15T18:53:36.205Z" }, + { url = "https://files.pythonhosted.org/packages/b9/6f/dc341741b375be53a5baeee5b4bf0f0e525d38caed428f7932d23bb7bcb1/coverage-7.15.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dfd3db045e95960ae3683059571e597fda7cc610106a8916f77c5839048c1deb", size = 254150, upload-time = "2026-07-15T18:53:37.863Z" }, + { url = "https://files.pythonhosted.org/packages/e9/8d/966a18a5b195cb4e77b14c53f5f3dce22b5da05e6de7fafd1e08f2d2067a/coverage-7.15.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:affd532502d34c0472d0cdb181325c89f1d2c44992fef0c17e88e7b1576259a1", size = 249234, upload-time = "2026-07-15T18:53:39.394Z" }, + { url = "https://files.pythonhosted.org/packages/c5/8b/8b2e367496ab48484d48e79984fec76cdc1b7cb5d3a00ee799a5602e3ec9/coverage-7.15.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d17d7512151fedfcc64c1821a8977fc9be0dbf495754669afcab7b57abc98ae9", size = 250276, upload-time = "2026-07-15T18:53:41.027Z" }, + { url = "https://files.pythonhosted.org/packages/63/92/1199318a200eb6c8c6ce0192c892c8710ac791abbe0f35099294620bbfda/coverage-7.15.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e26ff680768b8095e8874aabe0e9d3a47a2a9f176a8340d05f8604c56457c23a", size = 248283, upload-time = "2026-07-15T18:53:42.557Z" }, + { url = "https://files.pythonhosted.org/packages/56/da/be284a55c5619bda891a89c27dfd59324a2c6a14d755cf6aac6960ceebeb/coverage-7.15.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7e8f27131dc7cd53de2c137dd207b3720919320b3c20d499dc30aa9ee6173287", size = 252093, upload-time = "2026-07-15T18:53:44.271Z" }, + { url = "https://files.pythonhosted.org/packages/d4/53/ee112da833ddd77b73c6d781a98029b45b584b136615b4900ed0569f887e/coverage-7.15.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:728a33676d4c3f0db977990a4bd421dcaa3be3e53b5b6273036fff6666008e89", size = 248552, upload-time = "2026-07-15T18:53:45.7Z" }, + { url = "https://files.pythonhosted.org/packages/82/6a/802cfc802e9113494c80bf3f284cd4d72faeb1f24e244f61046af364f2ca/coverage-7.15.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29c052f7c83ccfcc5c577eaae025d2e4a9bb80daf03c0ac31c996e83b000ce88", size = 249154, upload-time = "2026-07-15T18:53:47.256Z" }, + { url = "https://files.pythonhosted.org/packages/2c/65/529808e91d651147edae408fd9e894abc3b8cad7f3e594bbc36719a3e13a/coverage-7.15.2-cp310-cp310-win32.whl", hash = "sha256:1268ac8fb9ddcd783d3948dbabaf80a5d53bfdaa0575e873e2139a692f797443", size = 223334, upload-time = "2026-07-15T18:53:48.768Z" }, + { url = "https://files.pythonhosted.org/packages/68/0f/0e1829d7001130876dfbc0b4e1c737ea7c155b809e3e4a98a0aa268e2369/coverage-7.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:9f4432898c4bf2fba0435bbe35dd4437d7264565e5a88a21f5b49d8662a6b629", size = 223959, upload-time = "2026-07-15T18:53:50.429Z" }, + { url = "https://files.pythonhosted.org/packages/7d/3a/54536704f507d4573bf9161c4d0dd3dd59b6d85e48c664e901b6844d8e33/coverage-7.15.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f1ec6f304b156669cfde653b4e9a953f5de87e247ea02ac599bce0ab2744036", size = 221414, upload-time = "2026-07-15T18:53:51.941Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d9/8ba925d29743e3577b21e4d8c11a702b76bc93c41e7fdfd1177af63d4b8d/coverage-7.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d3361879d736f469f45723c11ea1a5bbdaf1f6928f0e632c940378b5aa9b660", size = 221913, upload-time = "2026-07-15T18:53:53.682Z" }, + { url = "https://files.pythonhosted.org/packages/09/54/a855f3aa0187f2b431ade4e4791b77b56282cfb5d201c83ec26a31b5b36a/coverage-7.15.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c6a98d698f9e2c8008d0370ec7fc452ebfcc530002ae2d0061170d768b992589", size = 252332, upload-time = "2026-07-15T18:53:55.467Z" }, + { url = "https://files.pythonhosted.org/packages/8e/d3/13ac97b4370640ba3452fc8559b06cc2f479ce3ba4a0b632a73e44c38a7d/coverage-7.15.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d50dd325e18ec25bfcc10cd7f99b04df1ab9ec76b0918c260e60817ad0643dee", size = 254243, upload-time = "2026-07-15T18:53:57.055Z" }, + { url = "https://files.pythonhosted.org/packages/88/83/5eca144942d8d0659d3f55176517f4a59cdc65eefd17146a0770935a3ebd/coverage-7.15.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67d7602480a47bdf5b675635403625553ebaa70d5a62a657c035149fd401cea0", size = 256352, upload-time = "2026-07-15T18:53:58.83Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ba/d3db2e01a50fc88cdb4c0f19542bcf6f61489e34dc9aa3538413e2459a38/coverage-7.15.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cee0f89f4767a6057c8fbf168f8135f18be651300496086bd873e3189fed0487", size = 258313, upload-time = "2026-07-15T18:54:00.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/b3/aba83416e9177df28e5186d856c19158c59fc0e7e814aaa61a4a2354ad1b/coverage-7.15.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a29ec5305a7335aacee2d799e3422e91e1c8a12474986e2b3b07e315c91be82f", size = 252449, upload-time = "2026-07-15T18:54:02.456Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a5/4b00ecac0194431ab451b0f6710f8e2517d04cef60f821b14dec4637d575/coverage-7.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:48ccc6395958eda89093ecdc35644c86f23a8b23a7f4d44958812b721aad67c1", size = 254043, upload-time = "2026-07-15T18:54:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/75/b6/cfa209b4313ee7f1b34da47efcd789ea51c024ad35af390e00f5a3c10a2e/coverage-7.15.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:81f382c5a94b434ec1f6da607edb904c76d7212e618cd4d1bc9f97bed4120ef5", size = 252107, upload-time = "2026-07-15T18:54:06.745Z" }, + { url = "https://files.pythonhosted.org/packages/36/67/e8cac5a6954038c98d7fe7eb9802afe7ab3ecb637bb7cc00e69b4148b56d/coverage-7.15.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bbc808daf4f5cd567af8075ecc72d21c6dfef9a254709a621a84c217c935ebc0", size = 255873, upload-time = "2026-07-15T18:54:08.48Z" }, + { url = "https://files.pythonhosted.org/packages/2c/92/395cca9f330a86c3fe3471d73e2c102116c4c58fdc619dbbc125c6e93a54/coverage-7.15.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:a4c46b247b5d4b78f613bd89fea926d32b25c6cc61a50bd1e99ba310348f3dad", size = 251826, upload-time = "2026-07-15T18:54:10.083Z" }, + { url = "https://files.pythonhosted.org/packages/51/60/3e91b20295439652424f426b7086ec5bf4fbe3f604c73eda22b986c4fd6b/coverage-7.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:094dd37f3ef7b2da8b068b583d1f4c40f91c65197e16c52a71962d5d537fc5db", size = 252735, upload-time = "2026-07-15T18:54:11.878Z" }, + { url = "https://files.pythonhosted.org/packages/a5/eb/8c07839005e5e3c6b3877d3a6e2a80ce766589f31dd2b6882b78d59a7b8c/coverage-7.15.2-cp311-cp311-win32.whl", hash = "sha256:a63b9e190711134d581c4d703df5df09851b1acf99792c7aacbbe9f41f0283c9", size = 223500, upload-time = "2026-07-15T18:54:13.525Z" }, + { url = "https://files.pythonhosted.org/packages/2e/98/59d83c257cd59f0fbaf9d9ddb26b744a576760dfd1ae16e516408894a02b/coverage-7.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:8bb9f4b4279187560796a4cdaca3b0a93dd97e48ee667df005f4ed9a97403688", size = 223973, upload-time = "2026-07-15T18:54:15.163Z" }, + { url = "https://files.pythonhosted.org/packages/ea/09/2d285c8bef5c4f695d120c1c96dc11715638aa8e134069f210bb6a62a9fe/coverage-7.15.2-cp311-cp311-win_arm64.whl", hash = "sha256:8c726b232659cbd2ae57ade46509eb068c9bd7a06df9fcbff6fe484870006934", size = 223519, upload-time = "2026-07-15T18:54:16.803Z" }, + { url = "https://files.pythonhosted.org/packages/6a/50/eb5bf42e531611a9f8d272556b1ed4de503f84a91413584094487cf69f8f/coverage-7.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1adac78e5abc7c5438f7a209c9ca69d06542f0bf481d728b6989ea80b813fdf9", size = 221587, upload-time = "2026-07-15T18:54:18.439Z" }, + { url = "https://files.pythonhosted.org/packages/06/d1/da99af464c335d4e023a6efcd7ec30f63b88a43c93745154ab74ffb31cea/coverage-7.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b868acc62aa5de3be7a9d05c2333bf8359ca987e43f9cb30ff8fbda6a024ab73", size = 221943, upload-time = "2026-07-15T18:54:20.062Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8a/13c42723d61ca447eafa18732e8141dd6a63f2732e1c7e1502c182dd88d7/coverage-7.15.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6f6966fc30e6f06ca8f98fb0ce51eda6b111b3ee8d066a8b1ec9e77fa06ab55d", size = 253450, upload-time = "2026-07-15T18:54:21.765Z" }, + { url = "https://files.pythonhosted.org/packages/d7/29/99021303f98fbdcb63504b4d07bea4cc025b9b2dd907c4f07c85d50a0dab/coverage-7.15.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:68af907f595ab01a78f794932ff3bdf929c316d3000810d38dbc247129e26f8b", size = 256187, upload-time = "2026-07-15T18:54:23.4Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a8/fd503715ed6ca9c5d742923aa5209257340b367a867b2ced0c7d4ba8a0b9/coverage-7.15.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afa29e2eff3d5729267e2cb2fd4ce9d61c952932fb2694e34ccb5d9540c6a296", size = 257301, upload-time = "2026-07-15T18:54:25.183Z" }, + { url = "https://files.pythonhosted.org/packages/da/40/3f4b8fb409810036ebc2857d36adc0498c6e957b5df0290c5036b2e143f1/coverage-7.15.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bbf44513ceb1589e31948e20eafbde9deaface90e1a1afa5f5f77b4423d17ce6", size = 259562, upload-time = "2026-07-15T18:54:27.204Z" }, + { url = "https://files.pythonhosted.org/packages/0b/8a/9bdffbef47db77cce3d6b02a28f7e919b19f0106c4b080c2c2246040f885/coverage-7.15.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9deddf09eecb717b7f980414b43d90a5b22ff3967d2949ab29cb0aa83d9e9098", size = 253841, upload-time = "2026-07-15T18:54:29.134Z" }, + { url = "https://files.pythonhosted.org/packages/1b/1e/9031efde019d31a06646261fce6dfc5c3c74e951e27a71e5c9a424563178/coverage-7.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae901f7e55ba405c84ee1cab3d3e962e4e871e4a2bcb9c90911adbd69b42ac5a", size = 255221, upload-time = "2026-07-15T18:54:31.142Z" }, + { url = "https://files.pythonhosted.org/packages/56/db/787acde872389fc84a9ef9d8cd1ccc658e391ab4cb5b28092a714426a394/coverage-7.15.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a0f47002c6eeb7c280228467a4cb0cc15ca2103a8421b986b2d3ec04a0f9bd8b", size = 253366, upload-time = "2026-07-15T18:54:32.886Z" }, + { url = "https://files.pythonhosted.org/packages/2f/9b/6f57bc4b93c842eef1695f8cdaf2318e35e7ba54f5ba80d84be213ab7858/coverage-7.15.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1cd7a5beb7af3e864a13b1f0fb26efd3695da43ef0daf71e586adfffaf34d5b2", size = 257434, upload-time = "2026-07-15T18:54:34.7Z" }, + { url = "https://files.pythonhosted.org/packages/88/26/b3186a21b2acc83e451118978905c81c7072c3333707804db09a78c096a2/coverage-7.15.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:97a5c5457a9fb1d6c4e06cfb5dc835871fbfb6a6a51addc9e925bdeff5ef7440", size = 252935, upload-time = "2026-07-15T18:54:36.548Z" }, + { url = "https://files.pythonhosted.org/packages/20/c2/c9f3376b2e717ea69ed7a6e9a5fcab968fb0b290db6cf4bd9a1fc7541b75/coverage-7.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0901cfe6c13bcd2302da4f83e884555d2a22bda6e4c476f09ef204ba20ca536e", size = 254807, upload-time = "2026-07-15T18:54:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e1/dfc15401f4a8aaeb486e1ba3e9e3c40522a6e38bd0ecf0b3f29cb8082957/coverage-7.15.2-cp312-cp312-win32.whl", hash = "sha256:b171bdd71cb7ff792bf32e376173b0ace7e7963e7e57c58dfc42063a6a7174cd", size = 223641, upload-time = "2026-07-15T18:54:40.103Z" }, + { url = "https://files.pythonhosted.org/packages/91/40/81b6d809d320cd366ec5bdf8176575e897dcb8efe7fb4b489ef9e93e4d13/coverage-7.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:582edc45c2040543fef83341be23c43024a3ab3ae0c2d8bc498a06282905ad40", size = 224172, upload-time = "2026-07-15T18:54:41.882Z" }, + { url = "https://files.pythonhosted.org/packages/ef/28/9f14ec438149f7de557f45518f09b4a7917b795cc37083aa7db482693f8c/coverage-7.15.2-cp312-cp312-win_arm64.whl", hash = "sha256:a638db90c61cd219aeee65e83a24fdaa57269a741ae0cf773309208ac862cee3", size = 223556, upload-time = "2026-07-15T18:54:43.674Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d5/f8c838e6b7282976f7c918884b792df7a0c42c5bba5d99c60ad2d221d56d/coverage-7.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1121caa19159a38b5463eaae4b1e1fde81e525b15ecc5e000cd5b1a108f743a8", size = 221606, upload-time = "2026-07-15T18:54:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/bf/37/97c926376364f66298cc44893b89cdf17b8bc406376497c4061ae4b8a8ff/coverage-7.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a300c6934e0989c327b9e8a1e110329da4641149f872bbe9f70168be66da76c1", size = 221982, upload-time = "2026-07-15T18:54:47.341Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/a36050a6e83c2135ee0776f452ca3948224befc6d7f26acecc082d0c106a/coverage-7.15.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2617f8799d268fabdeef42a7e89ac3a23e1deee9025427db2df970f99a89a578", size = 252972, upload-time = "2026-07-15T18:54:49.2Z" }, + { url = "https://files.pythonhosted.org/packages/31/d3/06b5f1daf95f0f15ab05bd75f26ba5f3c8b33d0bb72f3aaa3cf41d1bad3a/coverage-7.15.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7dc2950a2992cd676d35c20ae63522836deeb034f08874699d14068710af3dc1", size = 255569, upload-time = "2026-07-15T18:54:51.098Z" }, + { url = "https://files.pythonhosted.org/packages/81/1c/9afb3f8de2b8d36960391c48559a2e3ff96594b58099f115921549ea8d0d/coverage-7.15.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e36686f7a442185db2400b3df171aac520869faf9deb59df687d28659eda2a6", size = 256806, upload-time = "2026-07-15T18:54:53.145Z" }, + { url = "https://files.pythonhosted.org/packages/64/d8/b989f96061a5e32d82fddd1b1b9ff48a7c8f8ae7606f0e80fd9de54b1e33/coverage-7.15.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d29ca7bd67af6e12e74632d65f026eabc1364da5c254494cd914446a28a3ef7", size = 258936, upload-time = "2026-07-15T18:54:55.015Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fa/f99771f5110457c7b511c1935ca49ddf288218eaa84322e028b9334146ae/coverage-7.15.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:db9c8438057e5b0f6a22a0af99c0c1d26b57fbbdbd1be5861ddb8f897fcc3a2d", size = 253178, upload-time = "2026-07-15T18:54:57.527Z" }, + { url = "https://files.pythonhosted.org/packages/f6/96/c098a6044d119c751ceede7be91035fa8310170ec24a6523aff72f0a5793/coverage-7.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:63022c4c8dec1d0342f05c3ede99842fe3d007689acc45e86f123a1746e4a026", size = 254934, upload-time = "2026-07-15T18:54:59.41Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a2/1457b3a7a50c8d77500103b97a046db863e2f59a1cf6d2f814595f349885/coverage-7.15.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6c0be82b4d4aa5b2704e08518e2252f3e3d110164bcca826816801052e48a7aa", size = 252898, upload-time = "2026-07-15T18:55:01.338Z" }, + { url = "https://files.pythonhosted.org/packages/6c/0e/76958874c471ecfcdde0d2b2747bb2c61bdbf34a40636f4ce9db9923e643/coverage-7.15.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4510fb9cdf6bb02dfa6af0be4a534b8102d086e22e4a33f8836df663da3d660d", size = 257056, upload-time = "2026-07-15T18:55:03.243Z" }, + { url = "https://files.pythonhosted.org/packages/7c/7c/3d7c4e3bf58baa40327dc7edc2272b17cf02299366d52763db1b0ca1556a/coverage-7.15.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:42ec3d989421b174a2ab607c1539f24127ad362757b7f1c0c0d7a2993f7eb37b", size = 252718, upload-time = "2026-07-15T18:55:05.029Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b8/1cecffed9ce14fb25be9ba42d37b6bb61485c9a3ddd43cd3dde36b6087d8/coverage-7.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8f91bce78e32343af184c3b7fa28fcf5a9e2641f4b6623d392038f804939188", size = 254490, upload-time = "2026-07-15T18:55:06.889Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2c/42984561bc7f4c045dca67516a0c50ee5ef8d84352dbeb5559dc86c4823e/coverage-7.15.2-cp313-cp313-win32.whl", hash = "sha256:434e68d531858205895eb0d74b73d20b84260de426387d53c422a5acda2cf050", size = 223647, upload-time = "2026-07-15T18:55:08.941Z" }, + { url = "https://files.pythonhosted.org/packages/41/9f/39c7c9245efc583beddf89a87683574e663ed93637f3afb6cd7b88405676/coverage-7.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:26c3b04a6377fd7c09800921fa934e3a17c0020439cd59df73e73ae1d4b6a78c", size = 224190, upload-time = "2026-07-15T18:55:10.789Z" }, + { url = "https://files.pythonhosted.org/packages/c7/de/3a2883cf8a213659280ef4b403059e17a9acaeb7fc7fd4105e1226ff2e6d/coverage-7.15.2-cp313-cp313-win_arm64.whl", hash = "sha256:3ed010aa1b69cda8e827aabfca9866216c980e2dca82ab9a78c5f83689964c8b", size = 223583, upload-time = "2026-07-15T18:55:12.678Z" }, + { url = "https://files.pythonhosted.org/packages/81/5f/aed265fd7a3551a394f36dfe41868aee709b7f95db4052205b4ad1563ac3/coverage-7.15.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:40f633c5c5fc783732f6312280122e859538fa24461235597c13d803ea9a108a", size = 221650, upload-time = "2026-07-15T18:55:14.527Z" }, + { url = "https://files.pythonhosted.org/packages/6b/2c/222ba12a545189017120f8eddfc1a0bd4616b47d5d4a8d99421edb2fe4c6/coverage-7.15.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:075560438765b7a2ef43bf7aa7758661b53d889df47f062a31bda6c1ade553a2", size = 221988, upload-time = "2026-07-15T18:55:16.674Z" }, + { url = "https://files.pythonhosted.org/packages/aa/38/304b5877ab46e6c290b4292cfcf3fe28245f0e5597cad7f6acc91fc7e0a4/coverage-7.15.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:25fd15dd40a0a2c51a500d664ca29053c09c3259d998407bf982b6e114696138", size = 253029, upload-time = "2026-07-15T18:55:18.856Z" }, + { url = "https://files.pythonhosted.org/packages/6c/58/821b533b8db9e44cf1d8a97bd525149ced40dde1d0093da02cb78e715244/coverage-7.15.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b9a6367e4aff723e8ee8190836836124284e8fcd4265e307c844010cfa074f3f", size = 255536, upload-time = "2026-07-15T18:55:21.027Z" }, + { url = "https://files.pythonhosted.org/packages/f1/f2/7aa06604c389d32ea7f0a6a988359a7eafc3cd3f8e7bc2e88cd2fdf0b877/coverage-7.15.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9854ca62c152874b2060772503535be2e8f53f70b8aaa7686b094888d872f984", size = 256881, upload-time = "2026-07-15T18:55:23.125Z" }, + { url = "https://files.pythonhosted.org/packages/a2/4f/1ef342339c7916d0096bc5888cc0f653882cc7bc8f897d5cb89143287c9b/coverage-7.15.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:913b6c56e110da40e035bbd168353bf7aaa2544a5eaccea5d98a4629aac156c7", size = 259196, upload-time = "2026-07-15T18:55:25.099Z" }, + { url = "https://files.pythonhosted.org/packages/fe/f4/7ed055d7a9c5ec13b161773a115a5ccc6b0081d568c31fad830806306cc7/coverage-7.15.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aaccad4129d735a8a4d526f26929894c9a4e8ef7034566f210b176749d6906e3", size = 253036, upload-time = "2026-07-15T18:55:27.018Z" }, + { url = "https://files.pythonhosted.org/packages/14/79/ea82cca18c242a3a38b6c017da39726aa62dcb64aa635abf79b92009975c/coverage-7.15.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a164b50081fc7357331c4024ef4d17b78ba325f8380d05f5a69599a7e05257ee", size = 254887, upload-time = "2026-07-15T18:55:29.084Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ba/a136db3c0d9562b00e10b72540dbf3a33cd3bc5b95060c9308e247494623/coverage-7.15.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:bfd341ccf78128e72c094bc70cc25b3ef309c33c7c2c66ba3ed4309549e02de1", size = 252852, upload-time = "2026-07-15T18:55:31.184Z" }, + { url = "https://files.pythonhosted.org/packages/17/17/ea334246b16b7d059953fad6fdefa11e33c68efbd3fe37b1098120a1fac2/coverage-7.15.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1473b3ba8e7ee0f076117b1a72c23f579a2b9e2bb742f48a8d86ea27ca93f91a", size = 257128, upload-time = "2026-07-15T18:55:33.163Z" }, + { url = "https://files.pythonhosted.org/packages/ed/c3/074fb66d46d607855f710876b117cbda562c5ab08363528e78820449f937/coverage-7.15.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:17c432b5f73ad52ef46fb06019f6fa7c66ce381961cf0f7dfd1d3a4bd3a98145", size = 252668, upload-time = "2026-07-15T18:55:35.063Z" }, + { url = "https://files.pythonhosted.org/packages/e1/c1/f620850ada9b36435921c9a3a8057013422b1d964eb4bf37fe138724d192/coverage-7.15.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:77f0ef5011df53a4bd1b35211ab122287f8d9b8d7aa1c4553e5c2deb24b1d446", size = 254325, upload-time = "2026-07-15T18:55:37.125Z" }, + { url = "https://files.pythonhosted.org/packages/cc/31/a729ca3689404493af82ef8e6ff70bd88bdda8da89aeef6ca9b387aeb2b4/coverage-7.15.2-cp314-cp314-win32.whl", hash = "sha256:f653e5d7248c1191ec988a85c72edeab46c3ff44f90639a4ed4874ec0be90243", size = 223844, upload-time = "2026-07-15T18:55:39.078Z" }, + { url = "https://files.pythonhosted.org/packages/c6/83/5d809dc808fb1698c671f3e372259bb9158e64b7ea526fc6ab7de64de9fe/coverage-7.15.2-cp314-cp314-win_amd64.whl", hash = "sha256:9911f31aad8906abe337c271343485cf20df5e70df5d2f57f9f136e7b55f26bc", size = 224331, upload-time = "2026-07-15T18:55:41.346Z" }, + { url = "https://files.pythonhosted.org/packages/16/4e/35e488548e952795829e129995c4174df33bf432b591d1aa42c8d9e4e7ad/coverage-7.15.2-cp314-cp314-win_arm64.whl", hash = "sha256:e38def96ad59853824c97953fdcd2c320a84ba3ce99b417db78af8bb6c3db635", size = 223760, upload-time = "2026-07-15T18:55:43.518Z" }, + { url = "https://files.pythonhosted.org/packages/ed/49/dd2c86cd6374038f6e415fb5bfb86db5218553209c081384a020369dee79/coverage-7.15.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:835ec4e20b45f0a7f63ed78f94065aca00de033403df8377bfe8b9c6abc0a7be", size = 222384, upload-time = "2026-07-15T18:55:45.569Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/173ff17a1c0808e5a438f549f6f145d5ac7528f2791310b63523e3200ac7/coverage-7.15.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7466cc7ab6dc0db871d264bf99e8779f0917ee63d40730af0552f71535a6e072", size = 222647, upload-time = "2026-07-15T18:55:47.544Z" }, + { url = "https://files.pythonhosted.org/packages/84/f8/b8cba872162356fb44ac79c10309d987206a4461e32072fc29228dad7331/coverage-7.15.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e370c12133095ff18432de8c044962be85a5a96d90c6fcbce8e17e76236d2328", size = 264013, upload-time = "2026-07-15T18:55:49.768Z" }, + { url = "https://files.pythonhosted.org/packages/ee/67/a807a7586d0b8cae485308ddd55756f0806c92f8e0b411bacbf23c48edf3/coverage-7.15.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fe41909c9515c3bfdb5f02c4d1f857dba322d9a9a1178069b91eea77889df63a", size = 266135, upload-time = "2026-07-15T18:55:51.941Z" }, + { url = "https://files.pythonhosted.org/packages/ce/67/cd78771dc985f7e4ebdcc82b1a96d9a932af9e806f01f2f91a89f4c72e80/coverage-7.15.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6aa28cfb6488e5453b5b762d65f73aa586380f6693a04d58078ce228a29b06c0", size = 268555, upload-time = "2026-07-15T18:55:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/18/3e/10134cf81275188c58568f324fc74aedff32c63ca4d5bbc513a91944a6f0/coverage-7.15.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcc0aae933921d03096f53b0b03eeb702129fd406dee59f08d2efacc68681fa5", size = 269674, upload-time = "2026-07-15T18:55:56.066Z" }, + { url = "https://files.pythonhosted.org/packages/75/4a/771b77de446cba985dc414bbc5844bd21604da05dbc044286df8318a48a7/coverage-7.15.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c63387e21ab21f512c69c9756a8c7dadd322c7275edb064064433c9a09c3743", size = 263101, upload-time = "2026-07-15T18:55:58.107Z" }, + { url = "https://files.pythonhosted.org/packages/5f/b5/70a7011da15f4071943361183aefa27847f3e3aec4fd335f1cb3d3a622b1/coverage-7.15.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e55510bc98ae943cece9e667a6c0fe94c6a92913720dea34243657a17993d0c", size = 266007, upload-time = "2026-07-15T18:56:00.468Z" }, + { url = "https://files.pythonhosted.org/packages/b4/0d/f9547e804ce7ad49646ffeffac26699510efbe6c0f751b66fdc960c4e825/coverage-7.15.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2ff08701be2d1556fc78b326c80a3e8042da09352ecb3819105f8e386c8a3071", size = 263611, upload-time = "2026-07-15T18:56:02.615Z" }, + { url = "https://files.pythonhosted.org/packages/ac/59/f576a396659c0efd351f5c1544f67c3560e89c7761cabf7f65e412beeda5/coverage-7.15.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:38c9518b7103826c403a461544e3c2e77151e8676d06eaed85911a97e962584a", size = 267344, upload-time = "2026-07-15T18:56:04.622Z" }, + { url = "https://files.pythonhosted.org/packages/7c/5d/c2e4fce3579c0cb635024293f1a32bbe26df101b3e3a69f22243d1352b6c/coverage-7.15.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:dee88b1ed88587abd8c0269a1fc1f4cc77f7750d1dfde2869e2a123af420e67d", size = 262456, upload-time = "2026-07-15T18:56:06.641Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/956287d69436b66094bc4b57ac2da71e43bfd2a5524e958900b9f582fcf8/coverage-7.15.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2fbeeeecea279727f8ac16c8e1133ddfeee793e985c86ae343d6a5ce744eef8c", size = 264771, upload-time = "2026-07-15T18:56:08.795Z" }, + { url = "https://files.pythonhosted.org/packages/2c/5a/6f979530c2734c575de77cf58f5f28d51f7123a94b5030fd9156fe5f363c/coverage-7.15.2-cp314-cp314t-win32.whl", hash = "sha256:cb0fddaa6884be6aae36ced9544b5e90f7d5f03845a2853bf47a14953a4e8688", size = 224151, upload-time = "2026-07-15T18:56:10.856Z" }, + { url = "https://files.pythonhosted.org/packages/54/7e/27f6b2a74d484742f4017553e710b01e396b23d809df3e95ca0bb9a2824b/coverage-7.15.2-cp314-cp314t-win_amd64.whl", hash = "sha256:77f091ea3a9cc611cd29f433565476bc1936c084ac8eee00ea0e7e70c27e4199", size = 224981, upload-time = "2026-07-15T18:56:12.928Z" }, + { url = "https://files.pythonhosted.org/packages/b1/48/284863423aa474240f6842bd00d680da22f4e6ea2e466618ef7c9c9e69a9/coverage-7.15.2-cp314-cp314t-win_arm64.whl", hash = "sha256:6fc448c377d6eeb00a47c673494bd9bae29280ca53987e1869e67ebedfe20658", size = 224294, upload-time = "2026-07-15T18:56:15.156Z" }, + { url = "https://files.pythonhosted.org/packages/ec/82/32e3bd191d498e64f6f911ad55d14006a0861e54869d2d32452326399e65/coverage-7.15.2-py3-none-any.whl", hash = "sha256:eb6bcae8d1a9d305351ecb108232441d11c5cfe9de840a04388ba5d2db8d735c", size = 213375, upload-time = "2026-07-15T18:56:17.305Z" }, ] [package.optional-dependencies] @@ -950,6 +937,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/aa/50/a9caea39ad19c431c1a3f8a31114df65b260cdfe67786b6c7e7c040c4c44/cryptography-49.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be9fcb48a55f023493482827d4f459bd263cc20efde64f204b97c123201850c6", size = 3783731, upload-time = "2026-06-12T20:02:43.319Z" }, ] +[[package]] +name = "deepagents" +version = "0.6.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "langchain" }, + { name = "langchain-anthropic" }, + { name = "langchain-core" }, + { name = "langchain-google-genai" }, + { name = "langsmith" }, + { name = "wcmatch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/db/a6acdc72a9e90c3f07ed10de35c951734a02d4facb693bb59684ad368801/deepagents-0.6.12.tar.gz", hash = "sha256:1f281c0bc5a63132f62e2ee345c1dc593b23188da6e23016401f6879fbe54b5f", size = 211364, upload-time = "2026-06-25T17:26:52.775Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/49/af7219b3c13520fee047bb807cfaefba17f8e4584c551d946773589a4f08/deepagents-0.6.12-py3-none-any.whl", hash = "sha256:28b8fa0119ca0a689e3e18e288c4634e4046062acfc87a1cb34289d3af3a1c88", size = 236120, upload-time = "2026-06-25T17:26:51.736Z" }, +] + [[package]] name = "dependency-groups" version = "1.3.1" @@ -983,16 +987,16 @@ wheels = [ [[package]] name = "docker" -version = "7.1.0" +version = "7.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "requests" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834, upload-time = "2024-05-23T11:13:57.216Z" } +sdist = { url = "https://files.pythonhosted.org/packages/88/7f/731ff914b0255d3d065f45fd4e626d4b8c95dbcbaada049f337a6ac16410/docker-7.2.0.tar.gz", hash = "sha256:cebb93773d334f778e023a7ee352a8d6e13ab1bd3b863a4d4a59dec897df43ac", size = 118731, upload-time = "2026-07-09T14:53:46.39Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774, upload-time = "2024-05-23T11:13:55.01Z" }, + { url = "https://files.pythonhosted.org/packages/75/23/529140fe1aab80fc6992f93a706deec709140a6397439139a054e1515c45/docker-7.2.0-py3-none-any.whl", hash = "sha256:a3f45fdeb9165e2d25d9a1d02ddf3bc70fb572cf5ebbf9b58558c22caf29b71f", size = 148775, upload-time = "2026-07-09T14:53:45.224Z" }, ] [[package]] @@ -1018,7 +1022,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1036,7 +1040,7 @@ wheels = [ [[package]] name = "fastapi" -version = "0.137.1" +version = "0.139.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -1045,9 +1049,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/b1/e5b92c59d2c37817e77c1a8c2fc1f79cdcc04c68253e5406b43e3204cba7/fastapi-0.137.1.tar.gz", hash = "sha256:822360704230d9533d8d9475399613525968aa2f0b5bd2a3ccc9f18c88fd541c", size = 408293, upload-time = "2026-06-15T11:28:20.79Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/95/d3f0ae10836324a2eab98a52b61210ac609f08200bf4bb0dc8132d32f78a/fastapi-0.139.2.tar.gz", hash = "sha256:333145a6891e9b5b3cfceb69baf817e8240cde4d4588ae5a10bf56ffacb6255e", size = 423428, upload-time = "2026-07-16T15:06:17.912Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/35/380b9a5922f4340e51c309cde09e5bd32e62f02302971bee30dc15aa0624/fastapi-0.137.1-py3-none-any.whl", hash = "sha256:64f6983c59e45c4b9fdc44e57cb8035c2451ee91ea8e8ec042aca37de7cf6b69", size = 121877, upload-time = "2026-06-15T11:28:19.523Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c7/cb03251d9dfb177246a9809a76f189d21df32dbd4a845951881d11323b7f/fastapi-0.139.2-py3-none-any.whl", hash = "sha256:b9ad015a835173d59865e2f5d8296fbc2b317bf56a2ba1a5bfbdd03de2fd4b1c", size = 130234, upload-time = "2026-07-16T15:06:19.557Z" }, ] [[package]] @@ -1115,11 +1119,20 @@ wheels = [ [[package]] name = "filelock" -version = "3.29.4" +version = "3.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/2b/8b6480a70a647035334a604d0931926de4b5cd1f57835d45ad5eed2b1a1e/filelock-3.30.0.tar.gz", hash = "sha256:1774e682dbe443bd60f9609162fc596e2c80dc84ffc2957068953406d0520090", size = 174927, upload-time = "2026-07-16T03:53:58.152Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/af/9b01bcf5c91e81899bb890b87bd9077732a9b3365c098e67fe77958c39ed/filelock-3.30.0-py3-none-any.whl", hash = "sha256:40632998f0772e64183bb819f086a1b9def6be1090cf1dcb9d45f46806ef279b", size = 93131, upload-time = "2026-07-16T03:53:56.727Z" }, +] + +[[package]] +name = "filetype" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e6/dc/be6cbe99670cd6e4ad387123647cb08e0c32975e223f82551e914c5568a6/filelock-3.29.4.tar.gz", hash = "sha256:10cdb3656fc44541cdf30652a93fb10ec6b05325620eb316bd26893e4201538a", size = 63028, upload-time = "2026-06-13T16:12:00.744Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020, upload-time = "2022-11-02T17:34:04.141Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/37/a065dc3bd6e49423a6532c642ca7378d3f467b1ef44c2800c937af7f9739/filelock-3.29.4-py3-none-any.whl", hash = "sha256:dac1648087d5115554850d113e7dd8c83ab2d38e3435dde2d4f163847e57b767", size = 42757, upload-time = "2026-06-13T16:11:59.582Z" }, + { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970, upload-time = "2022-11-02T17:34:01.425Z" }, ] [[package]] @@ -1276,56 +1289,35 @@ wheels = [ [[package]] name = "fsspec" -version = "2026.4.0" +version = "2026.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d5/8d/1c51c094345df128ca4a990d633fe1a0ff28726c9e6b3c41ba65087bba1d/fsspec-2026.4.0.tar.gz", hash = "sha256:301d8ac70ae90ef3ad05dcf94d6c3754a097f9b5fe4667d2787aa359ec7df7e4", size = 312760, upload-time = "2026-04-29T20:42:38.635Z" } +sdist = { url = "https://files.pythonhosted.org/packages/10/a1/ae4e3e5003468d6391d2c77b6fa1cd73bd5d13511d81c642d7b28ac90ed4/fsspec-2026.6.0.tar.gz", hash = "sha256:f5bac145310fe30e16e1471bd6840b2d990d609e872251d7e674241822abf01a", size = 313646, upload-time = "2026-06-16T01:57:28.105Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402, upload-time = "2026-04-29T20:42:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/e5/22/4222d7ddf3da30f363edaa98e329c2bce6c65497c9cb2810931c8b2c0fbc/fsspec-2026.6.0-py3-none-any.whl", hash = "sha256:02e0b71817df9b2169dc30a16832045764def1191b43dcff5bb85bdee212d2a1", size = 203949, upload-time = "2026-06-16T01:57:26.358Z" }, ] [[package]] name = "google-adk" -version = "1.35.0" +version = "2.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiosqlite" }, - { name = "anyio" }, { name = "authlib" }, { name = "click" }, { name = "fastapi" }, - { name = "google-api-python-client" }, { name = "google-auth", extra = ["pyopenssl"] }, - { name = "google-cloud-aiplatform", extra = ["agent-engines"] }, - { name = "google-cloud-bigquery" }, - { name = "google-cloud-bigquery-storage" }, - { name = "google-cloud-bigtable" }, - { name = "google-cloud-dataplex" }, - { name = "google-cloud-discoveryengine" }, - { name = "google-cloud-pubsub" }, - { name = "google-cloud-secret-manager" }, - { name = "google-cloud-spanner" }, - { name = "google-cloud-speech" }, - { name = "google-cloud-storage" }, { name = "google-genai" }, { name = "graphviz" }, { name = "httpx" }, { name = "jsonschema" }, - { name = "mcp" }, { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-gcp-logging" }, - { name = "opentelemetry-exporter-gcp-monitoring" }, - { name = "opentelemetry-exporter-gcp-trace" }, - { name = "opentelemetry-exporter-otlp-proto-http" }, - { name = "opentelemetry-resourcedetector-gcp" }, { name = "opentelemetry-sdk" }, - { name = "pyarrow" }, + { name = "packaging" }, { name = "pydantic" }, - { name = "python-dateutil" }, { name = "python-dotenv" }, + { name = "python-multipart" }, { name = "pyyaml" }, { name = "requests" }, - { name = "sqlalchemy" }, - { name = "sqlalchemy-spanner" }, { name = "starlette" }, { name = "tenacity" }, { name = "typing-extensions" }, @@ -1334,471 +1326,35 @@ dependencies = [ { name = "watchdog" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/a7/8cba69e86af4f25b73f0bd4cbce9b0ca990a6a779cedee9a242264fca259/google_adk-1.35.0.tar.gz", hash = "sha256:c3f36447d29c1a3400ba45b344f232d857db9b18d1224517a00b267da1f51dff", size = 2432700, upload-time = "2026-06-10T05:32:34.778Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/a1/6048b1c22817859bafc1101f8ba26f704233d9acb07e715dff5fb41b9b55/google_adk-2.4.0.tar.gz", hash = "sha256:5a2996b288d591deefcb277eeeeb7da838d72056675763bfef52ad3b36975dde", size = 3566788, upload-time = "2026-07-07T19:46:14.802Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/9a/dc5192a79bea70730c9261b8ca54ee4103265a260444d3bffdd2eab47876/google_adk-1.35.0-py3-none-any.whl", hash = "sha256:f4c10f86c37e4fba157868d6884d4493bbb88a53fea00004d900dc03a3347f85", size = 2877569, upload-time = "2026-06-10T05:32:37.085Z" }, -] - -[[package]] -name = "google-api-core" -version = "2.31.0" -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/c6/22/155cadf1d49272a9cf48f3168c0f3874fa13397297e611a5ea00cd093880/google_api_core-2.31.0.tar.gz", hash = "sha256:2be84ee0f584c48e6bde1b36766e23348b361fb7e55e56135fc76ce1c397f9c2", size = 176492, upload-time = "2026-06-03T14:52:17.257Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/40/9bdbb60b03a332bd45acb8703da08bbc27d991d35286b62e42acc86d243a/google_api_core-2.31.0-py3-none-any.whl", hash = "sha256:ef79fb3784c71cbac89cbd03301ba0c8fb8ad2aa95d7f9204dd9628f7adf59ab", size = 173102, upload-time = "2026-06-03T14:51:26.729Z" }, -] - -[package.optional-dependencies] -grpc = [ - { name = "grpcio" }, - { name = "grpcio-status" }, -] - -[[package]] -name = "google-api-python-client" -version = "2.197.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core" }, - { name = "google-auth" }, - { name = "google-auth-httplib2" }, - { name = "httplib2" }, - { name = "uritemplate" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/22/09/081d66357118bd260f8f182cb1b2dd5bd32ca88e3714d7c93896cab946fc/google_api_python_client-2.197.0.tar.gz", hash = "sha256:32e03977eda4a66eafc6ae58dc9ec46426b6025636d5ef019c5703013eddd4e5", size = 14707398, upload-time = "2026-05-28T20:23:12.498Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/e5/e9cc221fd75230974d4ef45eb72d2261feca3c110d5554215d516bfe6534/google_api_python_client-2.197.0-py3-none-any.whl", hash = "sha256:0f8b89aa75768161dd4f5092d6bcb386c13236b32e0d9a938c02f71342094d14", size = 15287302, upload-time = "2026-05-28T20:23:09.683Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ab/12ec18054990ac69f37dae8327f5d8d5e04557bada0d8d725afd872d3020/google_adk-2.4.0-py3-none-any.whl", hash = "sha256:fba91f1a693e5fc2fd13dc40d625562bd52e44a7baaeecedb01811a68063d847", size = 4123277, upload-time = "2026-07-07T19:46:13.026Z" }, ] [[package]] name = "google-auth" -version = "2.54.0" +version = "2.56.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "pyasn1-modules" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/15/f6/494e18317546d7def90c957b71d68b025d24f0e22e486c2606bc57765c48/google_auth-2.54.0.tar.gz", hash = "sha256:130f6fd5e3f497fdad897a23ed9489973437edf561238c4b92a4d02c435f8af9", size = 343161, upload-time = "2026-06-12T18:03:17.606Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/66/b4ba60005743e01933e22b4f62313e063f7460458b7d8a358427b4930013/google_auth-2.56.0.tar.gz", hash = "sha256:f90fa030b569a92654b9d690665a073841df33d57487be53db583a9a0867a553", size = 364629, upload-time = "2026-07-13T19:09:57.143Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/c5/d53bddd2c0949833fcb4ea06f9d5dd1c40575a1a4214cd1021eff57ba301/google_auth-2.54.0-py3-none-any.whl", hash = "sha256:784e9837f92244141250470d47c893df50cbab485ce491aca5e9deb558ad2b48", size = 249878, upload-time = "2026-06-12T18:02:57.58Z" }, + { url = "https://files.pythonhosted.org/packages/a8/7d/cd3e187f14ce832e419e70709bfcc40cb0dc11517d5d03c9d3919bcc3101/google_auth-2.56.0-py3-none-any.whl", hash = "sha256:6e88c10217e07a92bfd01cac8ee99e32ccfb08414c3102e6c5b8d58f37a0d1e0", size = 257976, upload-time = "2026-07-13T19:09:42.685Z" }, ] [package.optional-dependencies] pyopenssl = [ - { name = "pyopenssl" }, + { name = "cryptography" }, ] requests = [ { name = "requests" }, ] -[[package]] -name = "google-auth-httplib2" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-auth" }, - { name = "httplib2" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/b3/f192c8bc7e41e0ebdbd95afcae4783417a34b6a6af62d22daf22c3fd38fc/google_auth_httplib2-0.4.0.tar.gz", hash = "sha256:d5b030a204b7a4b4d553ba9ca701b62481ee2b74419325580be70f7d85ffed35", size = 11161, upload-time = "2026-05-07T08:03:46.878Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/97/be/954c35a62b9e31de66b0a43c225c9b6bb9e0f98d6b1dc110a2308e3644f5/google_auth_httplib2-0.4.0-py3-none-any.whl", hash = "sha256:8e55cfafa3358cba85f6cad4a886138e88e158d71e7e5c9ee5936a5c1507fb91", size = 9529, upload-time = "2026-05-07T08:02:12.375Z" }, -] - -[[package]] -name = "google-cloud-aiplatform" -version = "1.157.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "docstring-parser" }, - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "google-cloud-bigquery" }, - { name = "google-cloud-resource-manager" }, - { name = "google-cloud-storage" }, - { name = "google-genai" }, - { name = "packaging" }, - { name = "proto-plus" }, - { name = "protobuf" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e0/d9/e2a5f5a8535bbc8f68729796f3fc2d68d59a72818fb44f6544edbc2592e4/google_cloud_aiplatform-1.157.0.tar.gz", hash = "sha256:ce8413ed3584c4896f7656b663214c24e91c2c89426f1c91fbd1d220ffda23af", size = 11064992, upload-time = "2026-06-10T00:19:33.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/82/3ec2ba56dc1fa71ef783348a0c519721879dbc8f1e568534e6d4b4856ccd/google_cloud_aiplatform-1.157.0-py2.py3-none-any.whl", hash = "sha256:0ca499ac5648988916fc089f9e94bd99667eefba13f6936475247f4a0bf86634", size = 9200777, upload-time = "2026-06-10T00:19:30.181Z" }, -] - -[package.optional-dependencies] -agent-engines = [ - { name = "aiohttp" }, - { name = "cloudpickle" }, - { name = "google-cloud-iam" }, - { name = "google-cloud-logging" }, - { name = "google-cloud-trace" }, - { name = "opentelemetry-exporter-gcp-logging" }, - { name = "opentelemetry-exporter-gcp-trace" }, - { name = "opentelemetry-exporter-otlp-proto-http" }, - { name = "opentelemetry-sdk" }, - { name = "packaging" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] - -[[package]] -name = "google-cloud-appengine-logging" -version = "1.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "grpcio" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7f/b9/fcafc8d2dc68975a65cdff74807547cff9b2a7b00e738d3f5ff0bd112867/google_cloud_appengine_logging-1.10.0.tar.gz", hash = "sha256:b5563e76010a36e6adf1cc489620c29ee4fb3b986b006d237e9a061eb0f0abb7", size = 17744, upload-time = "2026-06-03T14:52:40.298Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/b3/4eeb9f59c4e7e07e1f08704b6508249eea5760878810014e636026300416/google_cloud_appengine_logging-1.10.0-py3-none-any.whl", hash = "sha256:193675caaf062c41688a3e2c744b73614db82408bc7fb060353b6878d7134492", size = 18143, upload-time = "2026-06-03T14:51:55.174Z" }, -] - -[[package]] -name = "google-cloud-audit-log" -version = "0.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "googleapis-common-protos" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/46/b971191224557091cc865b47d527e61da180e33b9397904bdefdae1dcacd/google_cloud_audit_log-0.6.0.tar.gz", hash = "sha256:4dd343683c0bb31187ebef3426803f13159e950fbea3fe60a864855cfed959b8", size = 44674, upload-time = "2026-06-03T14:52:48.095Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/99/27c70286bfa3503e43f845578ed5c2ab30c0cc68e525c168286f05f9a51c/google_cloud_audit_log-0.6.0-py3-none-any.whl", hash = "sha256:8c5ecbc341ad3b3daf776981f6d7fd7ab5ff5a29c5dce3172c669b570e0f6717", size = 44853, upload-time = "2026-06-03T14:52:03.775Z" }, -] - -[[package]] -name = "google-cloud-bigquery" -version = "3.41.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "google-cloud-core" }, - { name = "google-resumable-media" }, - { name = "packaging" }, - { name = "python-dateutil" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ce/13/6515c7aab55a4a0cf708ffd309fb9af5bab54c13e32dc22c5acd6497193c/google_cloud_bigquery-3.41.0.tar.gz", hash = "sha256:2217e488b47ed576360c9b2cc07d59d883a54b83167c0ef37f915c26b01a06fe", size = 513434, upload-time = "2026-03-30T22:50:55.347Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/33/1d3902efadef9194566d499d61507e1f038454e0b55499d2d7f8ab2a4fee/google_cloud_bigquery-3.41.0-py3-none-any.whl", hash = "sha256:2a5b5a737b401cbd824a6e5eac7554100b878668d908e6548836b5d8aaa4dcaa", size = 262343, upload-time = "2026-03-30T22:48:45.444Z" }, -] - -[[package]] -name = "google-cloud-bigquery-storage" -version = "2.39.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "grpcio" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1b/85/c998751fb4182b84872df7eafcdd2f68e325c791102b65d416975c020020/google_cloud_bigquery_storage-2.39.0.tar.gz", hash = "sha256:d5afd90ad06cf24d9167316cca70ab5b344e880fc13031d7392aa78ee76b8bb6", size = 309852, upload-time = "2026-06-03T15:13:01.874Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/f6/4157466c10181907d07786fb41df5d0a9ff339c1770b9e2a15cfe483e845/google_cloud_bigquery_storage-2.39.0-py3-none-any.whl", hash = "sha256:8c192b6263804f7bdd6f57a17e763ba7f03fa4e53d7ecafca0187e0fd6467d48", size = 305958, upload-time = "2026-06-03T15:12:15.889Z" }, -] - -[[package]] -name = "google-cloud-bigtable" -version = "2.38.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "google-cloud-core" }, - { name = "google-crc32c" }, - { name = "grpc-google-iam-v1" }, - { name = "grpcio" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f7/2c/a62b2108459518914d75b8455dd69bac838d6bf276fe902320f5f16cf9cb/google_cloud_bigtable-2.38.0.tar.gz", hash = "sha256:0ad24f0106c2eb0f38e278b1641052e65882a4da0141d1f9ad78ea691724aaa3", size = 800955, upload-time = "2026-05-07T19:32:53.737Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/9d/9c0a81aa9cf6c058b02d3be194d70bcd7e4bd82f631c8110560c3908dbc4/google_cloud_bigtable-2.38.0-py3-none-any.whl", hash = "sha256:9f6a4bdbefb34d0420f41c574d9805d8a63d080d10be5a176205e3b322c122a1", size = 556168, upload-time = "2026-05-07T19:32:51.48Z" }, -] - -[[package]] -name = "google-cloud-core" -version = "2.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core" }, - { name = "google-auth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a8/dd/1eef226e470369b26824a505c34482c0b493bc35fe8e0c6b003b5feca21a/google_cloud_core-2.6.0.tar.gz", hash = "sha256:e76149739f90fac1fc6757c09f47eaccb3145b54adbd7759b0f7c4b235f46c83", size = 36001, upload-time = "2026-05-07T08:04:04.124Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/4a/98da8930ab109c73d9a5d13782a9ebb81ea8c111f6d534a567b71d23e52b/google_cloud_core-2.6.0-py3-none-any.whl", hash = "sha256:6d63ac8e5eca6d9e4319d0a1e2265fadcd7f1049904378caecfa01cf52dd869e", size = 29390, upload-time = "2026-05-07T08:02:34.672Z" }, -] - -[[package]] -name = "google-cloud-dataplex" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "grpc-google-iam-v1" }, - { name = "grpcio" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/96/41/695b333dad5c3bda1df09c0744b574d14ed1cc5f8d933863723d95476ea5/google_cloud_dataplex-2.20.0.tar.gz", hash = "sha256:cbdc55ec184a58c6d444f6d37fcc9070664a345a8e110f34dd7233ed37f92047", size = 894255, upload-time = "2026-06-03T15:28:01.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/9f/ca0ca400de2a1a1dbf264a5c7b1c67deb17ddf0e941598a90da759c97751/google_cloud_dataplex-2.20.0-py3-none-any.whl", hash = "sha256:920bbc466eea3ce0168f9fefc4a16fd33e6ddb70537588666ce8e6609f1e1553", size = 691436, upload-time = "2026-06-03T15:27:10.355Z" }, -] - -[[package]] -name = "google-cloud-discoveryengine" -version = "0.13.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8f/cd/b33bbc4b096d937abee5ebfad3908b2bdc65acd1582191aa33beaa2b70a5/google_cloud_discoveryengine-0.13.12.tar.gz", hash = "sha256:d6b9f8fadd8ad0d2f4438231c5eb7772a317e9f59cafbcbadc19b5d54c609419", size = 3582382, upload-time = "2025-09-22T16:51:14.052Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/70/607f6011648f603d35e60a16c34aee68a0b39510e4268d4859f3268684f9/google_cloud_discoveryengine-0.13.12-py3-none-any.whl", hash = "sha256:295f8c6df3fb26b90fb82c2cd6fbcf4b477661addcb19a94eea16463a5c4e041", size = 3337248, upload-time = "2025-09-22T16:50:57.375Z" }, -] - -[[package]] -name = "google-cloud-iam" -version = "2.23.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "grpc-google-iam-v1" }, - { name = "grpcio" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0d/5f/128a1462354e0f8f0b7baff34b5a1a4e5cd7aee100d8db0eb39843b43d1d/google_cloud_iam-2.23.0.tar.gz", hash = "sha256:49246f6221026d381cff4f8d804daf1bb6416153f2504bf5ef54d4af2450b828", size = 561685, upload-time = "2026-05-07T08:04:16.253Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/ee/470f0c337a235b12c6a880df25809b8b11b33986510d66450cb5ef540a83/google_cloud_iam-2.23.0-py3-none-any.whl", hash = "sha256:a123ac45080a5c1735218a6b3db4c6e6ea12a1cdc86feec1c30ad1ede6c91fc6", size = 515952, upload-time = "2026-05-07T08:02:48.144Z" }, -] - -[[package]] -name = "google-cloud-logging" -version = "3.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "google-cloud-appengine-logging" }, - { name = "google-cloud-audit-log" }, - { name = "google-cloud-core" }, - { name = "grpc-google-iam-v1" }, - { name = "grpcio" }, - { name = "opentelemetry-api" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9b/ba/e749846f13c8d1c6c01eb6317e8b09abc130fe67b5d72081a48d1bf96971/google_cloud_logging-3.16.0.tar.gz", hash = "sha256:08a3076b8f0f724219d6f73b2a242ef69d51e8bce226133aebe41a25f23f5400", size = 293703, upload-time = "2026-06-03T15:28:23.862Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/d5/91035dd77e0033dfb00d52b2bcad1e4f7408eb931981f86a1584301670a8/google_cloud_logging-3.16.0-py3-none-any.whl", hash = "sha256:9e5bfbdfe7b5315ece00e1703a2ea25fe42ca35e0b4750127b019f50d069b01b", size = 234188, upload-time = "2026-06-03T15:27:37.407Z" }, -] - -[[package]] -name = "google-cloud-monitoring" -version = "2.31.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "grpcio" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/84/9d/9522e169db3887e7f354bb9aa544a6e26c435ce19337e32432598db18c6f/google_cloud_monitoring-2.31.0.tar.gz", hash = "sha256:b4c9d3528c8643d4eb4b9d688cbb3c5914bc5f69b314ff7c5e1b47bdc073a9ae", size = 404747, upload-time = "2026-06-03T15:28:24.938Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/55/30/aa6635296da9c1c14d2e64f64e1cacd4f4debf8ab7e646c0559545f0f70d/google_cloud_monitoring-2.31.0-py3-none-any.whl", hash = "sha256:64f3d56ead48f0a0674f650cb2828c47b936582a02a27c55f2836681a86281c3", size = 391010, upload-time = "2026-06-03T15:27:39.536Z" }, -] - -[[package]] -name = "google-cloud-pubsub" -version = "2.39.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "grpc-google-iam-v1" }, - { name = "grpcio" }, - { name = "grpcio-status" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/11/2b/4bf2c17e319ff65340389565b0e1b4d72696d87802b2f5f94390fbefa73c/google_cloud_pubsub-2.39.0.tar.gz", hash = "sha256:eed65e25f57f95bf3e02d96d7ee171688b23922471f9f21b5a91ed90e1282c0f", size = 402096, upload-time = "2026-06-03T15:28:26.396Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/20/dd0b27d4ad4577c062e77ff968ca3e2d404186cd78c8a2a53a0ef5fe5389/google_cloud_pubsub-2.39.0-py3-none-any.whl", hash = "sha256:7210d691a46d7a66559696899ebe6eb731e63de29b624964b3be4dd2d12d3e19", size = 324665, upload-time = "2026-06-03T15:27:41.119Z" }, -] - -[[package]] -name = "google-cloud-resource-manager" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "grpc-google-iam-v1" }, - { name = "grpcio" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b2/1a/13060cabf553d52d151d2afc26b39561e82853380d499dd525a0d422d9f0/google_cloud_resource_manager-1.17.0.tar.gz", hash = "sha256:0f486b62e2c58ff992a3a50fa0f4a96eef7750aa6c971bb373398ccb91828660", size = 464971, upload-time = "2026-03-26T22:17:29.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/f7/661d7a9023e877a226b5683429c3662f75a29ef45cb1464cf39adb689218/google_cloud_resource_manager-1.17.0-py3-none-any.whl", hash = "sha256:e479baf4b014a57f298e01b8279e3290b032e3476d69c8e5e1427af8f82739a5", size = 404403, upload-time = "2026-03-26T22:15:26.57Z" }, -] - -[[package]] -name = "google-cloud-secret-manager" -version = "2.29.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "grpc-google-iam-v1" }, - { name = "grpcio" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d2/7c/5c88cdde9664f6c75fb68aa11e0af4309a92bef38dd38df0456ffb0f469b/google_cloud_secret_manager-2.29.0.tar.gz", hash = "sha256:ee64133af8fdb3780affb65ec6ccf10ab15a0113d8edeba388665f4be87ce1be", size = 278437, upload-time = "2026-06-03T16:13:43.149Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/c2/fc3275bc42a522757cb5141d7dae51f048b93d2f5fe4574fcee5392cef03/google_cloud_secret_manager-2.29.0-py3-none-any.whl", hash = "sha256:21bac2d0adb0bb3c13c346d7223832f197c2266534528a1bf1402774e06395a3", size = 225042, upload-time = "2026-06-03T16:12:20.162Z" }, -] - -[[package]] -name = "google-cloud-spanner" -version = "3.68.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "google-cloud-core" }, - { name = "google-cloud-monitoring" }, - { name = "grpc-google-iam-v1" }, - { name = "grpc-interceptor" }, - { name = "grpcio" }, - { name = "mmh3" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-resourcedetector-gcp" }, - { name = "opentelemetry-sdk" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "proto-plus" }, - { name = "protobuf" }, - { name = "sqlparse" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a4/2d/b857929745f57bb5b90f44970c02fdfbfb1184505ce4aa6e6c32550afb5f/google_cloud_spanner-3.68.0.tar.gz", hash = "sha256:90c55751cfc35bd58554c5715eab8be544095e21e40a805eb4d0c61a2bf07091", size = 904630, upload-time = "2026-06-12T18:03:27.665Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/f4/02ff12ebd23bb5af763b2b165deffe0dc78f933921903eb394a6ce4e0ed3/google_cloud_spanner-3.68.0-py3-none-any.whl", hash = "sha256:ad4aaf15e718fe0c54effbf510e1d9c7259f1252194c7192107848b06d8d2af8", size = 620018, upload-time = "2026-06-12T18:03:10.159Z" }, -] - -[[package]] -name = "google-cloud-speech" -version = "2.40.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "grpcio" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/c1/5dc9795314f4aefea0b01b02e9f5486a198341ecc15fe47f89a61c68df63/google_cloud_speech-2.40.0.tar.gz", hash = "sha256:e89e688e4ce0b926754038bf992d0d0f065c5f1c3503bb20e6c46d08b63658fc", size = 404366, upload-time = "2026-06-03T16:13:59.506Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/78/afeca8d597fab54bdd823f857aad15d6f9c4628ff3cb72aa237d01700721/google_cloud_speech-2.40.0-py3-none-any.whl", hash = "sha256:7cc0302b3b9ca33d2eae9669da94a44316601a240942895362ac70e765b9f39c", size = 345427, upload-time = "2026-06-03T16:12:40.909Z" }, -] - -[[package]] -name = "google-cloud-storage" -version = "3.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core" }, - { name = "google-auth" }, - { name = "google-cloud-core" }, - { name = "google-crc32c" }, - { name = "google-resumable-media" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/72/86f94e1639a8bcd9d33e8e01b49afcaa1c3a13bda7683c681717e0901e15/google_cloud_storage-3.12.0.tar.gz", hash = "sha256:03ae9847c6babb368f35f054126b8a08cbc0e3266efb990eb17b9926a45cf3be", size = 17338620, upload-time = "2026-06-12T18:03:29.215Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/bd/a89eaebd2f9db5f92ddcc8e4f23c266be1dbd11058bb83451d8dd029f34c/google_cloud_storage-3.12.0-py3-none-any.whl", hash = "sha256:3880773754ddf7c27567b04e2a4d193950b6b99429f37b9097d873686e95b09c", size = 340605, upload-time = "2026-06-12T18:03:12.677Z" }, -] - -[[package]] -name = "google-cloud-trace" -version = "1.19.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "grpcio" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/89/7b/c2a5848c4722373c92b500b65e6308ad89ca0c7c01054e0d948c58c107f2/google_cloud_trace-1.19.0.tar.gz", hash = "sha256:58293c6efcee6c74bb854ff01b008823bef66845c14f15ffa5209d545098a65d", size = 103875, upload-time = "2026-03-26T22:18:18.123Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/91/0090acafa7d2caf1bf0d7222d42935e118164a539f9f9a00a814afa63fa1/google_cloud_trace-1.19.0-py3-none-any.whl", hash = "sha256:59604c4c775c40af31b367df6bada0af34518cc35ac8cfedecd43898a120c51d", size = 108454, upload-time = "2026-03-26T22:14:32.631Z" }, -] - -[[package]] -name = "google-crc32c" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/41/4b9c02f99e4c5fb477122cd5437403b552873f014616ac1d19ac8221a58d/google_crc32c-1.8.0.tar.gz", hash = "sha256:a428e25fb7691024de47fecfbff7ff957214da51eddded0da0ae0e0f03a2cf79", size = 14192, upload-time = "2025-12-16T00:35:25.142Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/ac/6f7bc93886a823ab545948c2dd48143027b2355ad1944c7cf852b338dc91/google_crc32c-1.8.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:0470b8c3d73b5f4e3300165498e4cf25221c7eb37f1159e221d1825b6df8a7ff", size = 31296, upload-time = "2025-12-16T00:19:07.261Z" }, - { url = "https://files.pythonhosted.org/packages/f7/97/a5accde175dee985311d949cfcb1249dcbb290f5ec83c994ea733311948f/google_crc32c-1.8.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:119fcd90c57c89f30040b47c211acee231b25a45d225e3225294386f5d258288", size = 30870, upload-time = "2025-12-16T00:29:17.669Z" }, - { url = "https://files.pythonhosted.org/packages/3d/63/bec827e70b7a0d4094e7476f863c0dbd6b5f0f1f91d9c9b32b76dcdfeb4e/google_crc32c-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6f35aaffc8ccd81ba3162443fabb920e65b1f20ab1952a31b13173a67811467d", size = 33214, upload-time = "2025-12-16T00:40:19.618Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/11b70614df04c289128d782efc084b9035ef8466b3d0a8757c1b6f5cf7ac/google_crc32c-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:864abafe7d6e2c4c66395c1eb0fe12dc891879769b52a3d56499612ca93b6092", size = 33589, upload-time = "2025-12-16T00:40:20.7Z" }, - { url = "https://files.pythonhosted.org/packages/3e/00/a08a4bc24f1261cc5b0f47312d8aebfbe4b53c2e6307f1b595605eed246b/google_crc32c-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:db3fe8eaf0612fc8b20fa21a5f25bd785bc3cd5be69f8f3412b0ac2ffd49e733", size = 34437, upload-time = "2025-12-16T00:35:19.437Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ef/21ccfaab3d5078d41efe8612e0ed0bfc9ce22475de074162a91a25f7980d/google_crc32c-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:014a7e68d623e9a4222d663931febc3033c5c7c9730785727de2a81f87d5bab8", size = 31298, upload-time = "2025-12-16T00:20:32.241Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b8/f8413d3f4b676136e965e764ceedec904fe38ae8de0cdc52a12d8eb1096e/google_crc32c-1.8.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:86cfc00fe45a0ac7359e5214a1704e51a99e757d0272554874f419f79838c5f7", size = 30872, upload-time = "2025-12-16T00:33:58.785Z" }, - { url = "https://files.pythonhosted.org/packages/f6/fd/33aa4ec62b290477181c55bb1c9302c9698c58c0ce9a6ab4874abc8b0d60/google_crc32c-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:19b40d637a54cb71e0829179f6cb41835f0fbd9e8eb60552152a8b52c36cbe15", size = 33243, upload-time = "2025-12-16T00:40:21.46Z" }, - { url = "https://files.pythonhosted.org/packages/71/03/4820b3bd99c9653d1a5210cb32f9ba4da9681619b4d35b6a052432df4773/google_crc32c-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:17446feb05abddc187e5441a45971b8394ea4c1b6efd88ab0af393fd9e0a156a", size = 33608, upload-time = "2025-12-16T00:40:22.204Z" }, - { url = "https://files.pythonhosted.org/packages/7c/43/acf61476a11437bf9733fb2f70599b1ced11ec7ed9ea760fdd9a77d0c619/google_crc32c-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:71734788a88f551fbd6a97be9668a0020698e07b2bf5b3aa26a36c10cdfb27b2", size = 34439, upload-time = "2025-12-16T00:35:20.458Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5f/7307325b1198b59324c0fa9807cafb551afb65e831699f2ce211ad5c8240/google_crc32c-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:4b8286b659c1335172e39563ab0a768b8015e88e08329fa5321f774275fc3113", size = 31300, upload-time = "2025-12-16T00:21:56.723Z" }, - { url = "https://files.pythonhosted.org/packages/21/8e/58c0d5d86e2220e6a37befe7e6a94dd2f6006044b1a33edf1ff6d9f7e319/google_crc32c-1.8.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:2a3dc3318507de089c5384cc74d54318401410f82aa65b2d9cdde9d297aca7cb", size = 30867, upload-time = "2025-12-16T00:38:31.302Z" }, - { url = "https://files.pythonhosted.org/packages/ce/a9/a780cc66f86335a6019f557a8aaca8fbb970728f0efd2430d15ff1beae0e/google_crc32c-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14f87e04d613dfa218d6135e81b78272c3b904e2a7053b841481b38a7d901411", size = 33364, upload-time = "2025-12-16T00:40:22.96Z" }, - { url = "https://files.pythonhosted.org/packages/21/3f/3457ea803db0198c9aaca2dd373750972ce28a26f00544b6b85088811939/google_crc32c-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb5c869c2923d56cb0c8e6bcdd73c009c36ae39b652dbe46a05eb4ef0ad01454", size = 33740, upload-time = "2025-12-16T00:40:23.96Z" }, - { url = "https://files.pythonhosted.org/packages/df/c0/87c2073e0c72515bb8733d4eef7b21548e8d189f094b5dad20b0ecaf64f6/google_crc32c-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:3cc0c8912038065eafa603b238abf252e204accab2a704c63b9e14837a854962", size = 34437, upload-time = "2025-12-16T00:35:21.395Z" }, - { url = "https://files.pythonhosted.org/packages/d1/db/000f15b41724589b0e7bc24bc7a8967898d8d3bc8caf64c513d91ef1f6c0/google_crc32c-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:3ebb04528e83b2634857f43f9bb8ef5b2bbe7f10f140daeb01b58f972d04736b", size = 31297, upload-time = "2025-12-16T00:23:20.709Z" }, - { url = "https://files.pythonhosted.org/packages/d7/0d/8ebed0c39c53a7e838e2a486da8abb0e52de135f1b376ae2f0b160eb4c1a/google_crc32c-1.8.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:450dc98429d3e33ed2926fc99ee81001928d63460f8538f21a5d6060912a8e27", size = 30867, upload-time = "2025-12-16T00:43:14.628Z" }, - { url = "https://files.pythonhosted.org/packages/ce/42/b468aec74a0354b34c8cbf748db20d6e350a68a2b0912e128cabee49806c/google_crc32c-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3b9776774b24ba76831609ffbabce8cdf6fa2bd5e9df37b594221c7e333a81fa", size = 33344, upload-time = "2025-12-16T00:40:24.742Z" }, - { url = "https://files.pythonhosted.org/packages/1c/e8/b33784d6fc77fb5062a8a7854e43e1e618b87d5ddf610a88025e4de6226e/google_crc32c-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:89c17d53d75562edfff86679244830599ee0a48efc216200691de8b02ab6b2b8", size = 33694, upload-time = "2025-12-16T00:40:25.505Z" }, - { url = "https://files.pythonhosted.org/packages/92/b1/d3cbd4d988afb3d8e4db94ca953df429ed6db7282ed0e700d25e6c7bfc8d/google_crc32c-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:57a50a9035b75643996fbf224d6661e386c7162d1dfdab9bc4ca790947d1007f", size = 34435, upload-time = "2025-12-16T00:35:22.107Z" }, - { url = "https://files.pythonhosted.org/packages/21/88/8ecf3c2b864a490b9e7010c84fd203ec8cf3b280651106a3a74dd1b0ca72/google_crc32c-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:e6584b12cb06796d285d09e33f63309a09368b9d806a551d8036a4207ea43697", size = 31301, upload-time = "2025-12-16T00:24:48.527Z" }, - { url = "https://files.pythonhosted.org/packages/36/c6/f7ff6c11f5ca215d9f43d3629163727a272eabc356e5c9b2853df2bfe965/google_crc32c-1.8.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:f4b51844ef67d6cf2e9425983274da75f18b1597bb2c998e1c0a0e8d46f8f651", size = 30868, upload-time = "2025-12-16T00:48:12.163Z" }, - { url = "https://files.pythonhosted.org/packages/56/15/c25671c7aad70f8179d858c55a6ae8404902abe0cdcf32a29d581792b491/google_crc32c-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b0d1a7afc6e8e4635564ba8aa5c0548e3173e41b6384d7711a9123165f582de2", size = 33381, upload-time = "2025-12-16T00:40:26.268Z" }, - { url = "https://files.pythonhosted.org/packages/42/fa/f50f51260d7b0ef5d4898af122d8a7ec5a84e2984f676f746445f783705f/google_crc32c-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8b3f68782f3cbd1bce027e48768293072813469af6a61a86f6bb4977a4380f21", size = 33734, upload-time = "2025-12-16T00:40:27.028Z" }, - { url = "https://files.pythonhosted.org/packages/08/a5/7b059810934a09fb3ccb657e0843813c1fee1183d3bc2c8041800374aa2c/google_crc32c-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:d511b3153e7011a27ab6ee6bb3a5404a55b994dc1a7322c0b87b29606d9790e2", size = 34878, upload-time = "2025-12-16T00:35:23.142Z" }, - { url = "https://files.pythonhosted.org/packages/52/c5/c171e4d8c44fec1422d801a6d2e5d7ddabd733eeda505c79730ee9607f07/google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:87fa445064e7db928226b2e6f0d5304ab4cd0339e664a4e9a25029f384d9bb93", size = 28615, upload-time = "2025-12-16T00:40:29.298Z" }, - { url = "https://files.pythonhosted.org/packages/9c/97/7d75fe37a7a6ed171a2cf17117177e7aab7e6e0d115858741b41e9dd4254/google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f639065ea2042d5c034bf258a9f085eaa7af0cd250667c0635a3118e8f92c69c", size = 28800, upload-time = "2025-12-16T00:40:30.322Z" }, -] - [[package]] name = "google-genai" -version = "1.75.0" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1812,21 +1368,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/59/3ed61240ef20b3ae6ed54e82c6f8b6d1f194947bc6679679dd6cdb037594/google_genai-1.75.0.tar.gz", hash = "sha256:56bac3991b311c93f980c0a2abcd287b672146905df1fbd71c92ed633d5a07cf", size = 539039, upload-time = "2026-05-04T22:48:54.857Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/b6/552d40e96da22921eb1fead7c14b00b5b5473a20e45959488660fab35ee2/google_genai-1.75.0-py3-none-any.whl", hash = "sha256:8dc4c096e7d6288c3087f6893f582fe52468932464781edb8193bd92b9fefb2c", size = 793726, upload-time = "2026-05-04T22:48:53.033Z" }, -] - -[[package]] -name = "google-resumable-media" -version = "2.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-crc32c" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/48/f8/1ca5781d6be9cb9f73f7d40f4958c4bd1226a60598e3e39e1d6aaf838c4b/google_resumable_media-2.10.0.tar.gz", hash = "sha256:e324bc9d0fdae4c52a08ae90456edc4e71ece858399e1217ac0eb3a51d6bc6ee", size = 2164570, upload-time = "2026-06-03T16:14:26.103Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/01/e7b5f3aac89200c78318ed7643401e7f5ed3131b0cd353c07483606b1e61/google_genai-2.11.0.tar.gz", hash = "sha256:4c5e524d24b145c96be327f9a7f8f04b0fe4efee0533877795e9848afed01749", size = 622366, upload-time = "2026-07-09T17:49:43.862Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl", hash = "sha256:88152884bee37b2bf36a0ab81ad8c7fd12212c9803dd981d77c1b35b02d34e7c", size = 81533, upload-time = "2026-06-03T16:13:12.51Z" }, + { url = "https://files.pythonhosted.org/packages/93/ef/d296c23390160a8b0b1dafb36dd3cb36a39ed40c81cd27e04e6233334186/google_genai-2.11.0-py3-none-any.whl", hash = "sha256:5bc8186100e1d34d691fbe0cba392b7e04e98d286ca952323a6672d054accf95", size = 984162, upload-time = "2026-07-09T17:49:42.15Z" }, ] [[package]] @@ -1841,11 +1385,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/c8/e2645aa8ed02fd4c7a2f59d68783b65b1f3cbdfe39a6308e156509d1fee8/googleapis_common_protos-1.75.0-py3-none-any.whl", hash = "sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed", size = 300631, upload-time = "2026-05-07T08:03:30.345Z" }, ] -[package.optional-dependencies] -grpc = [ - { name = "grpcio" }, -] - [[package]] name = "graphql-core" version = "3.2.11" @@ -1864,184 +1403,74 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl", hash = "sha256:54f33de9f4f911d7e84e4191749cac8cc5653f815b06738c54db9a15ab8b1e42", size = 47300, upload-time = "2025-06-15T09:35:04.433Z" }, ] -[[package]] -name = "greenlet" -version = "3.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/6e/802acd792aebb2256fbbee8cacf2727faaeb6f240ac11008f09eae4414bc/greenlet-3.5.1.tar.gz", hash = "sha256:5a56aeb7d5d9cc4b3a735efb5095bd4b4f6f0e4f93e5ca876d0e2315137b7829", size = 197356, upload-time = "2026-05-20T15:05:03.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/21/117c8710abb7f146d804a124c07eb5964a60b90d02b72452885aecc18efa/greenlet-3.5.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:7eacb17a9d41538a2bc4912eba5ef13823c83cb69e4d141d0813debe7163187f", size = 283510, upload-time = "2026-05-20T13:12:26.475Z" }, - { url = "https://files.pythonhosted.org/packages/b9/f7/6762a56fa5f6c2295c449c6524e10ce481e381c994cc44d9d03aef0700fb/greenlet-3.5.1-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e5cc9606aa5f4e0bde0d3bd502b44f743864c3ffa5cfa1011b1e30f5aa02366f", size = 599696, upload-time = "2026-05-20T14:00:02.906Z" }, - { url = "https://files.pythonhosted.org/packages/0f/05/85a511e68ee109aff0aa00b4b497806091dd2d82ce209e49c6e801bd5d92/greenlet-3.5.1-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c3d35f87c7253b715d13d679e0783d845910144f282cb939fe1ba4ac8616269c", size = 612618, upload-time = "2026-05-20T14:05:39.202Z" }, - { url = "https://files.pythonhosted.org/packages/89/b8/8b83d18ae07c46c019617f35afd7b47aab7f9b4fbb12fc637d681e10bdd8/greenlet-3.5.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:540dae7b956209af4d70a3be35927b4055f617763771e5e84a5255bea934d2f5", size = 612947, upload-time = "2026-05-20T13:14:23.469Z" }, - { url = "https://files.pythonhosted.org/packages/5d/14/ad1f9fc9b82384c010212464a3702bd911f95dab2f1180bc6fbcfb1f958c/greenlet-3.5.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed8cdb691169715a9a492844a83246f090182247d1a5031dc78a403f68ba1e97", size = 1571425, upload-time = "2026-05-20T14:02:22.671Z" }, - { url = "https://files.pythonhosted.org/packages/46/1c/43b8203cf10f4292c9e3d270e9e5f5ade79115a0a0ca5ea6f1be5f8915a7/greenlet-3.5.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d59e840387076a51016777a9328b3f2c427c6f9208a6e958bad251be50a648d", size = 1638688, upload-time = "2026-05-20T13:14:30.026Z" }, - { url = "https://files.pythonhosted.org/packages/ac/6e/0344b1e99f58f71715456e46492101fd2daa408957b8186ade0a4b515da7/greenlet-3.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:b9152fca4a6466e114aaec745ae61cba739903a109754a9d4e1262f01e9259b1", size = 237763, upload-time = "2026-05-20T13:11:35.659Z" }, - { url = "https://files.pythonhosted.org/packages/42/3c/ff890b466eaba2b0f5e6bdfff025f8c75f41b8ffdc3dbc3d24ad261e764a/greenlet-3.5.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:73f78f9b9f0a5c06e5c946ba1e8e36f5114923b6be109ee618c54f079c3ea14f", size = 284764, upload-time = "2026-05-20T13:09:10.204Z" }, - { url = "https://files.pythonhosted.org/packages/81/0e/5e5457be3d256918f6a4756f073548a3f0190836e2cc94aa6d0d617a940b/greenlet-3.5.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0cbed8bb44e23c5b199f888f4e4ce096b45ad9f25ff74a7ad0213875e936bb2", size = 603479, upload-time = "2026-05-20T14:00:04.757Z" }, - { url = "https://files.pythonhosted.org/packages/6d/e1/f89a21d58d308298e6f275f13a1b472ed96c680b601a371b08be6a725989/greenlet-3.5.1-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a203a8bd0acb0701653d3bbb26e404854a68674139ed5cbb778830f42b09bb33", size = 615495, upload-time = "2026-05-20T14:05:40.87Z" }, - { url = "https://files.pythonhosted.org/packages/75/de/af6cef182862d2ccd6975440d21c9058a77c3f9b469abf94e322dfd2e0e3/greenlet-3.5.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a271fcd66c74615cda6a964fda3f304267a12e50a084472218a39bb0376f563", size = 614754, upload-time = "2026-05-20T13:14:24.947Z" }, - { url = "https://files.pythonhosted.org/packages/1a/c6/50e520283a9f19388a7326b05f9e8637e566003475eacaadad04f558c68d/greenlet-3.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ded7b068c7c31c1a8657d4fd42d886b3e051ae29f88b80c5ff9d502257b0f071", size = 1574097, upload-time = "2026-05-20T14:02:24.003Z" }, - { url = "https://files.pythonhosted.org/packages/21/1c/13abd1f4860d987fa5e1170a01930d6e6cd40d328de487a3c9fdaff0ffd0/greenlet-3.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0932b81d72f552ded9d810d00021b64d89f2195a91ce115b893f943b7a4ab3c", size = 1641058, upload-time = "2026-05-20T13:14:31.83Z" }, - { url = "https://files.pythonhosted.org/packages/f5/56/5f332b7705545eac2dc01b4e9254d24a793f2656d55d5cc6b94ee59d22ae/greenlet-3.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:88e300d136eac057b2397aa1cfd7328b4c87c7eb66a09c7bc6a1292234db474e", size = 238089, upload-time = "2026-05-20T13:14:03.229Z" }, - { url = "https://files.pythonhosted.org/packages/d9/a9/a3c2fa886c5b94863fb0e61b3bc14610b7aa94cf4f17f8741b11708305fc/greenlet-3.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:cc6ab7e555c8a112ad3a76e368e86e12a2754bcae1652a5602e133ec7b635523", size = 234989, upload-time = "2026-05-20T13:08:27.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/37/4549f149c9797c21b32c2683c33522af22522099de128b2406672526d005/greenlet-3.5.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:fa4f98af3a528f0c3fd592a26df7f376f93329c8f4d987f6bb979057af8bf5e2", size = 286220, upload-time = "2026-05-20T13:07:28.463Z" }, - { url = "https://files.pythonhosted.org/packages/38/ff/a4f436709716965eaab9f36ea7b906c8a927fbe32fb1372a2071d964f6b1/greenlet-3.5.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffea73584b216150eab159b6d12348fb253e68757974de1e2c40d8a318ac89ed", size = 601585, upload-time = "2026-05-20T14:00:06.141Z" }, - { url = "https://files.pythonhosted.org/packages/65/ad/54bc3fcee3ad368a61b19b67d88117f7a8c29727bf71fffdeda81fbd946e/greenlet-3.5.1-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1072b4f9edcc1e192d9283a66a3e68d6b84c561de33a83d7858beb9ba1effe10", size = 614215, upload-time = "2026-05-20T14:05:42.675Z" }, - { url = "https://files.pythonhosted.org/packages/40/69/b91cda0647df839483201545913514c2827ebea5e5ccdf931842763bc127/greenlet-3.5.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:add5217d68b31130f0beca584d7fef4878327d2e31642b66618a14eef312b63b", size = 611358, upload-time = "2026-05-20T13:14:26.37Z" }, - { url = "https://files.pythonhosted.org/packages/59/90/3cf77e080350cd02fa307bb2abf05df48f4482c240275bbd2c203ba8bb1c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a5ea42a752d47a145eae922b605cd1634665ac3d5ec1e72402d5048e8d60d207", size = 1570475, upload-time = "2026-05-20T14:02:25.29Z" }, - { url = "https://files.pythonhosted.org/packages/65/2c/18cece62045e74598c3c393f70dce4a63f56222015ba29a5d4eeb04f764c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5551170cf4f5ff5623e9af81323751979fee2c731e2287b61f73cd27257b823", size = 1635625, upload-time = "2026-05-20T13:14:34.027Z" }, - { url = "https://files.pythonhosted.org/packages/30/f5/310d104ddf41eb5a70f4c268d22508dfb0c3c8e86fec152be34d0d2ed819/greenlet-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c8bb982ad117d29478ef8f5533e97df21f1e2befd17a299257b0c96d1371c0b", size = 238791, upload-time = "2026-05-20T13:10:39.018Z" }, - { url = "https://files.pythonhosted.org/packages/62/90/ceca11f504cd23a8047a3dea31919adc48df9b626dd0c13f0d858734fdfd/greenlet-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:80eb4b04dadc4e67df3fae179a32c4706a3f495bc7f22fc8a81115d5f5512188", size = 235580, upload-time = "2026-05-20T13:08:45.056Z" }, - { url = "https://files.pythonhosted.org/packages/27/69/7f7e5372d998b81001899b1c0823c957aa413ba0f2662e65821611cc31e4/greenlet-3.5.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:51518ff74664078fc51bffcc6fc529b0df5ae58da192691cee765d45ce944a2b", size = 285060, upload-time = "2026-05-20T13:08:51.899Z" }, - { url = "https://files.pythonhosted.org/packages/b1/bf/387f9b6b865fd2ae0d0be09e0004827295a01b71be76ed350dd1e28a91a4/greenlet-3.5.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ffdb3c0bb002c99cd8f298957e046c3dbf6006b5b7cdf11a4e19194624a0a0a", size = 604370, upload-time = "2026-05-20T14:00:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/32/f5/169ce3d4e4c67291bd18f8cbe0299c9f3e45102c7f1fb3c14780c93e4532/greenlet-3.5.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7715a5a2c3378ba602c3a440558261e13a820bb53a82693aacd7b7f6d964e283", size = 616987, upload-time = "2026-05-20T14:05:44.237Z" }, - { url = "https://files.pythonhosted.org/packages/ee/e5/7f2e41d5273be07e77560d61ea4e56485b4d6c316d2a84518c62d1364061/greenlet-3.5.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc71ff466927a201b08305acac451ebe1aedfcea002f62f1f2f2ac2ac1e6a135", size = 613911, upload-time = "2026-05-20T13:14:27.539Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a4/fbdc67579b73615a1f91615e814303cc71e06128f7baaba87be79b8fb90c/greenlet-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cd443683db272ebaaca03af98c0b063ab30db70ea8a31a1559f35e3f7b744ccd", size = 1570689, upload-time = "2026-05-20T14:02:27.225Z" }, - { url = "https://files.pythonhosted.org/packages/e6/b4/77abbe35078be39718a46cd49caf16bceb35662f97a34101dca28aa98e47/greenlet-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:089fff7a6ce8d9316d1f65ebc00273a56be258c1725b32b94de90a3a979557e1", size = 1635602, upload-time = "2026-05-20T13:14:36.344Z" }, - { url = "https://files.pythonhosted.org/packages/37/f7/129f27ca700845b8ee8ca88ce7f43435a1239c2eddb7677fc938822762cf/greenlet-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:110a1ca7b49b014b097f6078272c3f4ed31af45b254de5228b79adba879f6af9", size = 238683, upload-time = "2026-05-20T13:11:50.57Z" }, - { url = "https://files.pythonhosted.org/packages/6d/5c/a485a36e87df8d8fd0632ee01511244f5156a20ed3746cc6599340326395/greenlet-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f16ba1efc0715b680a18b8123d90dad887c6112ae3555b4b5c32c149540c6b4e", size = 235499, upload-time = "2026-05-20T13:12:42.028Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cb/c62454606daf5640369c94d8a9dd540599b1bfc090e2d2180cb77f4038d2/greenlet-3.5.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8ab31c9de8651a2facdd5c5bb0011f2380dd1a7af78ce2adf4b56095294fc07", size = 285579, upload-time = "2026-05-20T13:08:56.396Z" }, - { url = "https://files.pythonhosted.org/packages/ec/71/c4270398c2eba968a6071af1dfbdcaeee6ec1c24bc8b435b8cc452700da6/greenlet-3.5.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e300185139abc337ade480c327183adf42a875ac7181bfe66d7d4efea31fbea", size = 651106, upload-time = "2026-05-20T14:00:09.448Z" }, - { url = "https://files.pythonhosted.org/packages/1a/ab/71e34b78a44ec271fb5f550c17bc46d301ddc5953890d935f270b0dcdb5a/greenlet-3.5.1-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7ffdb990dcaa0234cf9845aead5df2e3c3a8b6507d409274dd87e0d5ab05ffc2", size = 663478, upload-time = "2026-05-20T14:05:45.88Z" }, - { url = "https://files.pythonhosted.org/packages/77/96/4efd6fa5c62c85426a0c19077a586258ebc3a2a146ff2493e4312a697a22/greenlet-3.5.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f82b3597e9d83b63408affed0b48fd0f54935edac4302237b9a837be0dae33c", size = 660800, upload-time = "2026-05-20T13:14:29.129Z" }, - { url = "https://files.pythonhosted.org/packages/7a/e0/6c71401a25cac7000261304e866a2f2cc04dc74810d40e2f118aa4799495/greenlet-3.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c0141e37414c10164e702b8fb1473304221ad98f71600850c6ef7ff4880feba0", size = 1617518, upload-time = "2026-05-20T14:02:28.662Z" }, - { url = "https://files.pythonhosted.org/packages/41/26/c5c06643e8c0af9e7bf18e16cb51d0ab7625155f0392e1c9015d66d556cd/greenlet-3.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:50ae25a67bea74ea41fb14b960bc532df73eb713417b2d61892dced82fe8d3bc", size = 1681593, upload-time = "2026-05-20T13:14:39.417Z" }, - { url = "https://files.pythonhosted.org/packages/8a/bd/e11a108317485075e68af9d23039619b86b28130c3b50d227d42edece64b/greenlet-3.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:8a17c42330e261299766b75ac1ea32caa437a9453c8f65d16a13140db378ecd3", size = 239800, upload-time = "2026-05-20T13:09:30.128Z" }, - { url = "https://files.pythonhosted.org/packages/47/f8/8e8e8417b7bf28639a5a56356ef934d0375e1d0c70a57e04d7701e870ffe/greenlet-3.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:7b5f5fae05b8ac6d176a61b60c394a8cbdc2b5b91b81793066e68745cf165e54", size = 236862, upload-time = "2026-05-20T13:09:10.498Z" }, - { url = "https://files.pythonhosted.org/packages/90/12/41bf27fde4d3605d3773ae57751eda182b8be2f5398011c041173b1d9534/greenlet-3.5.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:ea8da1e900d758d078810d4255d8c6aa572181896a31ec79d779eb79c3adc9ad", size = 293637, upload-time = "2026-05-20T13:12:35.529Z" }, - { url = "https://files.pythonhosted.org/packages/44/44/ba14b23e9757707050c2f397d305bbcae62e5d7cad122f8b6baec5ae4a1f/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a19570c52a21420dcbc94e661994bc325c0b5b11304540fed514586da5dc8f2e", size = 650840, upload-time = "2026-05-20T14:00:11.079Z" }, - { url = "https://files.pythonhosted.org/packages/a8/37/5ddc2b686a6844f91abecef43411842426da2e1573f60b49ecf2547f4ae1/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3d955c89b75eeca4723d7cc14135f393cd47c32e2a6cb4a8e4c6e760a26b0986", size = 656416, upload-time = "2026-05-20T14:05:47.118Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f0/d17510297c35a2992712f0bf84de3779749999f7d3d63aa1f09db7c62dbe/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2daaaebd1a5aa88c49045b6baf9310b3263796bd88db713edf37cf53e7bb4e", size = 654397, upload-time = "2026-05-20T13:14:30.696Z" }, - { url = "https://files.pythonhosted.org/packages/37/eb/147387705bb89092645b012586e7273cb5ed3c90ef7eaf3a69173eaf0209/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bfbd69cc349e43bf3a8ae1c85548ff0718efc887615c2db16c3833d7b0b072d", size = 1614469, upload-time = "2026-05-20T14:02:30.192Z" }, - { url = "https://files.pythonhosted.org/packages/a6/4e/37ee0da7732b7aa9896f17e15579a9df34b9fcb9dd494f0adfa749af6623/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4378720dd888136c27215a0214d32a4d37c3852765d45bc37aad0623423cfd78", size = 1675115, upload-time = "2026-05-20T13:14:40.972Z" }, - { url = "https://files.pythonhosted.org/packages/57/f3/97dfcf4a6eb5077f8a672234216fb5923eb89f2cab7081cb10b2cf75b605/greenlet-3.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:45718441607f9325d948db98cbc691276059316d0358c188c246da4e1d4d23d2", size = 245246, upload-time = "2026-05-20T13:12:22.646Z" }, - { url = "https://files.pythonhosted.org/packages/5d/73/d7f72e34b582f694f4a9b248162db7b09cc458a259ba8f0c0bfa1a34ea7d/greenlet-3.5.1-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:2baee5ca02031757ffe8cc3d69f0cc0aec7065ce362622da74f32d3bcab1c541", size = 285575, upload-time = "2026-05-20T13:12:07.043Z" }, - { url = "https://files.pythonhosted.org/packages/df/59/fa9c6e87dc8ad27a95dabe2f29f372b733d05a8a67470f6c901ed9975655/greenlet-3.5.1-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b1ec3274918a81d3ea778b9e75b56b72b33f300edb6cf7f3a7fe1dae56683de", size = 656428, upload-time = "2026-05-20T14:00:12.556Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f9/e753408871eaa61dfe35e619cfc67512b036fde99893685d50eea9e07146/greenlet-3.5.1-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:111e2390ffffc47d5840b01711dd7fac07d4c09283d0283e7f3264b14e284c64", size = 667064, upload-time = "2026-05-20T14:05:48.662Z" }, - { url = "https://files.pythonhosted.org/packages/96/27/5565b5b40389f1c7753003a07e21892fda8660926787036d5bc0308b8113/greenlet-3.5.1-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e630136e905fe5ff43e86945ae41220b6d1470956a39220e708110ac48d01ea5", size = 665697, upload-time = "2026-05-20T13:14:32.943Z" }, - { url = "https://files.pythonhosted.org/packages/cf/82/e7de4178c0c2d1c9a5a3be3cc0b33e46a85b3ee4a77c071bf7ad8600e079/greenlet-3.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:975eac34b44a7077ca4d421348455b94f0f518246a7f14bc6d2fdcfe5b584368", size = 1621256, upload-time = "2026-05-20T14:02:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/00/10/f2dddcf7dacac17dfc68691809589adad06135eb28930429cf58a6467a2f/greenlet-3.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:9ab3c3a0b2ae6198e67c898dad5215a49f9ae0d0081b3c3ec59f333e39eeca26", size = 1685956, upload-time = "2026-05-20T13:14:42.55Z" }, - { url = "https://files.pythonhosted.org/packages/22/17/4a232b32133230ada52f70e9d7f5b65b0caef8772f01849bd8d149e7e4ca/greenlet-3.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:cbfc69be86e10dcfef5b1e6269d1d6926552aa89ee39e1de3353360c1b6989ab", size = 239802, upload-time = "2026-05-20T13:13:15.481Z" }, - { url = "https://files.pythonhosted.org/packages/c2/ae/4e623a7e6d4d2a5f4cb8e4c82de4169fc637942caae68d6e676b8a128ac5/greenlet-3.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:92fd6d44ac5e5a887c8a5dc4a8ba0ba908527c31c12f78c6bc7dcfe8aab279f6", size = 236853, upload-time = "2026-05-20T13:15:37.301Z" }, - { url = "https://files.pythonhosted.org/packages/7a/57/816d9cff29119da3505b3d6a5e14a8af89006ac36f47f891ff293ee05af1/greenlet-3.5.1-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:a6fdf2433a5441ef9a95464f7c3e674775da1c8c1177fff311cee1acad4626ed", size = 293877, upload-time = "2026-05-20T13:10:19.078Z" }, - { url = "https://files.pythonhosted.org/packages/23/a1/59b0a7c7d140ff1a75626680b9a9899b79a9176cab298b394968fb023295/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7546556f0d649f99f6a361098a55f761181bb2ea12ff150bb16d26092ad88244", size = 655333, upload-time = "2026-05-20T14:00:14.758Z" }, - { url = "https://files.pythonhosted.org/packages/72/1b/5efe127597625042218939d01855109f352779050768b670b52edcc16a6c/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d5ee3ea898009fa898f85f9982255d35278c477bebe185beca249cab42d4526c", size = 659443, upload-time = "2026-05-20T14:05:50.159Z" }, - { url = "https://files.pythonhosted.org/packages/6c/6d/c404246ea4d22d097a7426d0efb5b781bd7eb67715f09e79001bd552ab18/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5c81f74d204d3edd136ebfd50dce53acbb776995d721a0fe801626cfc93b8cd", size = 658356, upload-time = "2026-05-20T13:14:35.091Z" }, - { url = "https://files.pythonhosted.org/packages/51/02/f8ee37fb6d2219329f350af241c27fcf12df57e723d11f6fc6d3bacdadaa/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:2c18ef16bf6d4dd410e4dd52996888ea1497be26892fe5bbc73580aba4287b8e", size = 1619216, upload-time = "2026-05-20T14:02:33.403Z" }, - { url = "https://files.pythonhosted.org/packages/93/c5/3dc9475ace2c7a3680da12372cddd7f1ac874eb410a1ac48d3e9dab83782/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:17d86354f0ae6b61bf9be5148d0dd34e06c3cb7c602c671f79f29ac3b150e659", size = 1678427, upload-time = "2026-05-20T13:14:43.71Z" }, - { url = "https://files.pythonhosted.org/packages/df/4e/750c15c317a41ffb36f0bf40b933e3d744a7dede61889f74443ea69690cf/greenlet-3.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:e7516cf6ae6b8a582c2770a0caed47b8a48373ed732c33d69a72913ae6ac923e", size = 245225, upload-time = "2026-05-20T13:13:59.366Z" }, - { url = "https://files.pythonhosted.org/packages/4f/fd/d3baea2eeb7b617efd47e87ca06e2ec2c6118d303aa9e918e0ce16eadc10/greenlet-3.5.1-cp315-cp315t-win_arm64.whl", hash = "sha256:5028648bf2253ec4745add746129d3904121fa7fe871a76bed23c5720573ce0a", size = 239590, upload-time = "2026-05-20T13:13:37.382Z" }, -] - [[package]] name = "griffelib" -version = "2.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/82/74f4a3310cdabfbb10da554c3a672847f1ed33c6f61dd472681ce7f1fe67/griffelib-2.0.2.tar.gz", hash = "sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e", size = 166461, upload-time = "2026-03-27T11:34:51.091Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/8c/c9138d881c79aa0ea9ed83cbd58d5ca75624378b38cee225dcf5c42cc91f/griffelib-2.0.2-py3-none-any.whl", hash = "sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1", size = 142357, upload-time = "2026-03-27T11:34:46.275Z" }, -] - -[[package]] -name = "grpc-google-iam-v1" -version = "0.14.4" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "googleapis-common-protos", extra = ["grpc"] }, - { name = "grpcio" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/44/4f/d098419ad0bfc06c9ce440575f05aa22d8973b6c276e86ac7890093d3c37/grpc_google_iam_v1-0.14.4.tar.gz", hash = "sha256:392b3796947ed6334e61171d9ab06bf7eb357f554e5fc7556ad7aab6d0e17038", size = 23706, upload-time = "2026-04-01T01:57:49.813Z" } +sdist = { url = "https://files.pythonhosted.org/packages/33/e4/8d187ea29c2e30b3a09505c567513077d6117861bde1fbd997a167f262ec/griffelib-2.1.0.tar.gz", hash = "sha256:762a186d2c6fd6794d4ea20d428d597ffb857cb56b66421651cbba15bdd5e813", size = 216234, upload-time = "2026-06-19T12:05:42.278Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/22/c2dd50c09bf679bd38173656cd4402d2511e563b33bc88f90009cf50613c/grpc_google_iam_v1-0.14.4-py3-none-any.whl", hash = "sha256:412facc320fcbd94034b4df3d557662051d4d8adfa86e0ddb4dca70a3f739964", size = 32675, upload-time = "2026-04-01T01:57:47.69Z" }, -] - -[[package]] -name = "grpc-interceptor" -version = "0.15.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "grpcio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9f/28/57449d5567adf4c1d3e216aaca545913fbc21a915f2da6790d6734aac76e/grpc-interceptor-0.15.4.tar.gz", hash = "sha256:1f45c0bcb58b6f332f37c637632247c9b02bc6af0fdceb7ba7ce8d2ebbfb0926", size = 19322, upload-time = "2023-11-16T02:05:42.459Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/15/ac/8d53f230a7443401ce81791ec50a3b0e54924bf615ad287654fa4a2f5cdc/grpc_interceptor-0.15.4-py3-none-any.whl", hash = "sha256:0035f33228693ed3767ee49d937bac424318db173fef4d2d0170b3215f254d9d", size = 20848, upload-time = "2023-11-16T02:05:40.913Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d3/5268aeabf2ad82658c4e2ff3a060648d0f02f3926cb53247c0e4d0dab49e/griffelib-2.1.0-py3-none-any.whl", hash = "sha256:cc7b3d2d2865ad0b909fcc38086e3f554b5ea7acbaa7bbb7ecaa3f5dfb7d9f00", size = 142560, upload-time = "2026-06-19T12:05:38.742Z" }, ] [[package]] name = "grpcio" -version = "1.81.1" +version = "1.82.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b0/b5/1ff353970a87eda4c98251e34d2dfd214abd4982dc89119c9252a2a482d2/grpcio-1.81.1.tar.gz", hash = "sha256:6fa10a767143a5e82e8eaab53918af0cd8909a57a27f8cb2288b80a613ac671b", size = 13026582, upload-time = "2026-06-11T12:46:51.673Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/d5/f2b159d8eec08be2a855ef698f5b6f7f9fdda022e4dd9e4f5d968affd678/grpcio-1.81.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:6f9a0c9c1cc15c112d1c053064fd032b64917062292c3d70aea280e02ae10b77", size = 6086868, upload-time = "2026-06-11T12:44:19.364Z" }, - { url = "https://files.pythonhosted.org/packages/80/41/9c95232b94b219ed8b14029d9cd000e0381cafba869c451dda60af84f4ba/grpcio-1.81.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:69ef28e54fc85397f91b8c19592b8ef3d81952080366914823bd8572a2958120", size = 12062291, upload-time = "2026-06-11T12:44:27.142Z" }, - { url = "https://files.pythonhosted.org/packages/83/8b/bd9284bdd665ddf877a3e8bc2930d1bcf6ebdbae7b0da5c783dc26bd6e33/grpcio-1.81.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:15641444eca4a29358107b3dceb74c1c6305c55c822fd199b458aaea4068a7fb", size = 6635242, upload-time = "2026-06-11T12:44:30.741Z" }, - { url = "https://files.pythonhosted.org/packages/60/24/78fa025517a925f1a17da71c4ef9d5f1c6f9fa65af22dfb523c5c6317a21/grpcio-1.81.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:d4b2dddfc219f54f956ccd53cf76a1d338ffe68fc7f2849ec9c7feb9927ff692", size = 7332974, upload-time = "2026-06-11T12:44:33.72Z" }, - { url = "https://files.pythonhosted.org/packages/f7/11/402295b388dd35861007f8a26a37c2e2f284212d57bdf407c31f36043746/grpcio-1.81.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ca1cc11d82677b9662082e5478b7528e2b7db7beaa6bdff42bd62789d81be399", size = 6836597, upload-time = "2026-06-11T12:44:36.108Z" }, - { url = "https://files.pythonhosted.org/packages/4d/71/37b10fd4fd579ffade6e695c14e9df5e8cba9e2365b81c131da438b67c34/grpcio-1.81.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aa2ba7d2ad6df4d80127cea65e5b8d5e2c3adbf153ff4804452836328aca7c54", size = 7440660, upload-time = "2026-06-11T12:44:38.664Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d5/40203f828abc83d458b634666df6df13778032f178c03845ad5a93682388/grpcio-1.81.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:592b5fee597faa91cce2dd294dd7d9a1c83d76c4dbf877e33ec1adb866b2fbed", size = 8443171, upload-time = "2026-06-11T12:44:41.678Z" }, - { url = "https://files.pythonhosted.org/packages/eb/2c/0ed82ea35b5ec595e10444940c1db8c0e0ef57aa46bc8797d5ff838a219e/grpcio-1.81.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62481553b1793a27e9b9c3cf9e5bd483ef045ca72462592074b46d42b0c4d9b9", size = 7868905, upload-time = "2026-06-11T12:44:44.854Z" }, - { url = "https://files.pythonhosted.org/packages/9e/1f/dcbdc1a68a07cc2b631c3098953794f17d75f93426a019240b90ce5423d6/grpcio-1.81.1-cp310-cp310-win32.whl", hash = "sha256:bb693b1e3d9a2f3fd228e2110daf4b5aeedb36761ca1e4282f74725f6d89f611", size = 4202215, upload-time = "2026-06-11T12:44:47.165Z" }, - { url = "https://files.pythonhosted.org/packages/75/a1/d7ab9f1f42efcb7d9e6111d38be6b367737a72ea2c534e1f55c81e1b6436/grpcio-1.81.1-cp310-cp310-win_amd64.whl", hash = "sha256:88268ca418cacea64cecb0d1d600d3c6b3a8038fcba02e1e205178c5b1f47661", size = 4936582, upload-time = "2026-06-11T12:44:49.479Z" }, - { url = "https://files.pythonhosted.org/packages/52/ea/1c2fa386b718ff493225e61cfc052ef400b4d6ffc54cbe261026432624b5/grpcio-1.81.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:d71d30f2d92f67d944631c523713934fee37292469e182ebcd2c1dd8a64ce53f", size = 6093112, upload-time = "2026-06-11T12:44:52.131Z" }, - { url = "https://files.pythonhosted.org/packages/2b/18/acf45fa8bd1bc5d7b0c2fd3dc4c209379fbd5bb396b440b68a83342226b7/grpcio-1.81.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b137f4bf3ada9dc44d411478decc6ff09a79ed30b306cd2abaa98408c3588137", size = 12074277, upload-time = "2026-06-11T12:44:55.354Z" }, - { url = "https://files.pythonhosted.org/packages/48/d7/ee86a60699b7db039f772a2c4a7e4facc7138984ff42c0130933a0063884/grpcio-1.81.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a3acb384427816dd5d470f47e62137b87f74da694faa8a50147012cf40df276a", size = 6640348, upload-time = "2026-06-11T12:44:59.223Z" }, - { url = "https://files.pythonhosted.org/packages/26/ee/d2de5e47378ffc207d476c230fea3be4d2601edbce9995f4fe45535d4896/grpcio-1.81.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f9a0ebbe45c29b5e5866593c12b78bd9035f0f0f0d4bc8361680cd580d99db49", size = 7331842, upload-time = "2026-06-11T12:45:02.001Z" }, - { url = "https://files.pythonhosted.org/packages/23/d6/abeda5c2b896a0b341584fe5ac411bbf72e197a9a374c355fb90965e08d2/grpcio-1.81.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a37165cc80b1a368384b383e63a4c38116a10467ae44c904d2d7468c4470ec2", size = 6842229, upload-time = "2026-06-11T12:45:04.76Z" }, - { url = "https://files.pythonhosted.org/packages/10/1c/1f0da7d590b4aeee006826ba568d0e419ca14b23e18f901a3da3e9fba613/grpcio-1.81.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6282caffb41ec326d4cb67ca9cf53b739d1b2f975a2acb498c7418e9f7d9a416", size = 7446096, upload-time = "2026-06-11T12:45:07.499Z" }, - { url = "https://files.pythonhosted.org/packages/6a/81/5c505d508f7c887aa7982d21443a4126597c80d34b0bcf40f9cec576d7f3/grpcio-1.81.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a35009284d0d3d5c2c9601c164a911b8b4331608d98a9a66d47d97bb2f522b70", size = 8445238, upload-time = "2026-06-11T12:45:10.243Z" }, - { url = "https://files.pythonhosted.org/packages/f7/b2/524847365122ee509ca17bcc4e092198b700e94af7bfd5bb5e6dd9f3ee66/grpcio-1.81.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1b22c80559854b789a01fd89e8929b3798a156c0829b5282a8939f33ad4115ad", size = 7873989, upload-time = "2026-06-11T12:45:13.102Z" }, - { url = "https://files.pythonhosted.org/packages/18/fa/07c037c50b006909d1d13a5848774f8aa7b242f70dc03a035c64eea0e6db/grpcio-1.81.1-cp311-cp311-win32.whl", hash = "sha256:428bec0161b48d8cf583c068591bc0016d0d9cfff52462b72b3884861ea768c5", size = 4202223, upload-time = "2026-06-11T12:45:16.166Z" }, - { url = "https://files.pythonhosted.org/packages/41/ed/6bff15376920942fac6b95b9802752b837437172c9e8fc2d3170546b89cc/grpcio-1.81.1-cp311-cp311-win_amd64.whl", hash = "sha256:30e825f6848d9f18bba350ed6c75c1b02a0b5184474a31db9a32b1fa66fd8c79", size = 4941303, upload-time = "2026-06-11T12:45:18.724Z" }, - { url = "https://files.pythonhosted.org/packages/85/07/9a979c81738863a738dc23d65177056e71fbb2db817740ed870b33434e7a/grpcio-1.81.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:8b39472beafc0bdcafc4c8c73ad082ebfdb449d566897a61e7acb4fa88089115", size = 6053264, upload-time = "2026-06-11T12:45:21.017Z" }, - { url = "https://files.pythonhosted.org/packages/75/95/539706ca0d3bd40dbad583dc56fd883da941f37556b629132da5762781b9/grpcio-1.81.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:12b7524c88d4026d3dcb7b0ebe16b6714f3b4af402ddd0f0639ab064a00c87c3", size = 12052560, upload-time = "2026-06-11T12:45:23.652Z" }, - { url = "https://files.pythonhosted.org/packages/e0/44/f257b7e0bd69c93b06c6cb8ac8d1b901ccb42bedabd83c1a4c77a71f8810/grpcio-1.81.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1e123f9b37edb8375fd74130d1f69c944bbf0a7b06761ae7211154b8759e94d2", size = 6595983, upload-time = "2026-06-11T12:45:26.963Z" }, - { url = "https://files.pythonhosted.org/packages/b9/f3/19782aa04c960968bef8c5539329d8e3bbc3364e2e46d19eb5e5cc5e43b7/grpcio-1.81.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2c2e2ae6867c2966b8daccc836d54a13218e0007e9a490aeb81dd05be64d22d7", size = 7303455, upload-time = "2026-06-11T12:45:29.707Z" }, - { url = "https://files.pythonhosted.org/packages/eb/8c/dea020b6d91508cd84463917a63149ec196ee7db505d032ae43fcb3303b9/grpcio-1.81.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:766bc7c9a9c340342f4c864ccbda8e78111e4751f13b895812b9c148fb79e9d0", size = 6809167, upload-time = "2026-06-11T12:45:32.52Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c7/3030dd940408083bd32cd95d634777a71605ade4887154d93e8a89244946/grpcio-1.81.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b259a04a737cb3496be0901328eb8b7552ed8df4865d8c8f1cf1bffcfc0776a3", size = 7412536, upload-time = "2026-06-11T12:45:35.403Z" }, - { url = "https://files.pythonhosted.org/packages/e0/dd/1172a9e42b168edcafefad6115346ef619a3fc02158bb170e66ced24bcdd/grpcio-1.81.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:85b10a45b8993d195c4f3ff57025b8d1e11834909ee475c403bfa60cb4caefaf", size = 8408276, upload-time = "2026-06-11T12:45:37.78Z" }, - { url = "https://files.pythonhosted.org/packages/25/7a/71437c7f3596e5246155c515852795a85a1a8d228190212432b13b97a95d/grpcio-1.81.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8ea1936c26b99999b27479853039a7f34713f56c49375ad52b38535ec93a796c", size = 7849660, upload-time = "2026-06-11T12:45:40.627Z" }, - { url = "https://files.pythonhosted.org/packages/65/40/7debc0da45d2efebafb82da75644be347497fe4ee250514b8cd3b86ae8bf/grpcio-1.81.1-cp312-cp312-win32.whl", hash = "sha256:a185a04039df6cae8648bc8ab6d6fde7bf94f7188ecf7828e76ac52eef1e41d6", size = 4185819, upload-time = "2026-06-11T12:45:43.027Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b9/8fe3ba5ed462067774ebc1f9c7f26aa7ebcc280ddd476be107153de1339e/grpcio-1.81.1-cp312-cp312-win_amd64.whl", hash = "sha256:3ad74f8bb1a18963914c5452d289422830b39459e8776ebbcd207be1fbfb1d94", size = 4930461, upload-time = "2026-06-11T12:45:45.775Z" }, - { url = "https://files.pythonhosted.org/packages/7a/42/dcc2e4b600538ef18327c0839d56b7d3c3812337c5d710df5877dbb39b1e/grpcio-1.81.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:b10e1ff4756ed27d5a29d7fc79cfce7ef1ff56ad20025b89bac7cf79e09abbbe", size = 6054466, upload-time = "2026-06-11T12:45:48.43Z" }, - { url = "https://files.pythonhosted.org/packages/7b/4a/a36e03210183a8a7d4c80c3936acee679f4bd77d5861f369db47b2cc5f05/grpcio-1.81.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:819edbdcb42ab8598b494bcf0222684bbb7a3c772bd1b1f0be7e029a6063c28e", size = 12048795, upload-time = "2026-06-11T12:45:54.011Z" }, - { url = "https://files.pythonhosted.org/packages/b0/d5/d68e30b29098f63beab6fe501100fe82674ff142b32c672532da86a99b3a/grpcio-1.81.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c5bf2dc311127d91230cc79b92188c082634a06cf66c5234db49a43b910183b0", size = 6599094, upload-time = "2026-06-11T12:45:57.799Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b3/e837954d279754f638a11cca5dcf6b24a005efb398984cefaf7735945a54/grpcio-1.81.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e8ca6a1fcdb2943c9cbc1804a1baf3acb6071d72a471591678ded84218006e14", size = 7307182, upload-time = "2026-06-11T12:46:00.568Z" }, - { url = "https://files.pythonhosted.org/packages/0d/1e/b47957057e729adc6cdf519a47f8be2562b7140e280f1418443eb4022192/grpcio-1.81.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e64dd101d380a115cc5a0c7856788adb535f1a4e21fc543775602f8be95180ae", size = 6810962, upload-time = "2026-06-11T12:46:03.312Z" }, - { url = "https://files.pythonhosted.org/packages/40/26/569868e364e05b19ec8f969da53d230bcd89c962cd198f7c29943155c4d3/grpcio-1.81.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:98a07f9bf591e3a8919797bee1c53f026ba4acd587e5a4404c8e57c9ec36b2a5", size = 7415698, upload-time = "2026-06-11T12:46:06.005Z" }, - { url = "https://files.pythonhosted.org/packages/36/0c/5440a0582cb5653fc42a6e262eeb22700943313f8076f9dc927491b20a59/grpcio-1.81.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c261d74b1a945cf895a9d6eccd1685a8e837531beaab782da4d630a8d12deffb", size = 8407779, upload-time = "2026-06-11T12:46:08.84Z" }, - { url = "https://files.pythonhosted.org/packages/ff/aa/66fe9f39871d766987d869a03ee0842a026f499c7b1e62decb9e78a8088e/grpcio-1.81.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:58ad1131c300d3c9b933802b3cc4dc69d380822935ba50b28703156ea826fbf7", size = 7844521, upload-time = "2026-06-11T12:46:12.171Z" }, - { url = "https://files.pythonhosted.org/packages/f0/9e/69bb7194861bcd28fb3193261d4f9c3831b4446993f002cf59068943e7ab/grpcio-1.81.1-cp313-cp313-win32.whl", hash = "sha256:78e29211f26da2fdd0e9c6d2b79f489476140cf7029b6a64808ade7ca4156a42", size = 4182786, upload-time = "2026-06-11T12:46:15.192Z" }, - { url = "https://files.pythonhosted.org/packages/0d/20/3da8bb0d637feccdc3e1e419bb511ce93651ce7d54164f95de22cc0b8b34/grpcio-1.81.1-cp313-cp313-win_amd64.whl", hash = "sha256:edb59506291b647a30884b1d51a599d605f40b20af4a7dc3d33786a47a31de60", size = 4928648, upload-time = "2026-06-11T12:46:17.823Z" }, - { url = "https://files.pythonhosted.org/packages/b6/58/19414622b1bf6981bc9c05a365bd548e71876c89000083b3af489251e9c0/grpcio-1.81.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:506f48f2f9c29b143fca3dad7b0d518c188b6c9648c75a2ae6e2d9f2c13a060b", size = 6055336, upload-time = "2026-06-11T12:46:20.557Z" }, - { url = "https://files.pythonhosted.org/packages/32/f1/2ec88adb92b0eba970dd0e0e7dd086341daa3c75eba4f735f9e44bf684b0/grpcio-1.81.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d865db4a6318e1c1bea83292e0ed231090538fc4ca45425b0f0480eb338bbc6e", size = 12056279, upload-time = "2026-06-11T12:46:24.255Z" }, - { url = "https://files.pythonhosted.org/packages/41/36/e8c5f8c6ec71de73733695ebc809e98b178b534ec6d8eaa31a7ebab4ad4c/grpcio-1.81.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e2aa72e3ce1770317ef534f63d397b55e130725f5149bd36077c3b539019db27", size = 6608225, upload-time = "2026-06-11T12:46:27.601Z" }, - { url = "https://files.pythonhosted.org/packages/30/22/96fc577a845ab093326d9ab1adb874bd4936c8cf98ac8ed2f3db13a0a2fb/grpcio-1.81.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0490c30c261eded63f3f354979f9dc4502a9fb944cccb60cd9dc85f5a7349854", size = 7306576, upload-time = "2026-06-11T12:46:30.514Z" }, - { url = "https://files.pythonhosted.org/packages/76/7b/61dab5d5969f28d97fb1009cead1df0a5cd987d3315e1b37f18a4449f8bc/grpcio-1.81.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:410482da976329fe5f4067270401b12cf2bd552ff8020f054ecfaddb5475f9d6", size = 6812165, upload-time = "2026-06-11T12:46:33.699Z" }, - { url = "https://files.pythonhosted.org/packages/82/78/6e501929d4f5f96462fd82fd9f0f06e5f9612207582b862868d68757b27d/grpcio-1.81.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e3657301562ac3cb8018d30d0d3ebfa39932239f7b5703422057ef14b69949f5", size = 7422962, upload-time = "2026-06-11T12:46:36.511Z" }, - { url = "https://files.pythonhosted.org/packages/2a/7e/f2157589e66daa78ebb3165942d05a08bdea93b9d11c2bc1e172aef89685/grpcio-1.81.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:24c8e57504c8f45b237e40b99262d181071e5099a07053695b75d97bb53053a0", size = 8408176, upload-time = "2026-06-11T12:46:39.803Z" }, - { url = "https://files.pythonhosted.org/packages/da/df/c6717fef716e00d235ffb96123baf6dce76d6004f6233fa767c502861460/grpcio-1.81.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b427c19380991a4eaab2f6144b64b99b412043314c6bf4ab544f97bb31ee4190", size = 7846681, upload-time = "2026-06-11T12:46:43.013Z" }, - { url = "https://files.pythonhosted.org/packages/36/84/3502e9f210a6a5c4438c8aca3f88edd2e04f6a27f3d41b26cf0a0024b096/grpcio-1.81.1-cp314-cp314-win32.whl", hash = "sha256:61233fe8951e5c85dff81c2458b6528624760166946b5b47ea150a589168411f", size = 4264615, upload-time = "2026-06-11T12:46:45.741Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b0/4af731ff7492c68a96e4c71bfd0f4590acde92b31c6fe4894e6465c10ff6/grpcio-1.81.1-cp314-cp314-win_amd64.whl", hash = "sha256:3768a5ff1b2125e6f552e561b6b2dca0e64982d8949689b4df145cf8b98d7821", size = 5070275, upload-time = "2026-06-11T12:46:48.486Z" }, -] - -[[package]] -name = "grpcio-status" -version = "1.81.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "googleapis-common-protos" }, - { name = "grpcio" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/32/26/0aa9168c87882381fd810d140c279a2490ed6aee655f0515d6f56c5ca404/grpcio_status-1.81.1.tar.gz", hash = "sha256:9389a03e746017b10f0630c064289201458f3ce01f5d7ef4b0bebc1ef6cf82ad", size = 13923, upload-time = "2026-06-11T12:58:48.636Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/5e/5abfec5f7e89d3b7993d57cfb025ca5f968a2c18656d7fcda2b6919440b9/grpcio_status-1.81.1-py3-none-any.whl", hash = "sha256:08072fa9995f4a95c647fc6f4f85e2411573d00087bcabdf30f260114338f232", size = 14638, upload-time = "2026-06-11T12:58:31.982Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/90/bc/656b89387d6f4ed7e0686c7b64c2ae7e554a759aa58122c8e5fb99392c32/grpcio-1.82.1.tar.gz", hash = "sha256:707b24abd90fcb1e45bcc080577da1dbf9971d107490589b9539af8e1e77b4b5", size = 13187300, upload-time = "2026-07-08T12:36:16.588Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/14/5d05bfd85c101cbe44a12d7c1cea9c40698e0438cddf3a70019f735b5a27/grpcio-1.82.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:91859d1cac5f47caec5fc40e9f827500cdb54ce5b36450dc9a65616b5af49c17", size = 6177087, upload-time = "2026-07-08T12:34:06.825Z" }, + { url = "https://files.pythonhosted.org/packages/19/2e/c906f8e6d0b54c0137885fff6f7b5883c6bbc381b44a0ba5ea07d7d1579b/grpcio-1.82.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:c80c9741dcef192f669876a81957cf7713b441c2f0c43631350d75fa49321d31", size = 11960907, upload-time = "2026-07-08T12:34:10.583Z" }, + { url = "https://files.pythonhosted.org/packages/de/be/ec4aa76cdf25539b9e960cbb9d5739f892ea6cde58078b5293860c1159d3/grpcio-1.82.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b89cff456796d2f0581783726ad017a2c70aff2d27b0f05504c34e2e417f7560", size = 6754802, upload-time = "2026-07-08T12:34:13.082Z" }, + { url = "https://files.pythonhosted.org/packages/e6/dd/47519c2a8fd9db47ec4493f44bd9f5b0175307e07089b1132e54b7b5b19c/grpcio-1.82.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:d6e8a08f7038ba7a77f71e250804e4aba84fe91d22cfc54ff43c07b7529c4728", size = 7484535, upload-time = "2026-07-08T12:34:15.164Z" }, + { url = "https://files.pythonhosted.org/packages/63/99/659711e9689c4dd553bcd4eacff9cb9f458f34b60edf7afb3bbc1b0a58a2/grpcio-1.82.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:50fd2fe83426b1b1c6cdc4d72d555223b7dddf8ce07c5bac218b13fc6d684c6f", size = 6919066, upload-time = "2026-07-08T12:34:17.367Z" }, + { url = "https://files.pythonhosted.org/packages/29/39/f2b772356b4f593ffe439795509fcbf675b0ff98211ae8ce2a180f2e559f/grpcio-1.82.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b758540a24d5394a9c578bf9f6126389f474b106ac3d9df1d53de56cb14c9fd9", size = 7525855, upload-time = "2026-07-08T12:34:19.479Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/b28cfffb989a84d8272593498bddd2d68148cce1813ad55189c469b0f1f8/grpcio-1.82.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c4ba4aac238f685743575d9d700003ac16537cce26e7c774993134f530652464", size = 8565122, upload-time = "2026-07-08T12:34:21.951Z" }, + { url = "https://files.pythonhosted.org/packages/97/f9/54956cb0c701190cbc9d7e535c3f84acf0285c6b9ed198a902766e17c3cd/grpcio-1.82.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ed6fc621d6f366c88a60f0b971d5afd21d441d9aa561ee688de5b7acdb2cf901", size = 7933872, upload-time = "2026-07-08T12:34:24.539Z" }, + { url = "https://files.pythonhosted.org/packages/76/85/5f9cd1f965bbe4329556a212f178ae0c072b18b446cae05ed32fa8847c53/grpcio-1.82.1-cp310-cp310-win32.whl", hash = "sha256:bd2f45e46fff5b91c10997d0743a987517a7dde67c64c592835c2dcaac66f587", size = 4257373, upload-time = "2026-07-08T12:34:26.566Z" }, + { url = "https://files.pythonhosted.org/packages/93/b0/c4f42f7c69c53d27ed41643421b55908bcbe885b68f5a208135c72917c98/grpcio-1.82.1-cp310-cp310-win_amd64.whl", hash = "sha256:5e171d5f0d6a0af78ea7512783f170a44f80c165259d8773e3a354a7f991f2b5", size = 5006571, upload-time = "2026-07-08T12:34:28.778Z" }, + { url = "https://files.pythonhosted.org/packages/26/5b/e5092af97fa671ca279b3e373251af4bf87d5fbda7dc85f6a616899562a7/grpcio-1.82.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:0ddb18a9a9e1f46692b3567ae4abb3f8d117ce6afea48650f8eca06d8ab5d06f", size = 6181472, upload-time = "2026-07-08T12:34:31.009Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8f/18053a3a2ca03d0c2a1b8cc7271e705007a16aa5dae84bac00935c5b1a7f/grpcio-1.82.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:cf855b1af246720f567b0ce5d0724d45dfa4188eecc3296a2a69257b11b9e94b", size = 11970995, upload-time = "2026-07-08T12:34:33.603Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/21b1acb052876ad00959ec4d1b05fe08607d650bcfa282073bb164c2703c/grpcio-1.82.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddb30cb13e25bc13cea70ffc69d6d90c49d36ea6c1d4549e6912f70177834cac", size = 6760127, upload-time = "2026-07-08T12:34:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/3e/12/25eef9c245c54f0061317d13a302357fe8ea03bac240b2b02ececcf54da4/grpcio-1.82.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1e822b2774f719c017cbe700b6e47173b6ae290fb84906f52a5a3c2c60b62e1e", size = 7484377, upload-time = "2026-07-08T12:34:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/a0/41/1a348767eb9d9bd7765dc4fa8a01723d3bb386d67f981ee5c6f9c02b8b1c/grpcio-1.82.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5dafb1ece8ed45dee7c738f166ec82e19673221ed5ab8967f72858a4685345b2", size = 6924269, upload-time = "2026-07-08T12:34:40.583Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b9/3aae7a03d34c86ea27988db859a6087c186f6c3f53f9b551e07afd989bfa/grpcio-1.82.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e06503106e7271e0a49fd5a1ac04747f1e47e87d900476db6fe45bc87ee411f4", size = 7531848, upload-time = "2026-07-08T12:34:43.277Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/3c4afa625d0dac9090707966916284c035fc5b2fb3e2c51e156accee6735/grpcio-1.82.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ff99bc8cafb6a952201c37b995f425e641c93ffa6e072258525feab57290141d", size = 8568217, upload-time = "2026-07-08T12:34:45.502Z" }, + { url = "https://files.pythonhosted.org/packages/20/9c/d8489c628e73e20a3d034e7f66912de7b1acb405f01d388f056a88e47924/grpcio-1.82.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:644ae1b94266ac785330f4590a69e52b6a7eb73029043a02209db81c81397d69", size = 7938771, upload-time = "2026-07-08T12:34:48.323Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b7/0a92cfd1658f3a896d4aa12d4efeb7dd4ddfc723725ae22741a5241ea710/grpcio-1.82.1-cp311-cp311-win32.whl", hash = "sha256:e203d2e19d471630084a16c815616f8211dff21c268ab3c5f5bf38417832e074", size = 4256432, upload-time = "2026-07-08T12:34:50.432Z" }, + { url = "https://files.pythonhosted.org/packages/c7/6a/2872c761b025d9ec74386f22a4a7d59c5a5b00ebf718761b33739ffc45de/grpcio-1.82.1-cp311-cp311-win_amd64.whl", hash = "sha256:0d8299c285fe6cc6a1f56badf8d3bc5078c8d20273ee64bafa3783b4bc29a769", size = 5009633, upload-time = "2026-07-08T12:34:52.67Z" }, + { url = "https://files.pythonhosted.org/packages/dc/88/d1350bf3343a2ed87d801584e40609f6c6bd3087926eeca03de50348cf4a/grpcio-1.82.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:c09bd5fa0d5b1fbd773ec349fe61441c3e4ebf168c229aa7538a820bdfad6a58", size = 6144689, upload-time = "2026-07-08T12:34:55.567Z" }, + { url = "https://files.pythonhosted.org/packages/e6/33/71875cdecd27c24ac1385d4783a09853f01b84a825a36aec2a2bc7d0d080/grpcio-1.82.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:1eae24810720734598e3e6a1a528d5de0f265fe3fc86575e9ecce424b9ec7379", size = 11952034, upload-time = "2026-07-08T12:34:58.128Z" }, + { url = "https://files.pythonhosted.org/packages/82/b2/d9125df3d8a140dec12cc82c05b7deafedeababcff6496f28b2fd5634d10/grpcio-1.82.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a6bd5daf5bde7b24d7ad2cbaf8bf9eac620d96222016bb5e7ddde930dec0673f", size = 6710772, upload-time = "2026-07-08T12:35:01.33Z" }, + { url = "https://files.pythonhosted.org/packages/88/9b/69e2d1627398b964f34437dc476a5aff5a2cc8e7f247d26272b5674b5faf/grpcio-1.82.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1ecfde669cb687ac020d31ff76debe5dc7a62213335f02262eb6625628da1c03", size = 7450677, upload-time = "2026-07-08T12:35:03.926Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e7/8f855ca29c294956122a2a73023655b9b02602d5111dad2b9b00e7631c68/grpcio-1.82.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:011c8badee95734dee8bf05ce3464756a0ac3ebb8d443afd20c0e2b5e4640ad9", size = 6886855, upload-time = "2026-07-08T12:35:06.174Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f0/fa87e85f49925f44c479d07e58b051e69bcfef6b6d5fbc6749d140f6730a/grpcio-1.82.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b85f4564926fb23114d239392bdcae200db1e6179629edd7d7ab0ab89c96a197", size = 7501323, upload-time = "2026-07-08T12:35:08.49Z" }, + { url = "https://files.pythonhosted.org/packages/67/55/2e0b10ae1d3ef9dcc480b91dc2158f4931fc4675d3af0a2836e39b2a744f/grpcio-1.82.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2c0c8270833395644c3fe6b6a806397955a2bc0538000a19a78b90c05a6c16e0", size = 8536899, upload-time = "2026-07-08T12:35:10.975Z" }, + { url = "https://files.pythonhosted.org/packages/bd/95/a3d8b0431fa221efc51ee39d73595ede74ba82a43b7c4313192e580face2/grpcio-1.82.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2ba199205ff46c7778290fe1673c91ac8e7e45678dd5c86e9e56fa33ec8788f6", size = 7913892, upload-time = "2026-07-08T12:35:13.944Z" }, + { url = "https://files.pythonhosted.org/packages/b8/92/f2651ec704d9852a56faef394775038afba435b50ce82ab2404d119c3355/grpcio-1.82.1-cp312-cp312-win32.whl", hash = "sha256:06127691866e295c14e84a1fb86356dd962254f6abd0da4ca4b001eea9e89438", size = 4240985, upload-time = "2026-07-08T12:35:16.048Z" }, + { url = "https://files.pythonhosted.org/packages/96/4f/a5fe8bf0d0a1b24855f370293075c931f27de4eb55f0f158786095bf3c11/grpcio-1.82.1-cp312-cp312-win_amd64.whl", hash = "sha256:1fa3223a3a2e1db74f4c2b255189eb7ea875dfba56e221d252ee3fc7b204778e", size = 5001580, upload-time = "2026-07-08T12:35:18.689Z" }, + { url = "https://files.pythonhosted.org/packages/1b/3e/496992d08c0aaa11272eb6228dc8ab947da01fe835de243cd00521bce4c4/grpcio-1.82.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:b454a2d97bfab7565683a02345f86bd182ab69fd7c2bdb7414171e7538f266b1", size = 6146068, upload-time = "2026-07-08T12:35:21.365Z" }, + { url = "https://files.pythonhosted.org/packages/e7/8f/f263d6f14fdba6b56cfadd91fd3e158a52682b72c6016d1f8723d435659f/grpcio-1.82.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:3dde70abfc80b3be11de53ba0d601c439e7fb2afd3583ad1788d1146bec92fdc", size = 11948600, upload-time = "2026-07-08T12:35:24.312Z" }, + { url = "https://files.pythonhosted.org/packages/8c/14/3a02e6ee49c2d85bc15eaae321e0e11ab3542cad3c5b2de121ecce0c4296/grpcio-1.82.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5523099c98c292ea1ae08e617249db760c56a78f8deae879027fe7d1ffbcbf6", size = 6714591, upload-time = "2026-07-08T12:35:27.027Z" }, + { url = "https://files.pythonhosted.org/packages/69/80/58e3738696f48ab7645347b98d8a7f93d10e00e6218388fbfcd6c9310e3d/grpcio-1.82.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:5e5c4dc0a59b0f8490a6bdfd6fc8395b9d8ad8a8407c7d67ca7b5bba15c0877f", size = 7454995, upload-time = "2026-07-08T12:35:29.599Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6c/2557c1a889363072fbf2285ecd0e8c44860d4dbd60f017a32537c5b863e2/grpcio-1.82.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c40d94ba820329cc191981bc22fa6f6eed0799c6d921f3c6709521d59d4a2fd7", size = 6888621, upload-time = "2026-07-08T12:35:32.38Z" }, + { url = "https://files.pythonhosted.org/packages/d2/66/907706ccaff1223f1e10fd5b37fc16faead43392fccb4e786e7e390ac141/grpcio-1.82.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4c816180e31e273caaec6f8bd86a8392499d5bbb26f41da44e3dce48bde69095", size = 7505069, upload-time = "2026-07-08T12:35:35.072Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7c/ff97b0d0f635987ee5ec80dfedafa1aad629303745d48e8637d10eec5b80/grpcio-1.82.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e31fd780b261830720cb70b0fd8f0aa51d49e75a66d7464ad2e31d4b765f2580", size = 8535384, upload-time = "2026-07-08T12:35:37.954Z" }, + { url = "https://files.pythonhosted.org/packages/62/9e/a97fddd970a8d1588cade06eca20443761c1858b0ad6590a5c835aa18062/grpcio-1.82.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d76152d7c31d7210d4a106e5d8b64da5bba5d6abf11be30e2f7b0a0c59bbcbf", size = 7910707, upload-time = "2026-07-08T12:35:40.797Z" }, + { url = "https://files.pythonhosted.org/packages/20/e4/eaba1517888af483a88d449eb7566f0f7f63446d46f339c5891798435875/grpcio-1.82.1-cp313-cp313-win32.whl", hash = "sha256:38e9dcb5258226fb3282630b31b16a968df52c8c6ad514af540646e0a4578f8a", size = 4240363, upload-time = "2026-07-08T12:35:43.298Z" }, + { url = "https://files.pythonhosted.org/packages/b0/42/66a98d47732e35290bef722f6149fed3709cd4cf61166f6f53a12f417302/grpcio-1.82.1-cp313-cp313-win_amd64.whl", hash = "sha256:3dbfb52c36d9511ac2b8e6c94fdde837b393ae520cc321f52a333a2deedf5a90", size = 5000980, upload-time = "2026-07-08T12:35:46.262Z" }, + { url = "https://files.pythonhosted.org/packages/b4/cb/cf9ae9e164c6e6dc8a494faa9771763df9da150eefe19671009624d1559f/grpcio-1.82.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:35f990f7784c8fd2872644f07f96ebb4d9e48e145a190ab80d0280af91a1bfb2", size = 6146901, upload-time = "2026-07-08T12:35:49.261Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/eccf26dbcfb7f7cab8027c5490a16c8937c5aa7a2ec20a3eab2cf7a43165/grpcio-1.82.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:46536a4a1f4434df3c851b9254ff6fc7df5705b273681a15ca277d5921c178a0", size = 11954756, upload-time = "2026-07-08T12:35:52.196Z" }, + { url = "https://files.pythonhosted.org/packages/ad/75/3b3b4a3cc9f084b026af96e1d3e539b1af29ec7f41ed0dfff3cb99cc8626/grpcio-1.82.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d6650a7c1ebb7921c70e12a385439a8118efb99e669fa9ed31cf25db1843937c", size = 6723087, upload-time = "2026-07-08T12:35:54.973Z" }, + { url = "https://files.pythonhosted.org/packages/9c/8b/b0f0c9b1400a99a4da4c09b114f101b192f8f11192e76f620b8962f5d90b/grpcio-1.82.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b8e110c66df5204c0506d6c8787b35d48b8b699ef5aa366d6c4d67325c67fe9a", size = 7454542, upload-time = "2026-07-08T12:35:57.586Z" }, + { url = "https://files.pythonhosted.org/packages/b7/bd/428e38868382aa193697a5aa53973f29c58e58ba4268aa0c86a2715ee58b/grpcio-1.82.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f853eae07235a51a27bb5d6a9a175a59ca55dc9b99edc6ce2f76f07332d333ae", size = 6889588, upload-time = "2026-07-08T12:36:00.012Z" }, + { url = "https://files.pythonhosted.org/packages/49/ce/03e01d5e10259bf5c08ee50570cc94724e79c956f61fd2f09b341af0956c/grpcio-1.82.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:60b0f2c95337694fc094b77d9f60f50566c84b5677393e342eb98daeee242d98", size = 7514166, upload-time = "2026-07-08T12:36:02.693Z" }, + { url = "https://files.pythonhosted.org/packages/ff/59/278b4b600329e2ba3849f3c1ea3c820b3a01b38a7ad184ba09595e8d2733/grpcio-1.82.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:b064fc444812bdaa9825d33c26f8d732d63ee6a5d78557c1faf92c98687fed27", size = 8536166, upload-time = "2026-07-08T12:36:05.349Z" }, + { url = "https://files.pythonhosted.org/packages/44/27/7ccf2ef00f27a8e47a79d641c8ceaf7d3028c7a03d9a97b4c8a9a783c086/grpcio-1.82.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7d7ede11d747b4e1bd05e3bc0260e155b65a88735a895a10f6521f19b889511e", size = 7912572, upload-time = "2026-07-08T12:36:08.393Z" }, + { url = "https://files.pythonhosted.org/packages/0d/be/33742482d2753f2d3a1b7641664b6622262d44f2f3b609f13425dd86d36f/grpcio-1.82.1-cp314-cp314-win32.whl", hash = "sha256:3d21f19838dc255ecbb79321b15ae9b98fbddff4c3d4aedb0a81bdd7f4ab572a", size = 4321856, upload-time = "2026-07-08T12:36:10.899Z" }, + { url = "https://files.pythonhosted.org/packages/cc/67/03329c847172c78ddeb1eb9be6b444fdbc12775a84c958b27e427e7b926d/grpcio-1.82.1-cp314-cp314-win_amd64.whl", hash = "sha256:e20f1edbb15f99e3128ec86433f9785fd5a451d8f115e74fe0056134f092a9d5", size = 5141114, upload-time = "2026-07-08T12:36:13.595Z" }, ] [[package]] @@ -2161,18 +1590,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, ] -[[package]] -name = "httplib2" -version = "0.31.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyparsing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c1/1f/e86365613582c027dda5ddb64e1010e57a3d53e99ab8a72093fa13d565ec/httplib2-0.31.2.tar.gz", hash = "sha256:385e0869d7397484f4eab426197a4c020b606edd43372492337c0b4010ae5d24", size = 250800, upload-time = "2026-01-23T11:04:44.165Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/90/fd509079dfcab01102c0fdd87f3a9506894bc70afcf9e9785ef6b2b3aff6/httplib2-0.31.2-py3-none-any.whl", hash = "sha256:dbf0c2fa3862acf3c55c078ea9c0bc4481d7dc5117cae71be9514912cf9f8349", size = 91099, upload-time = "2026-01-23T11:04:42.78Z" }, -] - [[package]] name = "httpx" version = "0.28.1" @@ -2199,7 +1616,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "1.19.0" +version = "1.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -2210,12 +1627,11 @@ dependencies = [ { name = "packaging" }, { name = "pyyaml" }, { name = "tqdm" }, - { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/88/27/629cfe58c582f92ded066c4a07d1a057ff617118ab7973200f770bd853cb/huggingface_hub-1.19.0.tar.gz", hash = "sha256:fd771622182d40977272a923953ee3b1b13538f9f8a7f5d78398f10af0f1c0bd", size = 824721, upload-time = "2026-06-11T12:33:18.665Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/8f/999e4dda11c6187c78f090eac00895a47e11a0049308f07579bcb7aa3aa2/huggingface_hub-1.23.0.tar.gz", hash = "sha256:c04997fb8bbdace1e57b7703d30ed7678af51f70d00d241819ff411b92ae9a88", size = 919163, upload-time = "2026-07-09T14:49:32.315Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a5/558da89f66464d8d0229ff497e8b8666977de2d8cf48c28a2862ecf1250f/huggingface_hub-1.19.0-py3-none-any.whl", hash = "sha256:1dc72e1f6b4d6df6b30eb72e57d00514ef453d660f04af2b87f0e67267f31ee0", size = 693398, upload-time = "2026-06-11T12:33:16.695Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ce/13b2ba57838b8db1e6bd033c1b21ce0b9f6153b87d4e4939f77074e41eb0/huggingface_hub-1.23.0-py3-none-any.whl", hash = "sha256:b1d604788f5adc7f0eb246e03e0ec19011ca06e38400218c347dccc3dffa64a2", size = 770336, upload-time = "2026-07-09T14:49:30.597Z" }, ] [[package]] @@ -2241,14 +1657,14 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "8.7.1" +version = "8.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/72/c600ae4f68c28fc19f9c31b9403053e5dbb8cace2e6842c7b7c3e4d42fe9/importlib_metadata-8.9.0.tar.gz", hash = "sha256:58850626cef4bd2df100378b0f2aea9724a7b92f10770d547725b047078f99ee", size = 56140, upload-time = "2026-03-20T16:56:26.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, + { url = "https://files.pythonhosted.org/packages/7d/f9/97f2ca8bb3ec6e4b1d64f983ebe98b9a192faddff67fac3d6303a537e670/importlib_metadata-8.9.0-py3-none-any.whl", hash = "sha256:e0f761b6ea91ced3b0844c14c9d955224d538105921f8e6754c00f6ca79fba7f", size = 27220, upload-time = "2026-03-20T16:56:25.07Z" }, ] [[package]] @@ -2308,14 +1724,14 @@ wheels = [ [[package]] name = "jaraco-functools" -version = "4.5.0" +version = "4.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "more-itertools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/cf/ea4ef2920830dea3f5ab2ea4da6fb67724e6dca80ee2553788c3607243d0/jaraco_functools-4.5.0.tar.gz", hash = "sha256:3bb5665ea4a020cf78a7040e89154c77edadb3ca74f366479669c5999aa70b03", size = 20272, upload-time = "2026-05-15T21:34:10.025Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/1f/c23395957d41ccf27c4e535c3d334c4051e5395b3752057ba4cbaec35c56/jaraco_functools-4.6.0.tar.gz", hash = "sha256:880c577ec9720b3a052d5bc611fb9f2269b3d87902ef42440df443b88e443280", size = 20837, upload-time = "2026-07-14T01:28:02.544Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/9a/982e48afcffcd727a9144506720ffd4224b6b7e355c98641866f38b7c043/jaraco_functools-4.5.0-py3-none-any.whl", hash = "sha256:79ce39246eddbde4b3a03b77ea5f0f7878dc669b166a66cf3fa8e266aa3fa2f4", size = 10594, upload-time = "2026-05-15T21:34:08.595Z" }, + { url = "https://files.pythonhosted.org/packages/02/36/ecc85bc96c273dc8a11273ed4782272975e6338d4a3e9228621175edf0e3/jaraco_functools-4.6.0-py3-none-any.whl", hash = "sha256:99e3dc0060c5cbe8fcd1cdb36258e2a65ca40f1566b2033b12abb1bb44dd3c30", size = 11677, upload-time = "2026-07-14T01:28:01.59Z" }, ] [[package]] @@ -2341,105 +1757,101 @@ wheels = [ [[package]] name = "jiter" -version = "0.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/b5/55f06bb281d92fb3cc86d14e1def2bd908bb77693183e7cb1f5a3c388b0c/jiter-0.15.0.tar.gz", hash = "sha256:4251acc80e2b7c9b7b8823456ea0fceeb0734dac2df7636d3c711b38476b5a76", size = 166640, upload-time = "2026-05-19T10:09:48.361Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/da/76a2c7e510ba15fe323d9509c223ab272da79ea59f54488f4a78da6426db/jiter-0.15.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:edebcf7d1f601199084bb6e844d7dc67e03e04f6ac786b0332d616635c4ff7a4", size = 310849, upload-time = "2026-05-19T10:06:51.944Z" }, - { url = "https://files.pythonhosted.org/packages/5d/8e/827be942883a4dc0862c48626ff41af3320b1902d136a0bf4b9041f2c567/jiter-0.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f924585cdacf631cd382b657966847bb537bf9ed0a6f9b991da5f05a631480f", size = 314991, upload-time = "2026-05-19T10:06:53.522Z" }, - { url = "https://files.pythonhosted.org/packages/6d/38/be2832be361ba1b9517c76f46d30b64e985be1dd43c974f4c3a4b1844436/jiter-0.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abbf258599526ad0326fe51e252e24f2bd6f24f1852681b4b78feda3808f1d18", size = 340843, upload-time = "2026-05-19T10:06:55.071Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d8/90f01fb83c0c7ba509303ec93e32a308fbfa167d264860b01c0fd0dbbd06/jiter-0.15.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c468136b8bd6bb18c8786e4236a1fa27362f24cb23450ba0cb204ab379b8e6f", size = 365116, upload-time = "2026-05-19T10:06:56.893Z" }, - { url = "https://files.pythonhosted.org/packages/91/38/94593d34f8c67a0b6f6cbc027f016ffa9780b3a858a7a86f6fd7a15bcc1e/jiter-0.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05906b93d72f03339e6bb7cf8dc10ebda64a0266126eed6beba79e20abcf5fd4", size = 457970, upload-time = "2026-05-19T10:06:58.707Z" }, - { url = "https://files.pythonhosted.org/packages/df/04/d79962dd49d00c97e2a9b4cacea1947904d02135936960351f9a96d4c1a6/jiter-0.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30ce785d2adb8e32c3f7741442370a74834ec4c01f3c48f0750227a0b4ef27d6", size = 375744, upload-time = "2026-05-19T10:07:00.471Z" }, - { url = "https://files.pythonhosted.org/packages/c3/2e/5d37abe2be0e819c21e2338bebd410e481763ce526a9138c8c3652fa0123/jiter-0.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd73e3da91a0a722d67165e849ce2cdc10de0e0d48738c142be8c6c5f310f4c", size = 349609, upload-time = "2026-05-19T10:07:01.829Z" }, - { url = "https://files.pythonhosted.org/packages/7a/90/98768ad2ed90c1fda15d64157de2dfbf73c1c074d4b1bfaca915480bc7cf/jiter-0.15.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:ceb8fc27d38793f9c97149be8302720c5b22e5c195a37bf2c45dc36c4600a512", size = 354366, upload-time = "2026-05-19T10:07:03.587Z" }, - { url = "https://files.pythonhosted.org/packages/d6/c4/fbfb806209f1fe4b7dccdfb07bc62bb044300734a945b06fd64db446ef6a/jiter-0.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d726e3ceeb337191324b49de298142f27c3ad10886341555d1d5315b5f252c6a", size = 393519, upload-time = "2026-05-19T10:07:05.08Z" }, - { url = "https://files.pythonhosted.org/packages/37/1c/b9c257cd70cb453b6d10f3ebf0402cdb11669ab455389096f09839670290/jiter-0.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2c8aea7781d2a372227871de4e1a1332aa96f5a89fd76c5e835dafdbad102887", size = 519952, upload-time = "2026-05-19T10:07:06.589Z" }, - { url = "https://files.pythonhosted.org/packages/a9/1a/aa85027db7ab15829c12feebbc33b404f53fc399bd559d85fd0d6365ff0d/jiter-0.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf4bd113a69c0a740e27cb962ce10630c36d2b8f59d759a651b955ee9d18a823", size = 550770, upload-time = "2026-05-19T10:07:08.228Z" }, - { url = "https://files.pythonhosted.org/packages/d4/54/8c3f65c8a5687925e84708f19d63f7f37d28e2b86a48d951702ad94424d8/jiter-0.15.0-cp310-cp310-win32.whl", hash = "sha256:d92a5cd21fdb083931d546c207aa29633787c5dc5b02daab2d32b843f88a2c53", size = 209303, upload-time = "2026-05-19T10:07:10.006Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/0528a1eb9f42dd2d8228a0711458628f35924d131f623eaebc35fd23d3d4/jiter-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:e58585a58209d72691ce2d62a9147445f5a87beb0bde97fde284c96ae392a3d1", size = 200404, upload-time = "2026-05-19T10:07:11.426Z" }, - { url = "https://files.pythonhosted.org/packages/e4/13/daa722f5765c393576f466378f9dfd29d77c9bed939e0688f96afa3601ea/jiter-0.15.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0f862193b8696249d22ec433e85fd2ab0ad9596bc3e45e6c0bc55e8aeba97be2", size = 310899, upload-time = "2026-05-19T10:07:12.89Z" }, - { url = "https://files.pythonhosted.org/packages/7f/82/2d2551829b082f4b6d82b9f939b031fb808a10aab1ec0664f82e150bb9a2/jiter-0.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1303d4d68a9b051ea90502402063ecf3807da00ad2affa19ca1ae3b90b3c5f67", size = 314963, upload-time = "2026-05-19T10:07:14.539Z" }, - { url = "https://files.pythonhosted.org/packages/2a/0a/8b1a51466f7fe9f31dbe4bc7e0ca848674f9825e0f737b929b97e8c60aa7/jiter-0.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:392b8ab019e5502d08aff85c6272209c24bc2cbe706ea82a56368f524236614a", size = 341730, upload-time = "2026-05-19T10:07:15.869Z" }, - { url = "https://files.pythonhosted.org/packages/f6/2a/e71dea19822e2e404e83992a08c1d6b9b617bb944f28c9c2fbd85d02c91e/jiter-0.15.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:773b6eb282ce11ee19f05f6b2d4404fa308e5bbd353b0b80a0262caad6db2cd7", size = 366214, upload-time = "2026-05-19T10:07:17.259Z" }, - { url = "https://files.pythonhosted.org/packages/c4/59/97e1fa539d124a509a00ab7f669289d1c1d236ecabf12948a18f16c91082/jiter-0.15.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2c0c44d569ce0f2850f5c926f8caeb5f245fbc84475aeb36efccc2103e6dbd", size = 459527, upload-time = "2026-05-19T10:07:18.741Z" }, - { url = "https://files.pythonhosted.org/packages/d1/7a/4a68d331aef8cf2e2393c14a3aacb635c62aa86071b0229899fb5baaa907/jiter-0.15.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:032396229564bca02440396bd327710719f724f5e7b7e9f7a8eb3faa4a2c2281", size = 375451, upload-time = "2026-05-19T10:07:20.208Z" }, - { url = "https://files.pythonhosted.org/packages/7b/7e/1c445c2b6f0e30a274dc8082e0c3c7825411cce80d726bccd697c98cc8d3/jiter-0.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d37768fce7f88dd2a8c6091f2325dea27d30d30d5c6e7a1c0f0af77723b708", size = 349428, upload-time = "2026-05-19T10:07:22.372Z" }, - { url = "https://files.pythonhosted.org/packages/00/94/e20d38984fc17a636371bffd2ae0f698124fdc8e75ef969cd2da6ba7cea7/jiter-0.15.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2c9cb907439d20bd0c7d7565ca01ee52234203208433749bae5b516907526928", size = 355405, upload-time = "2026-05-19T10:07:23.916Z" }, - { url = "https://files.pythonhosted.org/packages/94/fa/4d09f814779d0ea80a28ed8e4c6662ec9a4a8ecef0ac52190ebac6262d14/jiter-0.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9100ddbec09741cc66feb0fc6773f8bdbd0e3c345689368f260082ff85dcc0cd", size = 393688, upload-time = "2026-05-19T10:07:25.854Z" }, - { url = "https://files.pythonhosted.org/packages/54/9d/8eb5d4fb8bf7e93a75964a5da71a75c67c864baf7fa3f98598187b3c7e57/jiter-0.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ae1b0d82ac2d987f9ea512b1c9adfcc71a28de3dea3a6039b54d76cffda9901e", size = 520853, upload-time = "2026-05-19T10:07:27.303Z" }, - { url = "https://files.pythonhosted.org/packages/e7/2c/5e07874e59e623a943a0acf1552a80d05b70f31b402287a8fc6d7ec634c7/jiter-0.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8020c99ec13a7db2b6f96cbe82ef4721c88b426a4892f27478044af0284615ef", size = 551016, upload-time = "2026-05-19T10:07:28.846Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/d2d34422143474cadc15b60d482b1c35683dbc5c63c24346ddd0df09bcaf/jiter-0.15.0-cp311-cp311-win32.whl", hash = "sha256:42bfb257930800cf43e7c62c832402c704ab60797c992faf88d20e903eac8f32", size = 209518, upload-time = "2026-05-19T10:07:30.431Z" }, - { url = "https://files.pythonhosted.org/packages/1d/7d/52778b930e5cc3e52a37d950b1c10494244308b4329b25a0ff0d88303a81/jiter-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:860a74063284a2ae9bfedd694f299cc2c68e2696c5f3d440cc9d18bb81b9dd04", size = 200565, upload-time = "2026-05-19T10:07:32.125Z" }, - { url = "https://files.pythonhosted.org/packages/3b/4f/d9b4067feb69b3fa6eb0488e1b59e2ad5b463fe39f59e527eab2aca00bb0/jiter-0.15.0-cp311-cp311-win_arm64.whl", hash = "sha256:37a10c377ce3a4a85f4a67f28b7afe093154cde77eaf248a72e856aa08b4d865", size = 195488, upload-time = "2026-05-19T10:07:33.846Z" }, - { url = "https://files.pythonhosted.org/packages/44/53/4f6bddbcde3c71e56d0aa1337ec95950f3d27dd4153e25aadf0feac71751/jiter-0.15.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0e90a1c315a0226ec822d973817967f9223b7701546c8c2a7913e7ab0926294d", size = 308793, upload-time = "2026-05-19T10:07:35.25Z" }, - { url = "https://files.pythonhosted.org/packages/01/84/c01099b59a285a1ebba64ae93f62bfa036675340fd1b0045ae65890a0442/jiter-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8c9004af7c8d67cce7f1aae1026fb55607f4aa600710d08ede3a3ce4aeefe7e0", size = 309570, upload-time = "2026-05-19T10:07:36.919Z" }, - { url = "https://files.pythonhosted.org/packages/58/64/8fb7f9d45bb98190355454cd04dad8d8f27223d6bd52f83af07f637168a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c210f8b35dc6f30aafd4b4365ca89b9d1189f21ab49b8e68fa6322a847aef138", size = 336783, upload-time = "2026-05-19T10:07:38.694Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b6/f5739011d009b3a30f6a53c5240979030ba29ae46a8c67e3a15759f7c37d/jiter-0.15.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f30bae8bc1c2d613e28e5af3e8cceb09b742f1c8a8a5f839fb67afaffc03b61", size = 363555, upload-time = "2026-05-19T10:07:40.832Z" }, - { url = "https://files.pythonhosted.org/packages/e5/12/98a9d9f766665e8a3b6252454e17cb0c464606a28cf2fa09399b003345fa/jiter-0.15.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60e71b6d10cfc284c9bf36bd885e8d44c46f688ce50aa91b5edd90181dea687", size = 452255, upload-time = "2026-05-19T10:07:42.62Z" }, - { url = "https://files.pythonhosted.org/packages/e8/d5/60f972840f79c5e7544fce567c56f1e4e50468f996baba3e78d823dd62a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ab068bce62a45aa3e7367eceaffb5dde60b7eb853be8dece45132e3d0ff4879", size = 373559, upload-time = "2026-05-19T10:07:44.201Z" }, - { url = "https://files.pythonhosted.org/packages/ee/cf/d46ef1234ba335aabc2f013210db8e0821a22f5e644a2e9449df199ecc23/jiter-0.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa248c9eb220197d363f688818dac2fd4b2f0cd7d843ca7105d652034823427d", size = 346055, upload-time = "2026-05-19T10:07:46.005Z" }, - { url = "https://files.pythonhosted.org/packages/f0/63/4d2749d8d54d230bad9b3a6b0d00cc28c6ff6b2fdffc26a8ccf76cc5a974/jiter-0.15.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2a77aadd57cac1682e4401a72724d2796d89a4ba129b1a5812aa94ee480826eb", size = 351406, upload-time = "2026-05-19T10:07:47.855Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b9/9965b990035d8773328e0a8c8b457a87bf2b19f6c4126d9d99296be5d16a/jiter-0.15.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2ae901f3a55bfafdde31d289590fa25e3245735a2b1e8c7cc15871710a002871", size = 389357, upload-time = "2026-05-19T10:07:49.665Z" }, - { url = "https://files.pythonhosted.org/packages/2d/55/9ddf903deda1413e87fed792f416b7123daee5b8efbad6a202a7421c36a5/jiter-0.15.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f0b271b462769543716f92d3a4f90527df6ef5ed05ee95ec4137f513e21e1b77", size = 517263, upload-time = "2026-05-19T10:07:51.537Z" }, - { url = "https://files.pythonhosted.org/packages/e8/76/a0c40ad064d3a20a4fde231e35d56e9a01ce82164278180e82d5daf85469/jiter-0.15.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2fb6a5d26af81fc0f00f9360a891e05cf755e149bba391c4d563adc54812973d", size = 548646, upload-time = "2026-05-19T10:07:53.196Z" }, - { url = "https://files.pythonhosted.org/packages/23/4f/eca9b954942916ba2f453891b8593ab444cd872396fe66a3936616f236f3/jiter-0.15.0-cp312-cp312-win32.whl", hash = "sha256:c2f6bb8b5216ab9e7873bc08b5d7bef2b8abbb578a3069bf1cd14a45d71d771d", size = 206427, upload-time = "2026-05-19T10:07:55.307Z" }, - { url = "https://files.pythonhosted.org/packages/95/bf/8ead82a87495149542748e828d153fd232a512a22c83b02c4815c1a9c7d8/jiter-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:40b2c7e92c44a84d748d21706c68dc6ff8161d80b59c99d774721a0d2317d7c7", size = 197300, upload-time = "2026-05-19T10:07:56.651Z" }, - { url = "https://files.pythonhosted.org/packages/f4/e4/9b8a78fb2d894471bc344e37f1949bdd784bd914d031dba0ba3a40c71dd7/jiter-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:cc0bc345cf2df9d1c00ac443f50d543c1ccfa8b0422cb85b1ab70d681c0b255b", size = 192702, upload-time = "2026-05-19T10:07:58.307Z" }, - { url = "https://files.pythonhosted.org/packages/e5/f4/f708c900ecee41b2025ef8413d5351e5649eb2125c506f6720cc69b06f5c/jiter-0.15.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1c11465f97e2abf45a014b83b730222f8f1c5335e802c7055a67d50de6f1f4e3", size = 307829, upload-time = "2026-05-19T10:07:59.704Z" }, - { url = "https://files.pythonhosted.org/packages/86/59/db537c0949e83668c38481d426b9f2fd5ab758c4ee53a811dd0a510626a0/jiter-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e7b1776f0797956c509e123d0952d10d293a9492dea9f288ab9570ec01d1a5", size = 308445, upload-time = "2026-05-19T10:08:01.184Z" }, - { url = "https://files.pythonhosted.org/packages/37/38/ea0e13b18c30ef951da0d47d39e7fa9edb82a93a62990ffbd7cea9b622d4/jiter-0.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:351a341c2105aa430b7047e30f1bf7975f6313b00165d3fc07be2edaf741f279", size = 336181, upload-time = "2026-05-19T10:08:02.688Z" }, - { url = "https://files.pythonhosted.org/packages/58/fc/2303901b16c4ba05865588990a420c0b4156270b44379c20931544a1d962/jiter-0.15.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ab395feec8d249ec4044e228e98a7033f043426a265df439dc3698823f0a4e4", size = 362985, upload-time = "2026-05-19T10:08:04.394Z" }, - { url = "https://files.pythonhosted.org/packages/5b/6f/11bace093c52e7d4d26c8e606ccd7ae8c972189622469ec0d9e28161e28b/jiter-0.15.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2a438005b6f22d0273413484d6094d7c2c5d10ec1b3a3bf128e0d1d3ba53258", size = 453292, upload-time = "2026-05-19T10:08:05.967Z" }, - { url = "https://files.pythonhosted.org/packages/22/db/987f2f086ca4d7a6582eb4ccd513f9b26b42d9e4243a087609a3137a8fc7/jiter-0.15.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f18f85e4218d1b40f000f42a92239a7a61a902cd42c65e6c360dbd17dcb20894", size = 373501, upload-time = "2026-05-19T10:08:07.857Z" }, - { url = "https://files.pythonhosted.org/packages/8f/7c/89fbcabb2739b7a5b8dc959a1b6c5761f6484f5fed3486854b3c789bb1de/jiter-0.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1aa62e277fc1cbd80e6deacae6f4d983b41b3d7728e0645c5d741a6149bba45", size = 344683, upload-time = "2026-05-19T10:08:09.431Z" }, - { url = "https://files.pythonhosted.org/packages/30/6f/6cca7692e7dddfec6d8d76c54dc97f2af2a41df4ac0674b999df1f09a5f3/jiter-0.15.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:6550fa135c7deb8ead6af49ed7ff648532ea8334a1447fe34a36315ef79c5c29", size = 350892, upload-time = "2026-05-19T10:08:11.352Z" }, - { url = "https://files.pythonhosted.org/packages/39/14/0338d6190cb8e6d22e677ab1d4eabd4117f67cca70c54cd04b82ff64e068/jiter-0.15.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:066f8f33f18b2419cd8213b2436fa7fbc9c499f315971cfa3ce1f9820c001b1b", size = 388723, upload-time = "2026-05-19T10:08:12.912Z" }, - { url = "https://files.pythonhosted.org/packages/90/31/cc19f4a1bdb6afb09ce6a2f2615aa8d44d994eba0d8e6105ed1af920e736/jiter-0.15.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:75e8a04e91432dde9f1838373cf93d23726c79d3e908d319acf0e796f85592e7", size = 516648, upload-time = "2026-05-19T10:08:14.808Z" }, - { url = "https://files.pythonhosted.org/packages/49/9f/833c541512cd091b63c10c0381973dfe11bc7a503a818c16384417e0c81e/jiter-0.15.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a97261f1fccb8e50ecd2890a96e46efdc3f57c80a197324c6777827231eca712", size = 547382, upload-time = "2026-05-19T10:08:16.927Z" }, - { url = "https://files.pythonhosted.org/packages/d2/11/e7b70e91f90bc4477e8eee9e8a5f7cf3cb41b4525d6394dc98a714eb8f7f/jiter-0.15.0-cp313-cp313-win32.whl", hash = "sha256:c77496cb10bd7549690fbbab3e5ec05857b83e49276f4a9423a766ddd2afcd4c", size = 205845, upload-time = "2026-05-19T10:08:18.401Z" }, - { url = "https://files.pythonhosted.org/packages/4b/23/5c20d9ad6f02c493e4023e5d2d09e1c1f15fe2753c9102c544aff068a88e/jiter-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b15741f501469009ae0ae90b7147958a664a7dede40aa7ff174a8a4645f546d0", size = 196842, upload-time = "2026-05-19T10:08:20.131Z" }, - { url = "https://files.pythonhosted.org/packages/6b/11/1eb400ef248e8c925fd883fbe325daf5e42cd1b0d308539dd332bd4f7ffc/jiter-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d6a60072b44c3c2b797a7ddcbcbbf2b34ea3cfd4721580fbfd2a09d9d9b84ba", size = 192212, upload-time = "2026-05-19T10:08:21.807Z" }, - { url = "https://files.pythonhosted.org/packages/8a/60/2fd8d7c79da8acf9b7b277c7616847773779356b92acfc9bb158452174da/jiter-0.15.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ef1fd24d9413f6209e00d3d5a453e67acfe004a25cc6c8e8484faed4311ab9e8", size = 315065, upload-time = "2026-05-19T10:08:23.218Z" }, - { url = "https://files.pythonhosted.org/packages/46/f4/008fb7d65e8ac2abf00811651a661e025c4ba80bbc6f378450384ddd3aed/jiter-0.15.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:144f8e72cb53dab146347b91cceac01f5481237f2b93b4a339a1ee8f8878b67c", size = 339444, upload-time = "2026-05-19T10:08:24.701Z" }, - { url = "https://files.pythonhosted.org/packages/00/55/90b0c7b9c6896c0f2a591dd36d36b71d22e09674bfef178fa03ba3f81499/jiter-0.15.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553fcac2ef2cb990877f9fc0833b8b629a3e6a5670b6b5fd58219b41a653ddc4", size = 347779, upload-time = "2026-05-19T10:08:26.408Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/69666cec5000fd57734c118437394516c749ae8dbeea9fb66d6fef9c4775/jiter-0.15.0-cp313-cp313t-win_amd64.whl", hash = "sha256:774f93f65031856bf14ad9f59bdcab8b8cad501e5ceabd51ba3525f76937a25b", size = 200395, upload-time = "2026-05-19T10:08:28.055Z" }, - { url = "https://files.pythonhosted.org/packages/39/04/a6aa62cd27e8149b0d28df5561f10f6cceaf7935a9ccf3f1c5a05f9a0cd8/jiter-0.15.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f1e1754960f38ec40613a07e5e372df67acb3b890fb383b6fb3de3e49ddbf3c7", size = 190516, upload-time = "2026-05-19T10:08:29.35Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d2/079f350ebf7859d081de30aa890f9e3be68516f754f3ba32366ffff4dcee/jiter-0.15.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:ac0d9ddea4350974be7a221fc25895f251a8fee748c889bdced2141c0fec1a49", size = 308884, upload-time = "2026-05-19T10:08:31.667Z" }, - { url = "https://files.pythonhosted.org/packages/04/4e/a2c30a7f69b48c03b20935d647479106fe932f6e63f75faf53937197e05d/jiter-0.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01a8222cf05ab1128e239421156c207949808acaaea2bdfd33130ae666786e86", size = 310028, upload-time = "2026-05-19T10:08:33.304Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/2e7cdfd3cf8ca967be38c48f5cf474d79f089efaf559a40f15984a77ae69/jiter-0.15.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:182226cbc930c9fab81bc2e41a4da672f89539906dadb05e75670ac07b94f71f", size = 337485, upload-time = "2026-05-19T10:08:35.259Z" }, - { url = "https://files.pythonhosted.org/packages/9b/11/15a1aa28b120b8ee5b4f1fb894c125046225f09847738bd64233d3b84883/jiter-0.15.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:71683c38c825452999b5717fcae07ea708e8c93003e808be4319c1b02e3d176e", size = 364223, upload-time = "2026-05-19T10:08:36.694Z" }, - { url = "https://files.pythonhosted.org/packages/b7/25/f442e8af5f3d0dcf47b39e83a0efd9ee45ea946aa6d04625dc3181eae3b6/jiter-0.15.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f2218e6a9e5c18bc10fe6d41ac189c442c88eacf11bad9f28ef95a9bef00e6", size = 456387, upload-time = "2026-05-19T10:08:38.143Z" }, - { url = "https://files.pythonhosted.org/packages/da/f4/37f2d2c9f64f49af7da652ed7532bb5a2372e588e6927c3fdd76f911db65/jiter-0.15.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5157de9f76eb4bc5ea74a1219366a25f945ad305641d74e04f59c54087091aa9", size = 374461, upload-time = "2026-05-19T10:08:39.869Z" }, - { url = "https://files.pythonhosted.org/packages/60/28/edcfbbbf0cb15436f36664a8908a0df47ab9006298d4cd937dc08ea932d6/jiter-0.15.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c5db5527c221249a876160663ab891ace358c17f7b9c93ec1478b7f0550e5c", size = 345924, upload-time = "2026-05-19T10:08:41.668Z" }, - { url = "https://files.pythonhosted.org/packages/47/13/89fba6398dab7f202b7278c4b4aac122399d2c0183971c4a57a3b7088df5/jiter-0.15.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:3e4540b8e74e4268811ac05db226a6a128ff572e7e0ce3f1163b693cadb184cd", size = 352283, upload-time = "2026-05-19T10:08:43.091Z" }, - { url = "https://files.pythonhosted.org/packages/1b/da/0f6af8cef2c565a1ab44d970f268c43ccaa72707386ea6388e6fe2b6cd26/jiter-0.15.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:62ebd14e47e9aed9df4472afcb2663668ce4d74891cd54f86bf6e44029d6dc89", size = 389985, upload-time = "2026-05-19T10:08:44.915Z" }, - { url = "https://files.pythonhosted.org/packages/a1/ec/b9cb7d6d29e24ee14910266157d2a279d7a8f60ee0df7fa840882976ba64/jiter-0.15.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0be6f5ad41a809f303f416d17cec92a7a725902fb9b4f3de3d19362ac0ef8554", size = 517695, upload-time = "2026-05-19T10:08:46.486Z" }, - { url = "https://files.pythonhosted.org/packages/64/5e/6d1bda880723aae0ad86b4b763f044362448efe31e3e819635d41cb03451/jiter-0.15.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:813dfbb17d65328bf86e5f0905dd277ba2265d3ca20556e86c0c7035b7182e5a", size = 548868, upload-time = "2026-05-19T10:08:48.026Z" }, - { url = "https://files.pythonhosted.org/packages/0c/72/7de501cf38dcacaf35098796f3a50e0f2e338baba18a58946c618544b809/jiter-0.15.0-cp314-cp314-win32.whl", hash = "sha256:50e51156192722a9c58db112837d3f8ef96fb3c5ecc14e95f409134b08b158ec", size = 206380, upload-time = "2026-05-19T10:08:49.738Z" }, - { url = "https://files.pythonhosted.org/packages/1e/a9/e19addf4b0c1bdce52c6da12351e6bc42c340c45e7c09e2158e46d293ccc/jiter-0.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:30ce1a5d16b5641dc935d50ef775af6a0871e3d14ab05d6fc54dff371b78e558", size = 197687, upload-time = "2026-05-19T10:08:51.088Z" }, - { url = "https://files.pythonhosted.org/packages/f2/c9/776b1db01db25fc6c1d58d1979a37b0a9fe787e5f5b1d062d2eaacb77923/jiter-0.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:510c8b3c17a0ed9ac69850c0438dada3c9b82d9c4d589fcb62002a5a9cf3a866", size = 192571, upload-time = "2026-05-19T10:08:52.451Z" }, - { url = "https://files.pythonhosted.org/packages/a0/f6/45bb4670bacf300fd2c7abadbfb3af376e5f1b6ae75fd9bc069891d15870/jiter-0.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7553333dd0930c104a5a0db8df72bf7219fe663d731383b576bb6ed6351c984d", size = 317151, upload-time = "2026-05-19T10:08:53.867Z" }, - { url = "https://files.pythonhosted.org/packages/d7/68/ed635ad5acd7b73e454283083bbb7c8205ad10e88b0d9d7d793b09fe8226/jiter-0.15.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2143ab06181d2b029eedcb6af3cebe95f11bbac62441781860f98ee9330a6a6", size = 341243, upload-time = "2026-05-19T10:08:55.383Z" }, - { url = "https://files.pythonhosted.org/packages/5d/db/3ff4176b817b8ea33879e71e13d8bc2b0d481a7ed3fe9e080f333d415c16/jiter-0.15.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eac374c5c975709b69c10f09afd199df74150172156ad10c8d4fd785b7da995", size = 363629, upload-time = "2026-05-19T10:08:56.928Z" }, - { url = "https://files.pythonhosted.org/packages/ab/24/5f8270e0ba9c883582f96f722f8a0b58015c7ce1f8c6d4571cf394e99b6b/jiter-0.15.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3b3b775e33d3bfaec9899edc526ae97b0da0bf9d071a46124ba419149a414f8", size = 456198, upload-time = "2026-05-19T10:08:58.618Z" }, - { url = "https://files.pythonhosted.org/packages/45/5b/76fc02b0b5c54c3d18c60653156e2f76fde1816f9b4722db68d6ee2c897e/jiter-0.15.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3071db3346334beae1360b46da4606da57bf3528c167b3c38533afaf9f2c5", size = 373710, upload-time = "2026-05-19T10:09:00.151Z" }, - { url = "https://files.pythonhosted.org/packages/c4/52/4310821b0ea9277994d3e1f49fc6a4b34e4800caebacb2c0af81da59a454/jiter-0.15.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6694a173ecabc12eb60efbc0b474464ead1951ff65cd8b1e72100715c64512b", size = 349901, upload-time = "2026-05-19T10:09:01.621Z" }, - { url = "https://files.pythonhosted.org/packages/93/fe/67648c35b3594fba8854ac64cc8a826d8bcd18324bbdb53d77697c60b6ef/jiter-0.15.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a254e10b593624d230c365b6d616b22ca0ad65e63a16e6631c2b3466022e6ba8", size = 352438, upload-time = "2026-05-19T10:09:03.216Z" }, - { url = "https://files.pythonhosted.org/packages/cb/28/0a1879d07ad6b3e025a2750027363452ced93c2d16d1c9d4b153ffd51c91/jiter-0.15.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d8d2955167274e15d79a7a020afdd9b39c990eb80b2d89fca695d92dcfdd38ec", size = 388152, upload-time = "2026-05-19T10:09:04.741Z" }, - { url = "https://files.pythonhosted.org/packages/c1/78/46c6f6b56ba85c90021f4afd72ed42f691f8f84daacb5fe27277070e3858/jiter-0.15.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:acf4ee4d1fc55917239fe72972fb292dd773055d05eb040d36f4326e02cc2c0e", size = 517707, upload-time = "2026-05-19T10:09:06.231Z" }, - { url = "https://files.pythonhosted.org/packages/ca/cb/720662d4c88fcad606e826fef5424365527ba43ce4868a479aed8f8c507e/jiter-0.15.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:e7196e56f1cd69af1dbb07dff02dcfb260a50b45a82d409d92a06fedb32473b5", size = 548241, upload-time = "2026-05-19T10:09:08.093Z" }, - { url = "https://files.pythonhosted.org/packages/60/e3/935b8034fd143f21125c87d51404a9e0e1449186a494405721ff5d1d695e/jiter-0.15.0-cp314-cp314t-win32.whl", hash = "sha256:7f6163c0f10b055245f814dcc59f4818da60dfe72f3e72ab89fc24b6bd5e9c52", size = 207950, upload-time = "2026-05-19T10:09:09.616Z" }, - { url = "https://files.pythonhosted.org/packages/93/59/984fd9ece895953dad3e0880a650e766f5a2da2c5514f0eafdaaabbeb5f9/jiter-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:980c256edb05b78a111b99c4de3b1d32e31634b867fd1fc2cf726e7b7bba9854", size = 200055, upload-time = "2026-05-19T10:09:11.367Z" }, - { url = "https://files.pythonhosted.org/packages/0e/a4/cf8d779feb133a27a2e3bc833bccb9e13aa332cdf820497ebf72c10ce8c3/jiter-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:66b1880df2d01e206e8339769d1c7c1753bcb653efd6289e203f6f24ebada0c0", size = 191244, upload-time = "2026-05-19T10:09:12.74Z" }, - { url = "https://files.pythonhosted.org/packages/65/43/1fc62172aa98b50a7de9a25554060db510f85c89cfbed0dfe13e1907a139/jiter-0.15.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:411fa4dfa5a7ae3d11491027ffb9beadec3996010a986862db70d91abba1c750", size = 305585, upload-time = "2026-05-19T10:09:35.995Z" }, - { url = "https://files.pythonhosted.org/packages/e8/c4/dd58fcd9e2df83666e5c1c1347bef58ce919cd8efc3ffa38aeea62ce493b/jiter-0.15.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:2b0074e2f56eb2dacca1689760fd2852a068f85a0547a157b82cb4cafeb6768b", size = 306936, upload-time = "2026-05-19T10:09:37.435Z" }, - { url = "https://files.pythonhosted.org/packages/39/86/b695e16f1180c07f43ea98e73ecd21cf63fa2e1b0c1103739013784d11ae/jiter-0.15.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:913d02d29c9606643418d9ccfc3b72492ab25a6bf7889934e09a3490f8d3438b", size = 342453, upload-time = "2026-05-19T10:09:39.294Z" }, - { url = "https://files.pythonhosted.org/packages/34/56/55d76614af37fe3f22a3347d1e410d2a15da581997cb2da499a625000bb5/jiter-0.15.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b15d3ec9b0449c40e85319bdb4caa8b77ab526e74f5532ed94bec15e2f66822c", size = 345606, upload-time = "2026-05-19T10:09:40.727Z" }, - { url = "https://files.pythonhosted.org/packages/73/38/505941b2b092fd5bbbd60a52a880db1173f1690ae6751bed3af1c9ddcb4e/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:631f13a3d04e97d4e083993b10f4b99530e3a10d953e2eb5e196b7dc7f812ce0", size = 303769, upload-time = "2026-05-19T10:09:42.203Z" }, - { url = "https://files.pythonhosted.org/packages/e7/95/a06692b29e77473f286e1ec1f426d3ca44d7b5843be8ad21d7a5f3fcdcc0/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b6c0ffae686c39bf3737be60793783267628783ea42545632c10b291105aee45", size = 305128, upload-time = "2026-05-19T10:09:43.657Z" }, - { url = "https://files.pythonhosted.org/packages/23/85/7270d7ad41d6061a25b950c6bf91d638bd9aacb113200a8c8d57a055fd67/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d54fb5b31dea401a41af3f8a7d2512e9b6a6a005491e6166c7e4ffab9639a9c", size = 340459, upload-time = "2026-05-19T10:09:45.452Z" }, - { url = "https://files.pythonhosted.org/packages/c8/8d/302cb2057b7513327b4d575cff6b1d066ee6431a5357fc3f8867cd684406/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d5d6090cdc1b7c9e780dfb04949a990adb1e301a2fc0bbcee7de4638d33f9a", size = 344469, upload-time = "2026-05-19T10:09:46.864Z" }, +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/1f/10936e16d8860c70698a1aa939a46aa0224813b782bce4e000e637da0b2d/jiter-0.16.0.tar.gz", hash = "sha256:7b24c3492c5f4f84a37946ad9cf504910cf6a782d6a4e0689b6673c5894b4a1c", size = 176431, upload-time = "2026-06-29T13:05:13.657Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/d8/b959609e44012a42b1f3e5ba98ea3b33c7e41e6d4b77cd8f00fd19b1d3ad/jiter-0.16.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c5fc4f8def331036a7b8e981b4347ebe409981edbc8308a5ea842b8c3614fa6c", size = 310082, upload-time = "2026-06-29T13:02:31.356Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3d/4d7f5667ea0e0548534ba880b84bb3d12924fd133aa83ad6c6c80fca3d76/jiter-0.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a71d0d2014c3275043e1170bf3d4e771493cb0dcf07be54c567155f4d8ee64b", size = 315643, upload-time = "2026-06-29T13:02:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/9b/83/bed2dcb5c9f3e1ccfcbc67dda48265fe7d5ad0c9cadda5fe95f6e3b87f94/jiter-0.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:741eed508c233a76313a1c7b001f8f21b82f14327e9196ae8bd29a2cc164ae84", size = 341363, upload-time = "2026-06-29T13:02:34.853Z" }, + { url = "https://files.pythonhosted.org/packages/f4/2f/6bb3c3dda668ebc0445689c81a2b0f26a82b10843d67ed9c9b2c3edc177f/jiter-0.16.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3fb7bc819187b56dc48aa5c833aaf92257da8e07efdb9306156667bd2eeb491c", size = 365483, upload-time = "2026-06-29T13:02:36.295Z" }, + { url = "https://files.pythonhosted.org/packages/92/35/8a045ccb39164e70dcdae696413b661771f148b68b12b175c3a04d901937/jiter-0.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c9610fd25ebccb43fca584136f5c2fbb26802447eccd430dfdbab95a0fd5126", size = 461219, upload-time = "2026-06-29T13:02:38.116Z" }, + { url = "https://files.pythonhosted.org/packages/e7/99/22292dbbf0ed0c610cfe5ddc7f3bd67237a412f121318f865196e62a07bd/jiter-0.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4a1d68ff7ca1d3b5dee20a97a3decda7d5f15003823bf6d140c81f8561d3bc5c", size = 374905, upload-time = "2026-06-29T13:02:40.357Z" }, + { url = "https://files.pythonhosted.org/packages/29/ac/2f55ccb1f0eeafa6d89d24caf52f6f0944a59290ee199e9ade62177dca42/jiter-0.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb08c276dd02dac3a284acdd02cacc630d2e3cd6572a4b85519f35cbd133c3de", size = 348320, upload-time = "2026-06-29T13:02:41.923Z" }, + { url = "https://files.pythonhosted.org/packages/50/e3/7d88b9174c40064fabc07c84a9b62e6b10f5644562ec0e0a29392edbe978/jiter-0.16.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:8fc4d94713c4697347e38faf7d6ef91547c142219bdcfc7220c4870879974244", size = 356519, upload-time = "2026-06-29T13:02:43.436Z" }, + { url = "https://files.pythonhosted.org/packages/27/57/c4a33aeef513a9d5e26e31534e0bcc752d6ea0e54c94ddb7b68bade669c2/jiter-0.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a0f05e229edb29e68cdd0ccb83cea13b64263416120cf943767a6fd72e6787f", size = 394204, upload-time = "2026-06-29T13:02:44.987Z" }, + { url = "https://files.pythonhosted.org/packages/9d/70/c6c23e76ebb3766b111bc399437bbc9f870a76e2a92e10b2a5f561d57372/jiter-0.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2c842cbf374a8daf50b2c04212995bee34ca2ac2cdc29a901b4cdb072c9c4131", size = 521477, upload-time = "2026-06-29T13:02:46.724Z" }, + { url = "https://files.pythonhosted.org/packages/2a/d3/0001c8c0c5976af2625bb1cfb1895e8ec693b6589fe4574b8e6fc2c85501/jiter-0.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5ed466aee31294d7cdcd4d37dfe5c42c97bc29d9a5f00eacf24504358309cb9b", size = 552187, upload-time = "2026-06-29T13:02:48.144Z" }, + { url = "https://files.pythonhosted.org/packages/f6/76/311b718e07e85740e48619c0632b36f7e0b8d113984499e436452ed13a9a/jiter-0.16.0-cp310-cp310-win32.whl", hash = "sha256:b42e9ff5376819c053da25809a8d4b6fa6e473b4856ebe42e298ac958be3d7f9", size = 206513, upload-time = "2026-06-29T13:02:49.515Z" }, + { url = "https://files.pythonhosted.org/packages/db/7f/ac680eeb0777dc0eb7dc824800ba27880d7f6bc712e362d34ad8ee559f36/jiter-0.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:10438939205546132189c8e74a2d536a707841f3a25cd7c74ee91fe503407a26", size = 199505, upload-time = "2026-06-29T13:02:50.829Z" }, + { url = "https://files.pythonhosted.org/packages/4e/3f/fae6cc967d120ec89e31c5418a51176d8278b3087fbb384a9176754f353c/jiter-0.16.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:67fddeda1688f0cce2d2ae83ccf8a80f79936f2d2997d6cc2261f82fdb54a4d3", size = 309289, upload-time = "2026-06-29T13:02:52.301Z" }, + { url = "https://files.pythonhosted.org/packages/c8/e3/97c6c3562c077f6247d6e6ce5c82562500b6316c0d928e97e106b7a1321a/jiter-0.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c90c0f63df322be920eda6ce622e3083d8906ba267f8220fe7873213b8b4430e", size = 315181, upload-time = "2026-06-29T13:02:53.964Z" }, + { url = "https://files.pythonhosted.org/packages/7b/89/d8d073f8aa2667e46c6c0873f86fe4a512bba4293cc730f626a076211a62/jiter-0.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64c0203212098470032aabcde9356fc168f377aade3e43def61dfe17e92f2037", size = 340939, upload-time = "2026-06-29T13:02:55.412Z" }, + { url = "https://files.pythonhosted.org/packages/87/c9/db4fda3ed73fb864139305e935e5b8b38a5a24692a5a9dd356c22f1b9c8d/jiter-0.16.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12288303c9844e61e1651d02a9a6f6633e47d39f897d6991d1427161ce6b746e", size = 364932, upload-time = "2026-06-29T13:02:57.28Z" }, + { url = "https://files.pythonhosted.org/packages/a2/74/52b5e86241057f52ddd7c9a580f90effb51f9d06239f6fc612279b91a838/jiter-0.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cf109d010b4b05a105afb3d43be36a21322d345ad3111e13d15f680afef0e5b", size = 461132, upload-time = "2026-06-29T13:02:58.994Z" }, + { url = "https://files.pythonhosted.org/packages/a9/87/544a700f7447c1f31c5d7833821a4daa5683165c2d5a094fbf5b5800c3dc/jiter-0.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62c1b7fe1f77925acf5af68b6140b8810fa87dfd4dc0a9c8568ec2fa2a10429c", size = 374857, upload-time = "2026-06-29T13:03:00.455Z" }, + { url = "https://files.pythonhosted.org/packages/40/cd/0fcc3f7d39183674d5bfa9ec640faaeb506c60be7c8f94625dfba366e37c/jiter-0.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8597d23c87f59294f83bcb6229b9ed1fccee13dbba967b46930d2f1759466fee", size = 347053, upload-time = "2026-06-29T13:03:02.045Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ae/c7e64e7932ad597fa395b61440b249ada6366716e25c6e08dd2afbd021e6/jiter-0.16.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:3126a5dbad56401989ac769aca0cb56005bfb3e2366eea0ca99d1a91c3c1ee03", size = 356153, upload-time = "2026-06-29T13:03:03.706Z" }, + { url = "https://files.pythonhosted.org/packages/d4/1c/1c719044f14da814e1a060191ab19b96f3e99207bc5b4bfc6d6be34b3f80/jiter-0.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c4b4717bdb35ae456f831a6b08d01880fff399887a6bbc526a583a406e484eea", size = 393956, upload-time = "2026-06-29T13:03:05.165Z" }, + { url = "https://files.pythonhosted.org/packages/3b/dc/7b2f303a2847207e265503853a2d964a55354cffd62a5f2936c155486798/jiter-0.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:adff21bc78edfe086c15eb495b900306076de378dc2337c132401fc39bd79c91", size = 521081, upload-time = "2026-06-29T13:03:06.886Z" }, + { url = "https://files.pythonhosted.org/packages/c2/5f/501cf6e1e09caeb420195179ffc6f62aca603f1220ec53fd80d0d70b3e56/jiter-0.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dab907db06fc593645e73109acf4581ba5b548897d28b9348dc41ddc8343b2d3", size = 552085, upload-time = "2026-06-29T13:03:08.339Z" }, + { url = "https://files.pythonhosted.org/packages/79/54/aa5be86520113b79455c3877f3d1f07a348098df4083ba3688e9537e52dd/jiter-0.16.0-cp311-cp311-win32.whl", hash = "sha256:560b2cf3fb03240cd34f27409a238547488708f05b7c3924f571a60422251ec7", size = 206755, upload-time = "2026-06-29T13:03:09.653Z" }, + { url = "https://files.pythonhosted.org/packages/64/ec/2feb893eb330bd69b413866f4d5daada33c3962f1c6f270c91ca2d87fdf9/jiter-0.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:e431cfc9caf44c1d5459ff77d4e64cbf85fddb6a35dad836a15c6a9ec23087c1", size = 199155, upload-time = "2026-06-29T13:03:10.979Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9c/ca040d94415048a3666fc237774df8151c96f8d2b661cbe3b184acc95876/jiter-0.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:2a8e9e39cf083016137aa5cadafe3188adc2ba6ba1fbf1e5d18889ad3e9ad056", size = 194403, upload-time = "2026-06-29T13:03:12.341Z" }, + { url = "https://files.pythonhosted.org/packages/83/2b/52ace16ed031354f0539749a49e4bf33797d82bea5137910835fa4b09793/jiter-0.16.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:67c3bc1760f8c99d805dcab4e644027142a53b1d5d861f18780ebdbd5d40b72a", size = 306943, upload-time = "2026-06-29T13:03:14.035Z" }, + { url = "https://files.pythonhosted.org/packages/94/2e/34957c2c1b661c252ba9bcc60ae0bddc27e0f7202c6073326a13c5390eec/jiter-0.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5af7780e4a26bd7d0d989592bf9ef12ebf806b74ab709223ecca37c749872ea9", size = 307779, upload-time = "2026-06-29T13:03:15.418Z" }, + { url = "https://files.pythonhosted.org/packages/88/6c/59bd309cab4460c54cf1079f3eb7fe7af6a4c895c5c957a53378693bad2b/jiter-0.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5bf78d0e05e45cfdd66558893938d59afe3d1b1a824a202039b20e607d25a72", size = 335826, upload-time = "2026-06-29T13:03:17.11Z" }, + { url = "https://files.pythonhosted.org/packages/3b/8c/f5ef7b65f0df47afa16596969defb281ebb86e96df346d62be6fd853d620/jiter-0.16.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4444a83f946605990c98f625cdd3d2725bfb818158760c5748c653170a20e0e", size = 362573, upload-time = "2026-06-29T13:03:18.781Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0b/ace4354da061ee38844a0c27dc2c21eecd27aea119e8da324bea987522d0/jiter-0.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a23f0e4f957e1be65752d2dfac9a5a06b1917af8dc85deb639c3b9d02e31290", size = 457979, upload-time = "2026-06-29T13:03:20.293Z" }, + { url = "https://files.pythonhosted.org/packages/55/40/c0253d3772eb9dcd8e6606ee9b2d53ec8e5b814589c47f140aa585f21eaa/jiter-0.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c22a488f7b9218e245a0025a9ba6b100e2e54700831cf4cf16833a27fba3ad01", size = 372302, upload-time = "2026-06-29T13:03:21.739Z" }, + { url = "https://files.pythonhosted.org/packages/a8/d2/4839422241aa12860ce597b20068727094ba0bc480723c74924ca5bad483/jiter-0.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46add52f4ad47a08bfb1219f3e673da972191489a33016edefdb5ea55bfa8c48", size = 343805, upload-time = "2026-06-29T13:03:23.384Z" }, + { url = "https://files.pythonhosted.org/packages/e2/59/e196888a05befdda7dbe299b722d56f2f6eec65402bc34c0a3306d595feb/jiter-0.16.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:9c8a956fd72c2cf1e730d01ea080341f13aa0a97a4a33b51abebe725b7ae9ca9", size = 351107, upload-time = "2026-06-29T13:03:24.815Z" }, + { url = "https://files.pythonhosted.org/packages/ec/74/4cd9e0fca65232136400354b630fbfcd2de634e22ccbb96567725981b548/jiter-0.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:561926e0573ffe4a32498420a76d64b16c513e1ab413b9d28158a8764ac701e5", size = 388441, upload-time = "2026-06-29T13:03:26.266Z" }, + { url = "https://files.pythonhosted.org/packages/d9/8c/554691e48bc711299c0a293dd8a6179e24b2d66a54dc295421fcf64569c0/jiter-0.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:44d019fa8cdaf89bf29c71b39e3712143fdd0ac76725c6ef954f9957a5ea8730", size = 516354, upload-time = "2026-06-29T13:03:28.02Z" }, + { url = "https://files.pythonhosted.org/packages/a4/cb/01e9d69dc2cc6759d4f91e230b34489c4fdb2518992650633f9e20bece89/jiter-0.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0df91907609837f33341b8e6fe73b95991fdaa57caf1a0fbd343dffe826f386f", size = 547880, upload-time = "2026-06-29T13:03:29.534Z" }, + { url = "https://files.pythonhosted.org/packages/79/70/2953195f1c6ad00f49fa67e13df7e60acb3dd4f387101bc15abccddd905e/jiter-0.16.0-cp312-cp312-win32.whl", hash = "sha256:51d7b836acb0108d7c77df1742332cac2a1fa04a74d6dacec46e7091f0e91274", size = 203473, upload-time = "2026-06-29T13:03:31.025Z" }, + { url = "https://files.pythonhosted.org/packages/2d/05/2909a8b10699a4d560f8c502b6b2c5f3991b682b1922c1eedda242b225bd/jiter-0.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:1878349266f8ee36ecb1375cc5ba2f115f35fd9f0a1a4119e725e379126647f7", size = 196905, upload-time = "2026-06-29T13:03:32.472Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a9/6b82bb1c8d7790d602489b967b982a909e5d092875a6c2ade96444c8dfc5/jiter-0.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:2ed5738ae4af18271a51a528b8811b0cbfa4a1858de9d83359e4169855d6a331", size = 190618, upload-time = "2026-06-29T13:03:34.672Z" }, + { url = "https://files.pythonhosted.org/packages/91/c0/555fc60473d30d66894ba825e63615e3be7524fac23858356afa7a38906c/jiter-0.16.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:41977aa5654023948c2dae2a81cbf9c43343954bef1cd59a154dd15a4d84c195", size = 306203, upload-time = "2026-06-29T13:03:36.243Z" }, + { url = "https://files.pythonhosted.org/packages/d0/2b/c3eaf16f5d7c9bad66ea32f40a95bd169b29a91217fcc7f081375157e99c/jiter-0.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d28bb3c26762358dadf3e5bf0bccd29ae987d65e6988d2e6f49829c76b003c09", size = 306489, upload-time = "2026-06-29T13:03:37.846Z" }, + { url = "https://files.pythonhosted.org/packages/96/3f/02fdfc6705cad96127d883af5c34e4867f554f29ec7705ec1a46156400a9/jiter-0.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0542a7189c26920778658fc8fcf2af8bae05bae9924577f71804acef37996536", size = 335453, upload-time = "2026-06-29T13:03:39.221Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a6/e4bda5920d4b0d7c5dfb7174ce4a6b2e4d3e11c9162c452ef0eab4cdbdbd/jiter-0.16.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8fb8de1e23a0cb2a7f53c335049c7b72b6db41aa6227cdcc0972a1de5cb39450", size = 361625, upload-time = "2026-06-29T13:03:40.597Z" }, + { url = "https://files.pythonhosted.org/packages/b7/97/4e6b59b2c6e55cbb3e183595f81ad65dcfb21c915fee5e19e335df21bc55/jiter-0.16.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b72d0b2990ca754a9102779ac98d8597b7cb31678958562214a007f909eab78e", size = 456958, upload-time = "2026-06-29T13:03:42.074Z" }, + { url = "https://files.pythonhosted.org/packages/15/e0/97e9557686d2f94f4b93786eccb7eed28e9228ad132ea8237f44727314a7/jiter-0.16.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f91b1c27fc22a57993d5a5cb8a627cb8ed4b10502716fac1ffbfe1d19d84e8", size = 372017, upload-time = "2026-06-29T13:03:43.658Z" }, + { url = "https://files.pythonhosted.org/packages/0f/94/db768b6938e0df35c86beeba3dfbbb025c9ee5c19e1aa271f2396e50864d/jiter-0.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c682bea068a90b764577bdb78a60a4c1d1606daf9cd4c893832a37c7cc9d9026", size = 343320, upload-time = "2026-06-29T13:03:45.226Z" }, + { url = "https://files.pythonhosted.org/packages/c1/d6/5a59d938244a30735fe62d9433fd325f9021ea29d89780ea4596ea93bc89/jiter-0.16.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:8d031aabecc4f1b6276adfb42e3aabb77c89d468bf616600e8d3a11328929053", size = 350520, upload-time = "2026-06-29T13:03:46.671Z" }, + { url = "https://files.pythonhosted.org/packages/67/f8/c4a857f49c9af125f6bbcac7e3eee7f7978ed89682833062e2dbf62576b1/jiter-0.16.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eab2cd170150e70153de16896a1774e3a1dca80154c56b54d7a812c479a7165e", size = 387550, upload-time = "2026-06-29T13:03:48.361Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d6/5fbc2f7d6b67b754caa61a993a2e626e815dec47ffc2f9e35f01adfebec7/jiter-0.16.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6edb63a46e65a82c26800a868e49b2cac30dd5a4218b88d74bc2c848c8ad60bb", size = 515424, upload-time = "2026-06-29T13:03:49.881Z" }, + { url = "https://files.pythonhosted.org/packages/ed/54/284f0164b64a5fed915fea6ba7e9ba9b3d8d37c67d59cf2e3bb99d45cdfe/jiter-0.16.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:659039cc50b5addcc35fcc87ae2c1833b7c0a8e5326ef631a75e4478447bcf84", size = 546981, upload-time = "2026-06-29T13:03:51.363Z" }, + { url = "https://files.pythonhosted.org/packages/13/c5/2a467585a576594384e1d2c43e1224deaafc085f24e243529cf98beef8e1/jiter-0.16.0-cp313-cp313-win32.whl", hash = "sha256:c9c53be232c2e206ef9cdbad81a48bfa74c3d3f08bcf8124630a8a748aad993e", size = 202853, upload-time = "2026-06-29T13:03:53.015Z" }, + { url = "https://files.pythonhosted.org/packages/88/6a/de61d04b9eec69c71719968d2f716532a3bc121170c44a39e14979c6be81/jiter-0.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:baad945ed47f163ad833314f8e3288c396118934f94e7bbb9e243ce4b341a4fd", size = 196160, upload-time = "2026-06-29T13:03:54.447Z" }, + { url = "https://files.pythonhosted.org/packages/19/4b/b390ed59bafb3f31d008d1218578f10327714484b334439947f7e5b11e7f/jiter-0.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:3c1fd2dbe1b0af19e987f03fe66c5f5bd105a2229c1aff4ab14890b24f41d21a", size = 189862, upload-time = "2026-06-29T13:03:55.754Z" }, + { url = "https://files.pythonhosted.org/packages/a7/89/bc4f1b57d5da938fd344a466396541e586d161320d70bffd929aaafcd8f4/jiter-0.16.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:b2c61484666ad42726029af0c00ef4541f0f3b5cdc550221f56c2343208018ee", size = 308239, upload-time = "2026-06-29T13:03:57.205Z" }, + { url = "https://files.pythonhosted.org/packages/65/7a/c415453e5213001bf3b411ff65dec3d303b0e76a4a2cfea9768cd4960994/jiter-0.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:63efadc657488f45db1c676d81e704cac2abf3fdb892def1faea61db053127e2", size = 308928, upload-time = "2026-06-29T13:03:58.643Z" }, + { url = "https://files.pythonhosted.org/packages/11/fc/1f4fb7ebf9a724c7741994f4aae18fba1e2f3133df14521a79194952c34a/jiter-0.16.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf0d73f50e7b6935677854f6e8e31d499ca7064dd24734f703e060f5b237d883", size = 336998, upload-time = "2026-06-29T13:04:00.071Z" }, + { url = "https://files.pythonhosted.org/packages/a0/8d/72cadaac05ccfa7cc3a0a2232862e6c72443ca40cf300ba8b57f9f18b69b/jiter-0.16.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf3ea07d9bc8e7d03a9fbc051295462e6dbc295b894fd72457c3136e3e43d898", size = 362112, upload-time = "2026-06-29T13:04:01.52Z" }, + { url = "https://files.pythonhosted.org/packages/58/4a/c4b0d5f651fda90a24ffce9f8d56cde462a2e09d31ae3de3c68cef34c04e/jiter-0.16.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26798522707abb47d767db536e4148ceac1b14446bf028ee85e579a2e043cfe5", size = 459807, upload-time = "2026-06-29T13:04:03.214Z" }, + { url = "https://files.pythonhosted.org/packages/80/58/ef77879ea9aa56b50824edc5a445e226422c7a8d211f3fd2a56bcb9493cf/jiter-0.16.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bc837c1b9631be10abfe0191537fe8009838204cec7e44827401ace390ddb567", size = 373181, upload-time = "2026-06-29T13:04:04.629Z" }, + { url = "https://files.pythonhosted.org/packages/49/2e/ffbc3f254e4d8a66da3062c624a7df4b7c2b2cf9e1fe43cf394b3e104041/jiter-0.16.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49060fd70737fad59d33ba9dcc0d83247dc9e77187de26053a19c16c9f32bd69", size = 344927, upload-time = "2026-06-29T13:04:06.067Z" }, + { url = "https://files.pythonhosted.org/packages/9a/f6/0be5dc6d64a89f80aa8fec984f94dedb2973e251edcae55841d60786d578/jiter-0.16.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:adbb8edeadd431bc4477879d5d371ece7cb1334486584e0f252656dd7ffada29", size = 352754, upload-time = "2026-06-29T13:04:07.477Z" }, + { url = "https://files.pythonhosted.org/packages/da/6e/7d31243b3b91cd261dd19e9d3557fc3251a80883d3d8049c86174e7ab7af/jiter-0.16.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:31aaee5b80f672c1dc21272bcfb9cbdcfc1ea04ff50f00ed5af500b80c44fa93", size = 390553, upload-time = "2026-06-29T13:04:08.92Z" }, + { url = "https://files.pythonhosted.org/packages/25/33/51ae371fde3c88897520f62b4d5f8b27ad7103e2bb10812ff52195609853/jiter-0.16.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:6722bcef4ffc86c835574b1b2fac6b33b9fb4a889c781e67950e891591f3c55a", size = 516900, upload-time = "2026-06-29T13:04:10.407Z" }, + { url = "https://files.pythonhosted.org/packages/a0/45/6449b3d123ea439ba79507c657288f461d55049e7bcbdc2cf8eb8210f491/jiter-0.16.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:5ab4f50ff971b611d656554ea10b75f80097392c827bc32923c6eeb6386c8b00", size = 548754, upload-time = "2026-06-29T13:04:12.046Z" }, + { url = "https://files.pythonhosted.org/packages/9b/e7/fd2fb11ae3e2649333da3aa170d04d7b3000bbdc3b270f6513382fdf4e04/jiter-0.16.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:710cc51d4ebdcd3c1f70b232c1db1ea1344a075770422bbd4bede5708335acbe", size = 122381, upload-time = "2026-06-29T13:04:13.413Z" }, + { url = "https://files.pythonhosted.org/packages/26/80/f0b147a62c315a164ed2168908286ca302310824c218d3aae52b06c0c9a9/jiter-0.16.0-cp314-cp314-win32.whl", hash = "sha256:57b37fc887a32d44798e4d8ebfa7c9683ff3da1d5bf38f08d1bb3573ccb39106", size = 204578, upload-time = "2026-06-29T13:04:14.813Z" }, + { url = "https://files.pythonhosted.org/packages/5e/e6/4758a14304b4523a6f5adb2419340086aa3593bd4327c2b25b5948a90548/jiter-0.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:cbd18dd5e2df96b580487b5745adf57ef64ad89ba2d9662fc3c19386acce7db8", size = 198154, upload-time = "2026-06-29T13:04:16.272Z" }, + { url = "https://files.pythonhosted.org/packages/26/be/41fa54a2e7ea41d6c99f1dc5b1f0fd4cb474680304b5d268dd518e81da3a/jiter-0.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:a32d2027a9fa67f109ff245a3252ece3ccc32cc56703e1deab6cc846a59e0585", size = 191458, upload-time = "2026-06-29T13:04:17.707Z" }, + { url = "https://files.pythonhosted.org/packages/81/6b/59127338b86d9fe4d99418f5a15118bea778103ee0fe9d9dd7e0af174e95/jiter-0.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2577196f4474ef3fc4779a088a23b0897bbf86f9ea3679c372d45b8383b43207", size = 316739, upload-time = "2026-06-29T13:04:19.663Z" }, + { url = "https://files.pythonhosted.org/packages/2d/95/49461034d5388196d3dabf98748935f017b7785d8f3f5349f834bcc4ed0d/jiter-0.16.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616e89e008a93c01104161c75b4988e58716b01d62307ebfe161e52a56d2a818", size = 340911, upload-time = "2026-06-29T13:04:21.257Z" }, + { url = "https://files.pythonhosted.org/packages/cd/97/a4369f2fb82cb3dda13b98622f31249b2e014b223fe64ee534413ad72294/jiter-0.16.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e2e9efbe042210df657bade597f66d6d75723e3d8f45a12ea6d8167ff8bbce3", size = 361747, upload-time = "2026-06-29T13:04:22.677Z" }, + { url = "https://files.pythonhosted.org/packages/28/51/49b6ed456261646e1906016a6760367a28aacd3c24805e4e5fe64116c1db/jiter-0.16.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f4d9e473a5ce7d27fef8b848df4dc16e283893d3f53b4a585e72c9595f3c284", size = 460225, upload-time = "2026-06-29T13:04:24.441Z" }, + { url = "https://files.pythonhosted.org/packages/33/b5/5689aff4f66c5b60be63106e591dbfcba2190df97d2c9c7cf052361ddb98/jiter-0.16.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d30a4a1c87713060c8d1cc59a7b6c8fb6b8ef0a6900368014c76c87922a2929", size = 373169, upload-time = "2026-06-29T13:04:25.884Z" }, + { url = "https://files.pythonhosted.org/packages/a2/96/3ae1b85ee0d6d6cab254fb7f8da018272b932bbf2d69b07e98aa2a96c746/jiter-0.16.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae96332410f866e5900d809298b1ed82735932986c672495f9701daacd80620", size = 350332, upload-time = "2026-06-29T13:04:27.302Z" }, + { url = "https://files.pythonhosted.org/packages/15/32/c99d7bafd78986556c95bf60ce84c6cc98786eac56066c12d7f828bb6747/jiter-0.16.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:da3d7ec75dc83bb18bca888b5edfae0656a26849056c59e05a7728badd17e7af", size = 353377, upload-time = "2026-06-29T13:04:28.731Z" }, + { url = "https://files.pythonhosted.org/packages/0e/4b/f99a8e571287c3dec766bcc18528bbe8e8fb5365522ab5e6d64c93e87066/jiter-0.16.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ee6162b77d49a9939229df666dfa8af3e656b6701b54c4c84966d740e189264e", size = 387746, upload-time = "2026-06-29T13:04:30.319Z" }, + { url = "https://files.pythonhosted.org/packages/75/69/c78a5b3f71040e34eb5917df26fb7ae9a2174cad1ccbf277512507c53a6e/jiter-0.16.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:63ffdbdae7d4499f4cda14eadc12ddcabef0fc0c081191bdc2247489cb698077", size = 517292, upload-time = "2026-06-29T13:04:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f7/095b38eda4c70d03651c403f29a5590f16d12ddc5d544aac9f9cddf72277/jiter-0.16.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a111256a7193bea0759267b10385e5870949c239ed7b6ddbaaf57573edb38734", size = 549259, upload-time = "2026-06-29T13:04:33.721Z" }, + { url = "https://files.pythonhosted.org/packages/2e/c5/6a0207d90e5f656d95af98ebd0934f382d37674416f215aeda2ff8063e51/jiter-0.16.0-cp314-cp314t-win32.whl", hash = "sha256:de5ba8763e56b793561f43bed197c9ea55776daa5e9a6b91eed68a909bc9cdbf", size = 206523, upload-time = "2026-06-29T13:04:35.068Z" }, + { url = "https://files.pythonhosted.org/packages/a5/31/c757d5f30a8980fd945ce7b98be10be9e4ff59c7c42f5fd86804c2e87db8/jiter-0.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b8a3f9a6008048fe9def7bf465180564a6e458047d2ce499149cfbe73c3ae9db", size = 200366, upload-time = "2026-06-29T13:04:36.61Z" }, + { url = "https://files.pythonhosted.org/packages/7c/a2/d88de6d313d734a544a7901353ad5db67cb38dcfcd91713b7979dafc345d/jiter-0.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0fa25b09b13075c46f5bc174f2690525a925a4fc2f7c82969a2bbabff22386ce", size = 190516, upload-time = "2026-06-29T13:04:38.004Z" }, + { url = "https://files.pythonhosted.org/packages/06/d3/8e278946d43eeca2585b4dd0834a887cd71136329b837f3a16ed86a8b4b0/jiter-0.16.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:850ccb1d7eedb4200f4014b1c0e8a577de114fc3cd88faad646dcc9bc4bb12ad", size = 304518, upload-time = "2026-06-29T13:05:00.172Z" }, + { url = "https://files.pythonhosted.org/packages/72/43/28d4ef495028bf0506a413d4db3f4eb3e7288a382e0f065f306a17bbeb5e/jiter-0.16.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:e34e97bda77eb63242a410243c071e28ac7e0d8c0948c5ee658498690a4b2f2f", size = 310207, upload-time = "2026-06-29T13:05:02.123Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ca/c366b1012da1d640de975d9683acd44e4d150d9068845d0ca2610435253f/jiter-0.16.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7dc85ea77d4abbae8bad0d3538678aedee75bceec4e2f6c8dfb1c74772e5aa5", size = 342771, upload-time = "2026-06-29T13:05:03.55Z" }, + { url = "https://files.pythonhosted.org/packages/16/52/50cc4056fc1ae02e7154704e7ecc89df0afb8300222cfe8a52d3f67e4730/jiter-0.16.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17ca7fae79f6d99cd9a042b75f917eaada7b895cfc7dd2ee3a16089dcaec7a85", size = 346468, upload-time = "2026-06-29T13:05:05.452Z" }, + { url = "https://files.pythonhosted.org/packages/98/ab/664fd8c4be028b2bedd3d2ff08769c4ede23d0dbc87a77c62384a0515b5d/jiter-0.16.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:f17d61a28b4b3e0e3e2ba98490c70501403b4d196f78732439160e7fd3678127", size = 303106, upload-time = "2026-06-29T13:05:07.118Z" }, + { url = "https://files.pythonhosted.org/packages/1a/07/421f1d5b65493a76e16027b848aba6a7d28073ae75944fa4289cc914d39f/jiter-0.16.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:96e38eea538c8ddf853a35727c7be0741c76c13f04148ac5c116222f50ece3b3", size = 304658, upload-time = "2026-06-29T13:05:08.708Z" }, + { url = "https://files.pythonhosted.org/packages/0a/db/bba1155f01a01c3c37a89425d571da751bbedf5c54247b831a04cb971798/jiter-0.16.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d284fb8d94d5855d60c44fefcab4bf966f1da6fada73992b01f6f0c9bc0c6702", size = 339719, upload-time = "2026-06-29T13:05:10.41Z" }, + { url = "https://files.pythonhosted.org/packages/78/f7/18a1afcd64f35314b68c1f23afcd9994d0bc13e65cc77517afff4e83986d/jiter-0.16.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d613743df53199b1aa256a7d328340da6d7078aac7705a7db9d7a791e9cfd2", size = 343885, upload-time = "2026-06-29T13:05:12.087Z" }, ] [[package]] @@ -2453,14 +1865,14 @@ wheels = [ [[package]] name = "joserfc" -version = "1.7.1" +version = "1.7.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/44/90/25cb27518750218e4f850be63d8bbb2343efaad1c01c3571aaa4b3c33bd7/joserfc-1.7.1.tar.gz", hash = "sha256:77d0b76514879c68c6f433bc5b7357a4ab72008ff1e33d8379fd11d72bd8ca81", size = 233181, upload-time = "2026-06-08T07:21:33.412Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/c6/b1cac0280f8efc57626ea8804866b37099f23cae11b1485a42b213245e31/joserfc-1.7.3.tar.gz", hash = "sha256:116955c2587139dba20621fd0bd7fc9255fa960c9fe7f43c43ebef2e801dcfcf", size = 233821, upload-time = "2026-07-08T12:41:42.66Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/00/fa62404c3e347f946faa13aa21085205f9cc06ad17671e37f81a51662ae8/joserfc-1.7.1-py3-none-any.whl", hash = "sha256:b3e3d655612e2e1ef67b2600f2f420e12e537b020208fab1761fad647319c164", size = 70423, upload-time = "2026-06-08T07:21:32.001Z" }, + { url = "https://files.pythonhosted.org/packages/7d/f5/650b59d1b74f5befb7a7a7e7d7c92a26b94256df3541e2b4914152cd177a/joserfc-1.7.3-py3-none-any.whl", hash = "sha256:7c39f3f2c943dbc03122747fa8ebbd8e156e54904cf25651b452f4d2634a6075", size = 70982, upload-time = "2026-07-08T12:41:41.521Z" }, ] [[package]] @@ -2502,7 +1914,7 @@ dependencies = [ { name = "jsonschema-specifications" }, { name = "referencing" }, { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "rpds-py", version = "2026.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "rpds-py", version = "2026.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } wheels = [ @@ -2554,9 +1966,37 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, ] +[[package]] +name = "langchain" +version = "1.3.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "langchain-core" }, + { name = "langgraph" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/29/68/a6dbad9c22df4087a0f9e79ddd46226c442b30128bfeee538d5889492a73/langchain-1.3.14.tar.gz", hash = "sha256:1b6696c72ba3bbbce54d745e0180742c9f6ece8bbc59ed5a46c3e20b9a435929", size = 645181, upload-time = "2026-07-16T13:28:18.29Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/ec/0f942e78a621f8e3162ff1ed24284f469aaf51fb4607ee5831c626f2b2bc/langchain-1.3.14-py3-none-any.whl", hash = "sha256:4d10dbe91005952cddd56d0dc77aa108964da6bae90ab20063653957e901f782", size = 139560, upload-time = "2026-07-16T13:28:16.498Z" }, +] + +[[package]] +name = "langchain-anthropic" +version = "1.4.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anthropic" }, + { name = "langchain-core" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/22/40ab129b08329ca295b391aa1d48267692b42594757084c6918e22b655ac/langchain_anthropic-1.4.8.tar.gz", hash = "sha256:c76891b2044d56105ff13c106ed12650637b53bd598a4bdf15b4796eefa2a4ec", size = 708524, upload-time = "2026-06-26T21:28:46.916Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/14/746235c4da89d9bc6a608c5f489f628e03feb8f697195c146e452c8f23c8/langchain_anthropic-1.4.8-py3-none-any.whl", hash = "sha256:778e9301b6fd517824f76ec1776975ce8add97a1f6a36c50ae3c2f4b03a66f7f", size = 52366, upload-time = "2026-06-26T21:28:45.535Z" }, +] + [[package]] name = "langchain-core" -version = "1.4.7" +version = "1.4.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, @@ -2569,26 +2009,41 @@ dependencies = [ { name = "typing-extensions" }, { name = "uuid-utils" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ee/2b/fffaff399d20a56d40b9562fa19701e91abd72d8c9d9bc8c2673077b56b6/langchain_core-1.4.7.tar.gz", hash = "sha256:7a825d77de0a3f39adbd9d09612a75e85527e14a52c1601089bcc062972d9f2b", size = 952522, upload-time = "2026-06-12T19:23:57.588Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/b9/e937d0a90b26540bff07e7a7c64349f3b29c2dcc36257cd1cd3fdce17f2a/langchain_core-1.4.9.tar.gz", hash = "sha256:f8078901145bed0466755277500a5a22822a7b628808c4c0a28d4fc88895fcf2", size = 967294, upload-time = "2026-07-08T20:06:54.191Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3e/dcdffa60078ae7b3a00ebb4cbbf1a204a14c3609983c604886523a7d4418/langchain_core-1.4.7-py3-none-any.whl", hash = "sha256:bcadd51951140ecdcba98311dbd931ba5de02a5ba8a2288dad5069c1eea2a13d", size = 554941, upload-time = "2026-06-12T19:23:55.826Z" }, + { url = "https://files.pythonhosted.org/packages/84/70/ade2fada52772798ef815b6352b59e71b116aa0c32c3aef5be3dc2cbed12/langchain_core-1.4.9-py3-none-any.whl", hash = "sha256:28e3909e2a10cc81504952d795ac0a9e014c0018121ef89d48dd396fa09ec624", size = 558293, upload-time = "2026-07-08T20:06:52.382Z" }, +] + +[[package]] +name = "langchain-google-genai" +version = "4.2.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filetype" }, + { name = "google-genai" }, + { name = "langchain-core" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/0c/bc60dabc362ca7c6ffe8c4bcc2f724c7e566b43eb230cee51419f88f784c/langchain_google_genai-4.2.7.tar.gz", hash = "sha256:03b1463ffe4d42435f43c7870467f2215f684bb46400d2543435d10157c80ac7", size = 281605, upload-time = "2026-07-06T13:51:58.724Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/f9/d73d1e712591723aaddb7a7b1e94978cd2320c29acfe0d26b6169a2f26f0/langchain_google_genai-4.2.7-py3-none-any.whl", hash = "sha256:0d9c388d0e6c629718fca6abb19c6fdca728a9a7873d0324c1ec821288b5b571", size = 70702, upload-time = "2026-07-06T13:51:57.499Z" }, ] [[package]] name = "langchain-protocol" -version = "0.0.17" +version = "0.0.18" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/44/b3/4e2429876c7a35585618caa2b9f9089f7162a6b50562b614ad82ac11c17e/langchain_protocol-0.0.17.tar.gz", hash = "sha256:e7cbe58c205df4b4fd87dc6d5bb23f10e13b236d0e2e1b0b9d05bc2b648f3eea", size = 6026, upload-time = "2026-06-12T18:39:51.923Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d2/59/b5959aea96faa9146e2e49a7a22882b3528c62efafe9a6a95beab30c2305/langchain_protocol-0.0.18.tar.gz", hash = "sha256:ec3e11782f1ed0c9db38e5a9ed01b0e7a0d3fba406faa8aef6594b73c56a63e6", size = 6150, upload-time = "2026-06-18T17:08:26.959Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/0a/a1bfe72c6ec856e99773bbd96c8086421e554b3693d0142b9ea009c6ac92/langchain_protocol-0.0.17-py3-none-any.whl", hash = "sha256:982a08fe152586ed10d4ff3d538c2e0b5766e5f307cdea325e10be3f2c17cae6", size = 7096, upload-time = "2026-06-12T18:39:50.973Z" }, + { url = "https://files.pythonhosted.org/packages/99/2e/d82db9eec13ad0f72e7aaad5c4bc730ab111934fdc83c85523206eb9b0a0/langchain_protocol-0.0.18-py3-none-any.whl", hash = "sha256:70b53a86fbf9cedc863555effe44da192ab02d556ddbf2cf95b8873adcf41b5a", size = 7221, upload-time = "2026-06-18T17:08:25.996Z" }, ] [[package]] name = "langgraph" -version = "1.2.5" +version = "1.2.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, @@ -2598,9 +2053,9 @@ dependencies = [ { name = "pydantic" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/9d/7c9ebd17b95569122e2d2e641f535cf086c870d66bb8e59be33cdba856b3/langgraph-1.2.5.tar.gz", hash = "sha256:09a3bdec6fdb3228623fc78b6f69a1400d383f66348d0b04d0efb692022cc6ef", size = 712532, upload-time = "2026-06-12T20:30:58.498Z" } +sdist = { url = "https://files.pythonhosted.org/packages/41/4b/0d1130e26b41a99dcc88353bbe7162a1f255c4db746bd94024268e6af27b/langgraph-1.2.9.tar.gz", hash = "sha256:385f87bc1802c35af7e0aa479278ecba8582d103515eb48256cb2ddcd42d0bd4", size = 722869, upload-time = "2026-07-10T01:30:14.985Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/03/187281cf61845c5a9c397ae6cd9cd73bb54b39435e5575a7b83c853e5b76/langgraph-1.2.5-py3-none-any.whl", hash = "sha256:9286bb5def82fc865959c14378fe473518dc097d586225f622f029637a2a4bb9", size = 246150, upload-time = "2026-06-12T20:30:57.018Z" }, + { url = "https://files.pythonhosted.org/packages/41/16/0b8dc48823f1326f3e0c8012a3c07a40da6f194299e2ec080df236287baf/langgraph-1.2.9-py3-none-any.whl", hash = "sha256:c2d98ad94333937922ba04148641c1da2bfe45b5b8e55d7b6dcb0bb2df809e76", size = 247473, upload-time = "2026-07-10T01:30:13.733Z" }, ] [[package]] @@ -2647,7 +2102,7 @@ wheels = [ [[package]] name = "langsmith" -version = "0.8.16" +version = "0.8.18" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -2661,9 +2116,9 @@ dependencies = [ { name = "xxhash" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6f/19/1ed2af9c6d5d7a148e6b3e809b0af8ce8848e1f66a0726c8223d30e5292b/langsmith-0.8.16.tar.gz", hash = "sha256:8c943f0c9185fe2a9637b5b442828b7efd823b1de28d50d14c136c79660f909b", size = 4513275, upload-time = "2026-06-15T17:41:24.413Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/d9/a6681aa9847bbbc5ec21abe20a5e233b94e5edcfe39624db607ac7e8ccb4/langsmith-0.8.18.tar.gz", hash = "sha256:32dde9c0e67e053e0fb738921fc8ced768af7b8fa83d7a0e3fd63597cf8776dd", size = 4526988, upload-time = "2026-06-19T13:12:17.123Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/13/8186a9867c67f3fef9958a1d60b45f46c1a9b5d28f67d8fd136f28ceab3f/langsmith-0.8.16-py3-none-any.whl", hash = "sha256:081e57c0175d142192683288740a796eb0eb32d9e703b4bf9133678ceefe3286", size = 500303, upload-time = "2026-06-15T17:41:22.33Z" }, + { url = "https://files.pythonhosted.org/packages/03/70/0e0cc80a3b064c8d6c8d697c3125ed86e39d5a7393ec6dc8b07cb1cf13c4/langsmith-0.8.18-py3-none-any.whl", hash = "sha256:3940183349993faef48e6c7d08e4822ee9cefd906b362d0e3c2d650314d2f282", size = 508108, upload-time = "2026-06-19T13:12:15.348Z" }, ] [[package]] @@ -2713,7 +2168,7 @@ wheels = [ [[package]] name = "litellm" -version = "1.89.0" +version = "1.91.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -2729,9 +2184,9 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/4b/15d4cb75f054933c1f19bcfd5683e139cdf792099b995ae55916b26094dc/litellm-1.89.0.tar.gz", hash = "sha256:eb1910a23497044b4375a0500c65f4c60d291a575d7b679c7566a5df9b9a5fcb", size = 14062606, upload-time = "2026-06-13T23:45:53.723Z" } +sdist = { url = "https://files.pythonhosted.org/packages/86/1e/90cfeada42170986a290feebaca0a90923b3c310cddeb0f8690abd239ad4/litellm-1.91.0.tar.gz", hash = "sha256:4fd469fe7356ba8fcc86f4efdf332e3426b760962ab12331fdaf1a01aeec065f", size = 14872290, upload-time = "2026-07-04T19:18:28.466Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/86/49cf94af8c51cacc15fd9bff1e6f9de1fb07ab10b8bb09961675ab389af4/litellm-1.89.0-py3-none-any.whl", hash = "sha256:63b33e2de386ab2a83fed7ed852c755e59d461a21b16c79fc17993f1b8c3d154", size = 15475805, upload-time = "2026-06-13T23:45:46.037Z" }, + { url = "https://files.pythonhosted.org/packages/a4/15/81fc2d162513803fe08b359c2e2a98f7a33195034fb94b005e263e272c6b/litellm-1.91.0-py3-none-any.whl", hash = "sha256:c3eb52dd2c6a5779e9efd67350f3640f9055d9c05d4b572fc1dd01a217e562ad", size = 16669331, upload-time = "2026-07-04T19:18:25.203Z" }, ] [[package]] @@ -2743,18 +2198,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/8b/bf975fabd26195915ebdf3e4252baa936f1863bcd9eb49598b705638f5d5/lunr-0.8.0-py3-none-any.whl", hash = "sha256:a2bc4e08dbb35b32723006bf2edbe6dc1f4f4b95955eea0d23165a184d276ce8", size = 35211, upload-time = "2025-03-08T13:31:38.657Z" }, ] -[[package]] -name = "mako" -version = "1.3.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/00/62/791b31e69ae182791ec67f04850f2f062716bbd205483d63a215f3e062d3/mako-1.3.12.tar.gz", hash = "sha256:9f778e93289bd410bb35daadeb4fc66d95a746f0b75777b942088b7fd7af550a", size = 400219, upload-time = "2026-04-28T19:01:08.512Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/b1/a0ec7a5a9db730a08daef1fdfb8090435b82465abbf758a596f0ea88727e/mako-1.3.12-py3-none-any.whl", hash = "sha256:8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9", size = 78521, upload-time = "2026-04-28T19:01:10.393Z" }, -] - [[package]] name = "markdown-it-py" version = "4.2.0" @@ -2769,15 +2212,15 @@ wheels = [ [[package]] name = "markdownify" -version = "1.2.2" +version = "1.2.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beautifulsoup4" }, { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/bc/c8c8eea5335341306b0fa7e1cb33c5e1c8d24ef70ddd684da65f41c49c92/markdownify-1.2.2.tar.gz", hash = "sha256:b274f1b5943180b031b699b199cbaeb1e2ac938b75851849a31fd0c3d6603d09", size = 18816, upload-time = "2025-11-16T19:21:18.565Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ab/d1297139c0e2ceb151ae564c8c4f57ac0155d8f1f8b4cbd5d6523c82ea36/markdownify-1.2.3.tar.gz", hash = "sha256:1a176f05522c8a2cb1dd3ab9d307dcdadbed5c26ae717855bfc42b3b6d38d937", size = 18852, upload-time = "2026-06-30T20:27:39.06Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ce/f1e3e9d959db134cedf06825fae8d5b294bd368aacdd0831a3975b7c4d55/markdownify-1.2.2-py3-none-any.whl", hash = "sha256:3f02d3cc52714084d6e589f70397b6fc9f2f3a8531481bf35e8cc39f975e186a", size = 15724, upload-time = "2025-11-16T19:21:17.622Z" }, + { url = "https://files.pythonhosted.org/packages/04/10/fa543d484e8b1199243fe20eedd02cc5af050edebce98a7293a5773df592/markdownify-1.2.3-py3-none-any.whl", hash = "sha256:a189a0bedfd14009030fde5f85bb6f77c56897cb839b5c25315dd7d4e3e290ba", size = 15732, upload-time = "2026-06-30T20:27:38.094Z" }, ] [[package]] @@ -2867,31 +2310,31 @@ wheels = [ [[package]] name = "maturin" -version = "1.14.0" +version = "1.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a7/d0/b7c8b7778cc44df3efbc96eb23acaa995e06ea1a60eb9b02f29858fcbd08/maturin-1.14.0.tar.gz", hash = "sha256:f7f82a6aca4a6c402bf00b99200be199d4874d04b9b9e74e825726a3478bba7f", size = 367010, upload-time = "2026-06-12T00:13:30.811Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/b3/addd877f871fb1860d46d3a4f206ecb10b946c85846805e6367631926fd3/maturin-1.14.1.tar.gz", hash = "sha256:9d6577a62cd08e0ceba7a0db06fb098e0c9b1b3429bad747a4f3a18215a1b3df", size = 369637, upload-time = "2026-06-19T05:19:49.774Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/51/49367dcd8f6ec139e69ef0c695c8ff5075223673382101812b4affa53216/maturin-1.14.0-py3-none-linux_armv6l.whl", hash = "sha256:019ea3ec7e71f4c9759a367d4d21022ed5a3a621a2ce123abf3fb114ab3711ca", size = 10204135, upload-time = "2026-06-12T00:13:34.308Z" }, - { url = "https://files.pythonhosted.org/packages/dd/2a/487ce56c838d25e0ce64350e75ec4e3dc89544c0a6233221c229d6aa1a84/maturin-1.14.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6948a10f5f3470b791f79319be51debdd8bfd1778b36f2409f98e1314bc3859b", size = 19736800, upload-time = "2026-06-12T00:13:40.456Z" }, - { url = "https://files.pythonhosted.org/packages/a8/a5/12f2efc18f419edce3282a93629cba16278bb502135dac95cd04ef7c2eae/maturin-1.14.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:1506e86b1e273a98074a62e281b13f27ac96f8cdef85f7f98d3e3589a9387a23", size = 10201144, upload-time = "2026-06-12T00:13:26.842Z" }, - { url = "https://files.pythonhosted.org/packages/bf/95/3789e72273fd8bc80c33a11c787634b3251c4989d7a7203a92438836d4ff/maturin-1.14.0-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:df10ce4f7ba97fd3423f624f39b94c888ae3e5b470642a91918e1ccec81282fd", size = 10182394, upload-time = "2026-06-12T00:13:13.693Z" }, - { url = "https://files.pythonhosted.org/packages/40/79/15957eb4e055597f217e6310963a9c1371372e63c5b4a3e30803365addd2/maturin-1.14.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:75bcd4468a7fe597652cc2980c6bb16ce4bb8c411e3eb85dac2c4418cef0e95a", size = 10616603, upload-time = "2026-06-12T00:13:22.795Z" }, - { url = "https://files.pythonhosted.org/packages/3e/4b/d1822f88cd5e855640f0e10ee00c39b9be614c1ef2f827e9792332d94b9f/maturin-1.14.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:2d123337e817f8dfe23755d6760139c01104137bb63e9e20c289c547e25ec857", size = 10075309, upload-time = "2026-06-12T00:13:38.274Z" }, - { url = "https://files.pythonhosted.org/packages/c0/82/c1b160d2163e8784489285e82a5c811fdcef3e0704e35b34c1cfe1828de3/maturin-1.14.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:107f84110d890090a01bb1ecd01761fdfae925c23c659ba492c9b83dd179eab4", size = 10024058, upload-time = "2026-06-12T00:13:16.49Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e8/88a9d1872997d4535af10ebe79f550e834880bf613cf8e50b50d2d938e3b/maturin-1.14.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:9a84277aa907961cd47ad26fef1539e79efa30611972eaf7499606e773e991b2", size = 13302073, upload-time = "2026-06-12T00:13:29.027Z" }, - { url = "https://files.pythonhosted.org/packages/4a/13/3f6d28bb7b744558b9bc78c995c1855d7e5ff21ad475f46d9de5c3dab039/maturin-1.14.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:095714b2a904927e3c868a1c5d078257ff0443c5049f7623777352966768306e", size = 10863616, upload-time = "2026-06-12T00:13:32.191Z" }, - { url = "https://files.pythonhosted.org/packages/24/06/39352d2b402efa3a7dd01d4ed197b301ea35eec10208ba2b8c649101f4df/maturin-1.14.0-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:20229d332f87166b930e4ca07cdbee8a1726f2eea87a337610aa25bba3ddf4b4", size = 10399943, upload-time = "2026-06-12T00:13:36.273Z" }, - { url = "https://files.pythonhosted.org/packages/58/77/641504541336240fef3836b2d15a785eaeb33c941fb118513c267dd70840/maturin-1.14.0-py3-none-win32.whl", hash = "sha256:4ba1e3c3f33609f461d587b7549104c81a15fd6d42ba63a73cea9376a1e9876e", size = 8905117, upload-time = "2026-06-12T00:13:18.38Z" }, - { url = "https://files.pythonhosted.org/packages/02/4a/ca247a0c43069b2f48cf783c5b13c3a9eb92c8f596dc7fbdb9f75fea4414/maturin-1.14.0-py3-none-win_amd64.whl", hash = "sha256:cb09a313f097adeb4dda0082277871a28d1bd26615dbadab42e6234b6df6fe69", size = 10309099, upload-time = "2026-06-12T00:13:20.523Z" }, - { url = "https://files.pythonhosted.org/packages/8b/a4/f14a3f6086cc3caaa90d12e832e4aa41de771c310041959f0d35dd4efe17/maturin-1.14.0-py3-none-win_arm64.whl", hash = "sha256:8c1a8188195f5b6ce1aab99ae2d92e342900298f901456b43ca028947fd3b288", size = 9719100, upload-time = "2026-06-12T00:13:24.741Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f0/97c5a5bd9c71653a066c0976a484eaaae50b9369557838a4176b7b0bdaa5/maturin-1.14.1-py3-none-linux_armv6l.whl", hash = "sha256:522292398945442cdafa9daeb2271b2340fbde57027b818f923f88eab04174f8", size = 10207496, upload-time = "2026-06-19T05:19:09.321Z" }, + { url = "https://files.pythonhosted.org/packages/fe/83/294bca639b0e052f1e2f65199b3db258780c7d4e31408b934c9c974a1379/maturin-1.14.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ffe5ad71f21d1e6603c4dd75f7fee34adf5ed5ebcebb692886549888ebb329ed", size = 19680113, upload-time = "2026-06-19T05:19:13.43Z" }, + { url = "https://files.pythonhosted.org/packages/43/b6/79c881410a3b1c187f7eb3d407aecae646c6a4433d630d72200359015e83/maturin-1.14.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f3306078070c1508fd715b9116070cbcaff5959024272a9f1e6f5cb29768b86c", size = 10169205, upload-time = "2026-06-19T05:19:16.615Z" }, + { url = "https://files.pythonhosted.org/packages/93/9d/44b6f26dcb7f7a04c5501ac2dbb6ca1490150682baa525ca5860504f9eab/maturin-1.14.1-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:cd457cd88961156e26379e1155bd287cc0ec1c8b2f1582b0660fb31b87c8842d", size = 10188098, upload-time = "2026-06-19T05:19:19.736Z" }, + { url = "https://files.pythonhosted.org/packages/1a/bd/9c0d5d6983905ce2c9edaa073a7e89355a9cf7f396988e05d32f1c37785d/maturin-1.14.1-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:dfc54ae32e6fcb18302193ab9a30b0b25eefffba994ae13238974805533ef75e", size = 10627576, upload-time = "2026-06-19T05:19:22.713Z" }, + { url = "https://files.pythonhosted.org/packages/e5/33/b096412bd6a7cb399652b260666f901adf88a687181a6dbd6a3f89f0a94e/maturin-1.14.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:a131d912b5267e640bc96d70f4914e10590aed64082ec9abacba7cea52004224", size = 10085181, upload-time = "2026-06-19T05:19:25.69Z" }, + { url = "https://files.pythonhosted.org/packages/56/8d/08c3bf469c38a23c9e6c877e338193001eb604d010fedc08341974e38528/maturin-1.14.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:be18fc568fb76884c0205456336892a75105ec398e6b667cd777c6268bd06d69", size = 10026363, upload-time = "2026-06-19T05:19:28.904Z" }, + { url = "https://files.pythonhosted.org/packages/3a/a4/c4d1a92839f8745ab4aab988a7db884a79d6d710bd3b286fcf9316dece1a/maturin-1.14.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:994a0c8ba3ad8a92b3a9ee1b02645d200d610216b15cff5102b0fe65e8e08666", size = 13321347, upload-time = "2026-06-19T05:19:32.411Z" }, + { url = "https://files.pythonhosted.org/packages/b3/fa/170f04624d03fd07d2a8b1b67de83a127af93aef9eaa425839553347297b/maturin-1.14.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:be80866363e605d137991b491a741a84cde9ae350183c4c85f49690ca9aaaa65", size = 10877609, upload-time = "2026-06-19T05:19:35.448Z" }, + { url = "https://files.pythonhosted.org/packages/61/ad/1ae2e1d0ded282bf2c55ac13f0811d87deb425e200ae64a15785675dede9/maturin-1.14.1-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:5282dffd4b539d2be245f4e5b1a5ab6bc1033b58f4a4872f5833f9d43c954aa4", size = 10417316, upload-time = "2026-06-19T05:19:38.28Z" }, + { url = "https://files.pythonhosted.org/packages/fb/27/bf677183920718da49cd7982d6a3ffc440aad8919329f571d189f81b7bdf/maturin-1.14.1-py3-none-win32.whl", hash = "sha256:1a04de0a20188f95c721b5702eed18140bdcccb28c386797093eca3f62f4d4e0", size = 8931293, upload-time = "2026-06-19T05:19:41.183Z" }, + { url = "https://files.pythonhosted.org/packages/63/4b/585adeb9167b08d3cdff0032a938b0e72655c92003df4f52c3f696a1bcc2/maturin-1.14.1-py3-none-win_amd64.whl", hash = "sha256:3c9f94640ecc4895e94abaf834a0684430032c865b2748a36c12461fd9252fdd", size = 10314067, upload-time = "2026-06-19T05:19:44.389Z" }, + { url = "https://files.pythonhosted.org/packages/51/d4/dac8c0720ae246be1700afb6fbdbbea20fe35b13f6570b2f70faa005df77/maturin-1.14.1-py3-none-win_arm64.whl", hash = "sha256:15cea8fcb3ba47dd636f50092bb34baea8b04ac777392f23e6bf8a9a61efb894", size = 9718943, upload-time = "2026-06-19T05:19:47.49Z" }, ] [[package]] name = "mcp" -version = "1.27.2" +version = "1.28.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2909,9 +2352,9 @@ dependencies = [ { name = "typing-inspection" }, { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/27/3c/347cf965d313f5d41764e7d46bea6ffe7d9ef13b983cc429b0340962a082/mcp-1.27.2.tar.gz", hash = "sha256:8e02db104096d1c25b28e64bde29a5c32b31bc241710213e12fd4d84985bdfef", size = 621116, upload-time = "2026-05-29T17:16:04.039Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/77/9450b8f251a13affb6281997d0523c4615f8a8b35d0b21ff30db3a5aac9d/mcp-1.28.1.tar.gz", hash = "sha256:d51e36a5f5644faea4f85ea649bfffa6bc6c26770d42798ad6a3de3d2ba69683", size = 638501, upload-time = "2026-06-26T12:57:29.093Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/11/252c6f971dc4f16af1d98a1c469d8ba523aab00d1bb76b4d3bc1ff32eacc/mcp-1.27.2-py3-none-any.whl", hash = "sha256:d6ff5160c6ca65d93013626efb3fc249de683c30b2d8570755ceddd490344de5", size = 220498, upload-time = "2026-05-29T17:16:02.442Z" }, + { url = "https://files.pythonhosted.org/packages/e2/5e/d118fce19f87a2e7d8101c35c8ae0ec289098a4df0ff244cec23e415aca0/mcp-1.28.1-py3-none-any.whl", hash = "sha256:2726bca5e7193f61c5dde8b12500a6de2d9acf6d1a1c0be9e8c2e706437991df", size = 222620, upload-time = "2026-06-26T12:57:27.218Z" }, ] [[package]] @@ -2923,120 +2366,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] -[[package]] -name = "mmh3" -version = "5.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/1a/edb23803a168f070ded7a3014c6d706f63b90c84ccc024f89d794a3b7a6d/mmh3-5.2.1.tar.gz", hash = "sha256:bbea5b775f0ac84945191fb83f845a6fd9a21a03ea7f2e187defac7e401616ad", size = 33775, upload-time = "2026-03-05T15:55:57.716Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/bb/88ee54afa5644b0f35ab5b435f208394feb963e5bb47c4e404deb625ffa4/mmh3-5.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5d87a3584093e1a89987e3d36d82c98d9621b2cb944e22a420aa1401e096758f", size = 56080, upload-time = "2026-03-05T15:53:40.452Z" }, - { url = "https://files.pythonhosted.org/packages/cc/bf/5404c2fd6ac84819e8ff1b7e34437b37cf55a2b11318894909e7bb88de3f/mmh3-5.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30e4d2084df019880d55f6f7bea35328d9b464ebee090baa372c096dc77556fb", size = 40462, upload-time = "2026-03-05T15:53:41.751Z" }, - { url = "https://files.pythonhosted.org/packages/de/0b/52bffad0b52ae4ea53e222b594bd38c08ecac1fc410323220a7202e43da5/mmh3-5.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0bbc17250b10d3466875a40a52520a6bac3c02334ca709207648abd3c223ed5c", size = 40077, upload-time = "2026-03-05T15:53:42.753Z" }, - { url = "https://files.pythonhosted.org/packages/a0/9e/326c93d425b9fa4cbcdc71bc32aaba520db37577d632a24d25d927594eca/mmh3-5.2.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:76219cd1eefb9bf4af7856e3ae563d15158efa145c0aab01e9933051a1954045", size = 95302, upload-time = "2026-03-05T15:53:43.867Z" }, - { url = "https://files.pythonhosted.org/packages/c6/b1/e20d5f0d19c4c0f3df213fa7dcfa0942c4fb127d38e11f398ae8ddf6cccc/mmh3-5.2.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb9d44c25244e11c8be3f12c938ca8ba8404620ef8092245d2093c6ab3df260f", size = 101174, upload-time = "2026-03-05T15:53:45.194Z" }, - { url = "https://files.pythonhosted.org/packages/7f/4a/1a9bb3e33c18b1e1cee2c249a3053c4d4d9c93ecb30738f39a62249a7e86/mmh3-5.2.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d5d542bf2abd0fd0361e8017d03f7cb5786214ceb4a40eef1539d6585d93386", size = 103979, upload-time = "2026-03-05T15:53:46.334Z" }, - { url = "https://files.pythonhosted.org/packages/ff/8d/dab9ee7545429e7acdd38d23d0104471d31de09a0c695f1b751e0ff34532/mmh3-5.2.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:08043f7cb1fb9467c3fbbbaea7896986e7fbc81f4d3fd9289a73d9110ab6207a", size = 110898, upload-time = "2026-03-05T15:53:47.443Z" }, - { url = "https://files.pythonhosted.org/packages/72/08/408f11af7fe9e76b883142bb06536007cc7f237be2a5e9ad4e837716e627/mmh3-5.2.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:add7ac388d1e0bf57259afbcf9ed05621a3bf11ce5ee337e7536f1e1aaf056b0", size = 118308, upload-time = "2026-03-05T15:53:49.1Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/0551be7fe0000736d9ad12ffa1f130d7a0c17b49193d6dc41c82bd9404c6/mmh3-5.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:41105377f6282e8297f182e393a79cfffd521dde37ace52b106373bdcd9ca5cb", size = 101671, upload-time = "2026-03-05T15:53:50.317Z" }, - { url = "https://files.pythonhosted.org/packages/44/17/6e4f80c4e6ad590139fa2017c3aeca54e7cc9ef68e08aa142a0c90f40a97/mmh3-5.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3cb61db880ec11e984348227b333259994c2c85caa775eb7875decb3768db890", size = 96682, upload-time = "2026-03-05T15:53:51.48Z" }, - { url = "https://files.pythonhosted.org/packages/ad/a7/b82fccd38c1fa815de72e94ebe9874562964a10e21e6c1bc3b01d3f15a0e/mmh3-5.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e8b5378de2b139c3a830f0209c1e91f7705919a4b3e563a10955104f5097a70a", size = 110287, upload-time = "2026-03-05T15:53:52.68Z" }, - { url = "https://files.pythonhosted.org/packages/a8/a1/2644069031c8cec0be46f0346f568a53f42fddd843f03cc890306699c1e2/mmh3-5.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e904f2417f0d6f6d514f3f8b836416c360f306ddaee1f84de8eef1e722d212e5", size = 111899, upload-time = "2026-03-05T15:53:53.791Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/6614f3eb8fb33f931fa7616c6d477247e48ec6c5082b02eeeee998cffa94/mmh3-5.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f1fbb0a99125b1287c6d9747f937dc66621426836d1a2d50d05aecfc81911b57", size = 100078, upload-time = "2026-03-05T15:53:55.234Z" }, - { url = "https://files.pythonhosted.org/packages/27/9a/dd4d5a5fb893e64f71b42b69ecae97dd78db35075412488b24036bc5599c/mmh3-5.2.1-cp310-cp310-win32.whl", hash = "sha256:b4cce60d0223074803c9dbe0721ad3fa51dafe7d462fee4b656a1aa01ee07518", size = 40756, upload-time = "2026-03-05T15:53:56.319Z" }, - { url = "https://files.pythonhosted.org/packages/c9/34/0b25889450f8aeffcec840aa73251e853f059c1b72ed1d1c027b956f95f5/mmh3-5.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:6f01f044112d43a20be2f13a11683666d87151542ad627fe41a18b9791d2802f", size = 41519, upload-time = "2026-03-05T15:53:57.41Z" }, - { url = "https://files.pythonhosted.org/packages/fd/31/8fd42e3c526d0bcb1db7f569c0de6729e180860a0495e387a53af33c2043/mmh3-5.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:7501e9be34cb21e72fcfe672aafd0eee65c16ba2afa9dcb5500a587d3a0580f0", size = 39285, upload-time = "2026-03-05T15:53:58.697Z" }, - { url = "https://files.pythonhosted.org/packages/65/d7/3312a59df3c1cdd783f4cf0c4ee8e9decff9c5466937182e4cc7dbbfe6c5/mmh3-5.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dae0f0bd7d30c0ad61b9a504e8e272cb8391eed3f1587edf933f4f6b33437450", size = 56082, upload-time = "2026-03-05T15:53:59.702Z" }, - { url = "https://files.pythonhosted.org/packages/61/96/6f617baa098ca0d2989bfec6d28b5719532cd8d8848782662f5b755f657f/mmh3-5.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9aeaf53eaa075dd63e81512522fd180097312fb2c9f476333309184285c49ce0", size = 40458, upload-time = "2026-03-05T15:54:01.548Z" }, - { url = "https://files.pythonhosted.org/packages/c1/b4/9cd284bd6062d711e13d26c04d4778ab3f690c1c38a4563e3c767ec8802e/mmh3-5.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0634581290e6714c068f4aa24020acf7880927d1f0084fa753d9799ae9610082", size = 40079, upload-time = "2026-03-05T15:54:02.743Z" }, - { url = "https://files.pythonhosted.org/packages/f6/09/a806334ce1d3d50bf782b95fcee8b3648e1e170327d4bb7b4bad2ad7d956/mmh3-5.2.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e080c0637aea036f35507e803a4778f119a9b436617694ae1c5c366805f1e997", size = 97242, upload-time = "2026-03-05T15:54:04.536Z" }, - { url = "https://files.pythonhosted.org/packages/ee/93/723e317dd9e041c4dc4566a2eb53b01ad94de31750e0b834f1643905e97c/mmh3-5.2.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:db0562c5f71d18596dcd45e854cf2eeba27d7543e1a3acdafb7eef728f7fe85d", size = 103082, upload-time = "2026-03-05T15:54:06.387Z" }, - { url = "https://files.pythonhosted.org/packages/61/b5/f96121e69cc48696075071531cf574f112e1ffd08059f4bffb41210e6fc5/mmh3-5.2.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d9f9a3ce559a5267014b04b82956993270f63ec91765e13e9fd73daf2d2738e", size = 106054, upload-time = "2026-03-05T15:54:07.506Z" }, - { url = "https://files.pythonhosted.org/packages/82/49/192b987ec48d0b2aecf8ac285a9b11fbc00030f6b9c694664ae923458dde/mmh3-5.2.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:960b1b3efa39872ac8b6cc3a556edd6fb90ed74f08c9c45e028f1005b26aa55d", size = 112910, upload-time = "2026-03-05T15:54:09.403Z" }, - { url = "https://files.pythonhosted.org/packages/cf/a1/03e91fd334ed0144b83343a76eb11f17434cd08f746401488cfeafb2d241/mmh3-5.2.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d30b650595fdbe32366b94cb14f30bb2b625e512bd4e1df00611f99dc5c27fd4", size = 120551, upload-time = "2026-03-05T15:54:10.587Z" }, - { url = "https://files.pythonhosted.org/packages/93/b9/b89a71d2ff35c3a764d1c066c7313fc62c7cc48fa48a4b3b0304a4a0146f/mmh3-5.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:82f3802bfc4751f420d591c5c864de538b71cea117fce67e4595c2afede08a15", size = 99096, upload-time = "2026-03-05T15:54:11.76Z" }, - { url = "https://files.pythonhosted.org/packages/36/b5/613772c1c6ed5f7b63df55eb131e887cc43720fec392777b95a79d34e640/mmh3-5.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:915e7a2418f10bd1151b1953df06d896db9783c9cfdb9a8ee1f9b3a4331ab503", size = 98524, upload-time = "2026-03-05T15:54:13.122Z" }, - { url = "https://files.pythonhosted.org/packages/5e/0e/1524566fe8eaf871e4f7bc44095929fcd2620488f402822d848df19d679c/mmh3-5.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fc78739b5ec6e4fb02301984a3d442a91406e7700efbe305071e7fd1c78278f2", size = 106239, upload-time = "2026-03-05T15:54:14.601Z" }, - { url = "https://files.pythonhosted.org/packages/04/94/21adfa7d90a7a697137ad6de33eeff6445420ca55e433a5d4919c79bc3b5/mmh3-5.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:41aac7002a749f08727cb91babff1daf8deac317c0b1f317adc69be0e6c375d1", size = 109797, upload-time = "2026-03-05T15:54:15.819Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e6/1aacc3a219e1aa62fa65669995d4a3562b35be5200ec03680c7e4bec9676/mmh3-5.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9d8089d853c7963a8ce87fff93e2a67075c0bc08684a08ea6ad13577c38ffc38", size = 97228, upload-time = "2026-03-05T15:54:16.992Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b9/5e4cca8dcccf298add0a27f3c357bc8cf8baf821d35cdc6165e4bd5a48b0/mmh3-5.2.1-cp311-cp311-win32.whl", hash = "sha256:baeb47635cb33375dee4924cd93d7f5dcaa786c740b08423b0209b824a1ee728", size = 40751, upload-time = "2026-03-05T15:54:18.714Z" }, - { url = "https://files.pythonhosted.org/packages/72/fc/5b11d49247f499bcda591171e9cf3b6ee422b19e70aa2cef2e0ae65ca3b9/mmh3-5.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:1e4ecee40ba19e6975e1120829796770325841c2f153c0e9aecca927194c6a2a", size = 41517, upload-time = "2026-03-05T15:54:19.764Z" }, - { url = "https://files.pythonhosted.org/packages/8a/5f/2a511ee8a1c2a527c77726d5231685b72312c5a1a1b7639ad66a9652aa84/mmh3-5.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:c302245fd6c33d96bd169c7ccf2513c20f4c1e417c07ce9dce107c8bc3f8411f", size = 39287, upload-time = "2026-03-05T15:54:20.904Z" }, - { url = "https://files.pythonhosted.org/packages/92/94/bc5c3b573b40a328c4d141c20e399039ada95e5e2a661df3425c5165fd84/mmh3-5.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0cc21533878e5586b80d74c281d7f8da7932bc8ace50b8d5f6dbf7e3935f63f1", size = 56087, upload-time = "2026-03-05T15:54:21.92Z" }, - { url = "https://files.pythonhosted.org/packages/f6/80/64a02cc3e95c3af0aaa2590849d9ed24a9f14bb93537addde688e039b7c3/mmh3-5.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4eda76074cfca2787c8cf1bec603eaebdddd8b061ad5502f85cddae998d54f00", size = 40500, upload-time = "2026-03-05T15:54:22.953Z" }, - { url = "https://files.pythonhosted.org/packages/8b/72/e6d6602ce18adf4ddcd0e48f2e13590cc92a536199e52109f46f259d3c46/mmh3-5.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eee884572b06bbe8a2b54f424dbd996139442cf83c76478e1ec162512e0dd2c7", size = 40034, upload-time = "2026-03-05T15:54:23.943Z" }, - { url = "https://files.pythonhosted.org/packages/59/c2/bf4537a8e58e21886ef16477041238cab5095c836496e19fafc34b7445d2/mmh3-5.2.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0d0b7e803191db5f714d264044e06189c8ccd3219e936cc184f07106bd17fd7b", size = 97292, upload-time = "2026-03-05T15:54:25.335Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e2/51ed62063b44d10b06d975ac87af287729eeb5e3ed9772f7584a17983e90/mmh3-5.2.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8e6c219e375f6341d0959af814296372d265a8ca1af63825f65e2e87c618f006", size = 103274, upload-time = "2026-03-05T15:54:26.44Z" }, - { url = "https://files.pythonhosted.org/packages/75/ce/12a7524dca59eec92e5b31fdb13ede1e98eda277cf2b786cf73bfbc24e81/mmh3-5.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:26fb5b9c3946bf7f1daed7b37e0c03898a6f062149127570f8ede346390a0825", size = 106158, upload-time = "2026-03-05T15:54:28.578Z" }, - { url = "https://files.pythonhosted.org/packages/86/1f/d3ba6dd322d01ab5d44c46c8f0c38ab6bbbf9b5e20e666dfc05bf4a23604/mmh3-5.2.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3c38d142c706201db5b2345166eeef1e7740e3e2422b470b8ba5c8727a9b4c7a", size = 113005, upload-time = "2026-03-05T15:54:29.767Z" }, - { url = "https://files.pythonhosted.org/packages/b6/a9/15d6b6f913294ea41b44d901741298e3718e1cb89ee626b3694625826a43/mmh3-5.2.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50885073e2909251d4718634a191c49ae5f527e5e1736d738e365c3e8be8f22b", size = 120744, upload-time = "2026-03-05T15:54:30.931Z" }, - { url = "https://files.pythonhosted.org/packages/76/b3/70b73923fd0284c439860ff5c871b20210dfdbe9a6b9dd0ee6496d77f174/mmh3-5.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b3f99e1756fc48ad507b95e5d86f2fb21b3d495012ff13e6592ebac14033f166", size = 99111, upload-time = "2026-03-05T15:54:32.353Z" }, - { url = "https://files.pythonhosted.org/packages/dd/38/99f7f75cd27d10d8b899a1caafb9d531f3903e4d54d572220e3d8ac35e89/mmh3-5.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62815d2c67f2dd1be76a253d88af4e1da19aeaa1820146dec52cf8bee2958b16", size = 98623, upload-time = "2026-03-05T15:54:33.801Z" }, - { url = "https://files.pythonhosted.org/packages/fd/68/6e292c0853e204c44d2f03ea5f090be3317a0e2d9417ecb62c9eb27687df/mmh3-5.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8f767ba0911602ddef289404e33835a61168314ebd3c729833db2ed685824211", size = 106437, upload-time = "2026-03-05T15:54:35.177Z" }, - { url = "https://files.pythonhosted.org/packages/dd/c6/fedd7284c459cfb58721d461fcf5607a4c1f5d9ab195d113d51d10164d16/mmh3-5.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:67e41a497bac88cc1de96eeba56eeb933c39d54bc227352f8455aa87c4ca4000", size = 110002, upload-time = "2026-03-05T15:54:36.673Z" }, - { url = "https://files.pythonhosted.org/packages/3b/ac/ca8e0c19a34f5b71390171d2ff0b9f7f187550d66801a731bb68925126a4/mmh3-5.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d74a03fb57757ece25aa4b3c1c60157a1cece37a020542785f942e2f827eed5", size = 97507, upload-time = "2026-03-05T15:54:37.804Z" }, - { url = "https://files.pythonhosted.org/packages/df/94/6ebb9094cfc7ac5e7950776b9d13a66bb4a34f83814f32ba2abc9494fc68/mmh3-5.2.1-cp312-cp312-win32.whl", hash = "sha256:7374d6e3ef72afe49697ecd683f3da12f4fc06af2d75433d0580c6746d2fa025", size = 40773, upload-time = "2026-03-05T15:54:40.077Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/cd3527198cf159495966551c84a5f36805a10ac17b294f41f67b83f6a4d6/mmh3-5.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:3a9fed49c6ce4ed7e73f13182760c65c816da006debe67f37635580dfb0fae00", size = 41560, upload-time = "2026-03-05T15:54:41.148Z" }, - { url = "https://files.pythonhosted.org/packages/15/96/6fe5ebd0f970a076e3ed5512871ce7569447b962e96c125528a2f9724470/mmh3-5.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:bbfcb95d9a744e6e2827dfc66ad10e1020e0cac255eb7f85652832d5a264c2fc", size = 39313, upload-time = "2026-03-05T15:54:42.171Z" }, - { url = "https://files.pythonhosted.org/packages/25/a5/9daa0508a1569a54130f6198d5462a92deda870043624aa3ea72721aa765/mmh3-5.2.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:723b2681ed4cc07d3401bbea9c201ad4f2a4ca6ba8cddaff6789f715dd2b391e", size = 40832, upload-time = "2026-03-05T15:54:43.212Z" }, - { url = "https://files.pythonhosted.org/packages/0a/6b/3230c6d80c1f4b766dedf280a92c2241e99f87c1504ff74205ec8cebe451/mmh3-5.2.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:3619473a0e0d329fd4aec8075628f8f616be2da41605300696206d6f36920c3d", size = 41964, upload-time = "2026-03-05T15:54:44.204Z" }, - { url = "https://files.pythonhosted.org/packages/62/fb/648bfddb74a872004b6ee751551bfdda783fe6d70d2e9723bad84dbe5311/mmh3-5.2.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e48d4dbe0f88e53081da605ae68644e5182752803bbc2beb228cca7f1c4454d6", size = 39114, upload-time = "2026-03-05T15:54:45.205Z" }, - { url = "https://files.pythonhosted.org/packages/95/c2/ab7901f87af438468b496728d11264cb397b3574d41506e71b92128e0373/mmh3-5.2.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a482ac121de6973897c92c2f31defc6bafb11c83825109275cffce54bb64933f", size = 39819, upload-time = "2026-03-05T15:54:46.509Z" }, - { url = "https://files.pythonhosted.org/packages/2f/ed/6f88dda0df67de1612f2e130ffea34cf84aaee5bff5b0aff4dbff2babe34/mmh3-5.2.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:17fbb47f0885ace8327ce1235d0416dc86a211dcd8cc1e703f41523be32cfec8", size = 40330, upload-time = "2026-03-05T15:54:47.864Z" }, - { url = "https://files.pythonhosted.org/packages/3d/66/7516d23f53cdf90f43fce24ab80c28f45e6851d78b46bef8c02084edf583/mmh3-5.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d51fde50a77f81330523562e3c2734ffdca9c4c9e9d355478117905e1cfe16c6", size = 56078, upload-time = "2026-03-05T15:54:48.9Z" }, - { url = "https://files.pythonhosted.org/packages/bc/34/4d152fdf4a91a132cb226b671f11c6b796eada9ab78080fb5ce1e95adaab/mmh3-5.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:19bbd3b841174ae6ed588536ab5e1b1fe83d046e668602c20266547298d939a9", size = 40498, upload-time = "2026-03-05T15:54:49.942Z" }, - { url = "https://files.pythonhosted.org/packages/d4/4c/8e3af1b6d85a299767ec97bd923f12b06267089c1472c27c1696870d1175/mmh3-5.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be77c402d5e882b6fbacfd90823f13da8e0a69658405a39a569c6b58fdb17b03", size = 40033, upload-time = "2026-03-05T15:54:50.994Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f2/966ea560e32578d453c9e9db53d602cbb1d0da27317e232afa7c38ceba11/mmh3-5.2.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fd96476f04db5ceba1cfa0f21228f67c1f7402296f0e73fee3513aa680ad237b", size = 97320, upload-time = "2026-03-05T15:54:52.072Z" }, - { url = "https://files.pythonhosted.org/packages/bb/0d/2c5f9893b38aeb6b034d1a44ecd55a010148054f6a516abe53b5e4057297/mmh3-5.2.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:707151644085dd0f20fe4f4b573d28e5130c4aaa5f587e95b60989c5926653b5", size = 103299, upload-time = "2026-03-05T15:54:53.569Z" }, - { url = "https://files.pythonhosted.org/packages/1c/fc/2ebaef4a4d4376f89761274dc274035ffd96006ab496b4ee5af9b08f21a9/mmh3-5.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3737303ca9ea0f7cb83028781148fcda4f1dac7821db0c47672971dabcf63593", size = 106222, upload-time = "2026-03-05T15:54:55.092Z" }, - { url = "https://files.pythonhosted.org/packages/57/09/ea7ffe126d0ba0406622602a2d05e1e1a6841cc92fc322eb576c95b27fad/mmh3-5.2.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2778fed822d7db23ac5008b181441af0c869455b2e7d001f4019636ac31b6fe4", size = 113048, upload-time = "2026-03-05T15:54:56.305Z" }, - { url = "https://files.pythonhosted.org/packages/85/57/9447032edf93a64aa9bef4d9aa596400b1756f40411890f77a284f6293ca/mmh3-5.2.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d57dea657357230cc780e13920d7fa7db059d58fe721c80020f94476da4ca0a1", size = 120742, upload-time = "2026-03-05T15:54:57.453Z" }, - { url = "https://files.pythonhosted.org/packages/53/82/a86cc87cc88c92e9e1a598fee509f0409435b57879a6129bf3b3e40513c7/mmh3-5.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:169e0d178cb59314456ab30772429a802b25d13227088085b0d49b9fe1533104", size = 99132, upload-time = "2026-03-05T15:54:58.583Z" }, - { url = "https://files.pythonhosted.org/packages/54/f7/6b16eb1b40ee89bb740698735574536bc20d6cdafc65ae702ea235578e05/mmh3-5.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7e4e1f580033335c6f76d1e0d6b56baf009d1a64d6a4816347e4271ba951f46d", size = 98686, upload-time = "2026-03-05T15:55:00.078Z" }, - { url = "https://files.pythonhosted.org/packages/e8/88/a601e9f32ad1410f438a6d0544298ea621f989bd34a0731a7190f7dec799/mmh3-5.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2bd9f19f7f1fcebd74e830f4af0f28adad4975d40d80620be19ffb2b2af56c9f", size = 106479, upload-time = "2026-03-05T15:55:01.532Z" }, - { url = "https://files.pythonhosted.org/packages/d6/5c/ce29ae3dfc4feec4007a437a1b7435fb9507532a25147602cd5b52be86db/mmh3-5.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c88653877aeb514c089d1b3d473451677b8b9a6d1497dbddf1ae7934518b06d2", size = 110030, upload-time = "2026-03-05T15:55:02.934Z" }, - { url = "https://files.pythonhosted.org/packages/13/30/ae444ef2ff87c805d525da4fa63d27cda4fe8a48e77003a036b8461cfd5c/mmh3-5.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fceef7fe67c81e1585198215e42ad3fdba3a25644beda8fbdaf85f4d7b93175a", size = 97536, upload-time = "2026-03-05T15:55:04.135Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f9/dc3787ee5c813cc27fe79f45ad4500d9b5437f23a7402435cc34e07c7718/mmh3-5.2.1-cp313-cp313-win32.whl", hash = "sha256:54b64fb2433bc71488e7a449603bf8bd31fbcf9cb56fbe1eb6d459e90b86c37b", size = 40769, upload-time = "2026-03-05T15:55:05.277Z" }, - { url = "https://files.pythonhosted.org/packages/43/67/850e0b5a1e97799822ebfc4ca0e8c6ece3ed8baf7dcdf64de817dfdda2ca/mmh3-5.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:cae6383181f1e345317742d2ddd88f9e7d2682fa4c9432e3a74e47d92dce0229", size = 41563, upload-time = "2026-03-05T15:55:06.283Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cc/98c90b28e1da5458e19fbfaf4adb5289208d3bfccd45dd14eab216a2f0bb/mmh3-5.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:022aa1a528604e6c83d0a7705fdef0b5355d897a9e0fa3a8d26709ceaa06965d", size = 39310, upload-time = "2026-03-05T15:55:07.323Z" }, - { url = "https://files.pythonhosted.org/packages/63/b4/65bc1fb2bb7f83e91c30865023b1847cf89a5f237165575e8c83aa536584/mmh3-5.2.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:d771f085fcdf4035786adfb1d8db026df1eb4b41dac1c3d070d1e49512843227", size = 40794, upload-time = "2026-03-05T15:55:09.773Z" }, - { url = "https://files.pythonhosted.org/packages/c4/86/7168b3d83be8eb553897b1fac9da8bbb06568e5cfe555ffc329ebb46f59d/mmh3-5.2.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:7f196cd7910d71e9d9860da0ff7a77f64d22c1ad931f1dd18559a06e03109fc0", size = 41923, upload-time = "2026-03-05T15:55:10.924Z" }, - { url = "https://files.pythonhosted.org/packages/bf/9b/b653ab611c9060ce8ff0ba25c0226757755725e789292f3ca138a58082cd/mmh3-5.2.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b1f12bd684887a0a5d55e6363ca87056f361e45451105012d329b86ec19dbe0b", size = 39131, upload-time = "2026-03-05T15:55:11.961Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b4/5a2e0d34ab4d33543f01121e832395ea510132ea8e52cdf63926d9d81754/mmh3-5.2.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d106493a60dcb4aef35a0fac85105e150a11cf8bc2b0d388f5a33272d756c966", size = 39825, upload-time = "2026-03-05T15:55:13.013Z" }, - { url = "https://files.pythonhosted.org/packages/bd/69/81699a8f39a3f8d368bec6443435c0c392df0d200ad915bf0d222b588e03/mmh3-5.2.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:44983e45310ee5b9f73397350251cdf6e63a466406a105f1d16cb5baa659270b", size = 40344, upload-time = "2026-03-05T15:55:14.026Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b3/71c8c775807606e8fd8acc5c69016e1caf3200d50b50b6dd4b40ce10b76c/mmh3-5.2.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:368625fb01666655985391dbad3860dc0ba7c0d6b9125819f3121ee7292b4ac8", size = 56291, upload-time = "2026-03-05T15:55:15.137Z" }, - { url = "https://files.pythonhosted.org/packages/6f/75/2c24517d4b2ce9e4917362d24f274d3d541346af764430249ddcc4cb3a08/mmh3-5.2.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:72d1cc63bcc91e14933f77d51b3df899d6a07d184ec515ea7f56bff659e124d7", size = 40575, upload-time = "2026-03-05T15:55:16.518Z" }, - { url = "https://files.pythonhosted.org/packages/bf/b9/e4a360164365ac9f07a25f0f7928e3a66eb9ecc989384060747aa170e6aa/mmh3-5.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e8b4b5580280b9265af3e0409974fb79c64cf7523632d03fbf11df18f8b0181e", size = 40052, upload-time = "2026-03-05T15:55:17.735Z" }, - { url = "https://files.pythonhosted.org/packages/97/ca/120d92223a7546131bbbc31c9174168ee7a73b1366f5463ffe69d9e691fe/mmh3-5.2.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4cbbde66f1183db040daede83dd86c06d663c5bb2af6de1142b7c8c37923dd74", size = 97311, upload-time = "2026-03-05T15:55:18.959Z" }, - { url = "https://files.pythonhosted.org/packages/b6/71/c1a60c1652b8813ef9de6d289784847355417ee0f2980bca002fe87f4ae5/mmh3-5.2.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8ff038d52ef6aa0f309feeba00c5095c9118d0abf787e8e8454d6048db2037fc", size = 103279, upload-time = "2026-03-05T15:55:20.448Z" }, - { url = "https://files.pythonhosted.org/packages/48/29/ad97f4be1509cdcb28ae32c15593ce7c415db47ace37f8fad35b493faa9a/mmh3-5.2.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4130d0b9ce5fad6af07421b1aecc7e079519f70d6c05729ab871794eded8617", size = 106290, upload-time = "2026-03-05T15:55:21.6Z" }, - { url = "https://files.pythonhosted.org/packages/77/29/1f86d22e281bd8827ba373600a4a8b0c0eae5ca6aa55b9a8c26d2a34decc/mmh3-5.2.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e0bfe77d238308839699944164b96a2eeccaf55f2af400f54dc20669d8d5f2", size = 113116, upload-time = "2026-03-05T15:55:22.826Z" }, - { url = "https://files.pythonhosted.org/packages/a7/7c/339971ea7ed4c12d98f421f13db3ea576a9114082ccb59d2d1a0f00ccac1/mmh3-5.2.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f963eafc0a77a6c0562397da004f5876a9bcf7265a7bcc3205e29636bc4a1312", size = 120740, upload-time = "2026-03-05T15:55:24.3Z" }, - { url = "https://files.pythonhosted.org/packages/e4/92/3c7c4bdb8e926bb3c972d1e2907d77960c1c4b250b41e8366cf20c6e4373/mmh3-5.2.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:92883836caf50d5255be03d988d75bc93e3f86ba247b7ca137347c323f731deb", size = 99143, upload-time = "2026-03-05T15:55:25.456Z" }, - { url = "https://files.pythonhosted.org/packages/df/0a/33dd8706e732458c8375eae63c981292de07a406bad4ec03e5269654aa2c/mmh3-5.2.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:57b52603e89355ff318025dd55158f6e71396c0f1f609d548e9ea9c94cc6ce0a", size = 98703, upload-time = "2026-03-05T15:55:26.723Z" }, - { url = "https://files.pythonhosted.org/packages/51/04/76bbce05df76cbc3d396f13b2ea5b1578ef02b6a5187e132c6c33f99d596/mmh3-5.2.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f40a95186a72fa0b67d15fef0f157bfcda00b4f59c8a07cbe5530d41ac35d105", size = 106484, upload-time = "2026-03-05T15:55:28.214Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8f/c6e204a2c70b719c1f62ffd9da27aef2dddcba875ea9c31ca0e87b975a46/mmh3-5.2.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:58370d05d033ee97224c81263af123dea3d931025030fd34b61227a768a8858a", size = 110012, upload-time = "2026-03-05T15:55:29.532Z" }, - { url = "https://files.pythonhosted.org/packages/e3/37/7181efd8e39db386c1ebc3e6b7d1f702a09d7c1197a6f2742ed6b5c16597/mmh3-5.2.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7be6dfb49e48fd0a7d91ff758a2b51336f1cd21f9d44b20f6801f072bd080cdd", size = 97508, upload-time = "2026-03-05T15:55:31.01Z" }, - { url = "https://files.pythonhosted.org/packages/42/0f/afa7ca2615fd85e1469474bb860e381443d0b868c083b62b41cb1d7ca32f/mmh3-5.2.1-cp314-cp314-win32.whl", hash = "sha256:54fe8518abe06a4c3852754bfd498b30cc58e667f376c513eac89a244ce781a4", size = 41387, upload-time = "2026-03-05T15:55:32.403Z" }, - { url = "https://files.pythonhosted.org/packages/71/0d/46d42a260ee1357db3d486e6c7a692e303c017968e14865e00efa10d09fc/mmh3-5.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:3f796b535008708846044c43302719c6956f39ca2d93f2edda5319e79a29efbb", size = 42101, upload-time = "2026-03-05T15:55:33.646Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7b/848a8378059d96501a41159fca90d6a99e89736b0afbe8e8edffeac8c74b/mmh3-5.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:cd471ede0d802dd936b6fab28188302b2d497f68436025857ca72cd3810423fe", size = 39836, upload-time = "2026-03-05T15:55:35.026Z" }, - { url = "https://files.pythonhosted.org/packages/27/61/1dabea76c011ba8547c25d30c91c0ec22544487a8750997a27a0c9e1180b/mmh3-5.2.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:5174a697ce042fa77c407e05efe41e03aa56dae9ec67388055820fb48cf4c3ba", size = 57727, upload-time = "2026-03-05T15:55:36.162Z" }, - { url = "https://files.pythonhosted.org/packages/b7/32/731185950d1cf2d5e28979cc8593016ba1619a295faba10dda664a4931b5/mmh3-5.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0a3984146e414684a6be2862d84fcb1035f4984851cb81b26d933bab6119bf00", size = 41308, upload-time = "2026-03-05T15:55:37.254Z" }, - { url = "https://files.pythonhosted.org/packages/76/aa/66c76801c24b8c9418b4edde9b5e57c75e72c94e29c48f707e3962534f18/mmh3-5.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bd6e7d363aa93bd3421b30b6af97064daf47bc96005bddba67c5ffbc6df426b8", size = 40758, upload-time = "2026-03-05T15:55:38.61Z" }, - { url = "https://files.pythonhosted.org/packages/9e/bb/79a1f638a02f0ae389f706d13891e2fbf7d8c0a22ecde67ba828951bb60a/mmh3-5.2.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:113f78e7463a36dbbcea05bfe688efd7fa759d0f0c56e73c974d60dcfec3dfcc", size = 109670, upload-time = "2026-03-05T15:55:40.13Z" }, - { url = "https://files.pythonhosted.org/packages/26/94/8cd0e187a288985bcfc79bf5144d1d712df9dee74365f59d26e3a1865be6/mmh3-5.2.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e8ec5f606e0809426d2440e0683509fb605a8820a21ebd120dcdba61b74ef7f", size = 117399, upload-time = "2026-03-05T15:55:42.076Z" }, - { url = "https://files.pythonhosted.org/packages/42/94/dfea6059bd5c5beda565f58a4096e43f4858fb6d2862806b8bbd12cbb284/mmh3-5.2.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22b0f9971ec4e07e8223f2beebe96a6cfc779d940b6f27d26604040dd74d3a44", size = 120386, upload-time = "2026-03-05T15:55:43.481Z" }, - { url = "https://files.pythonhosted.org/packages/47/cb/f9c45e62aaa67220179f487772461d891bb582bb2f9783c944832c60efd9/mmh3-5.2.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:85ffc9920ffc39c5eee1e3ac9100c913a0973996fbad5111f939bbda49204bb7", size = 125924, upload-time = "2026-03-05T15:55:44.638Z" }, - { url = "https://files.pythonhosted.org/packages/a5/83/fe54a4a7c11bc9f623dfc1707decd034245602b076dfc1dcc771a4163170/mmh3-5.2.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7aec798c2b01aaa65a55f1124f3405804184373abb318a3091325aece235f67c", size = 135280, upload-time = "2026-03-05T15:55:45.866Z" }, - { url = "https://files.pythonhosted.org/packages/97/67/fe7e9e9c143daddd210cd22aef89cbc425d58ecf238d2b7d9eb0da974105/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:55dbbd8ffbc40d1697d5e2d0375b08599dae8746b0b08dea05eee4ce81648fac", size = 110050, upload-time = "2026-03-05T15:55:47.074Z" }, - { url = "https://files.pythonhosted.org/packages/43/c4/6d4b09fcbef80794de447c9378e39eefc047156b290fa3dd2d5257ca8227/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6c85c38a279ca9295a69b9b088a2e48aa49737bb1b34e6a9dc6297c110e8d912", size = 111158, upload-time = "2026-03-05T15:55:48.239Z" }, - { url = "https://files.pythonhosted.org/packages/81/a6/ca51c864bdb30524beb055a6d8826db3906af0834ec8c41d097a6e8573d5/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:6290289fa5fb4c70fd7f72016e03633d60388185483ff3b162912c81205ae2cf", size = 116890, upload-time = "2026-03-05T15:55:49.405Z" }, - { url = "https://files.pythonhosted.org/packages/cc/04/5a1fe2e2ad843d03e89af25238cbc4f6840a8bb6c4329a98ab694c71deda/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:4fc6cd65dc4d2fdb2625e288939a3566e36127a84811a4913f02f3d5931da52d", size = 123121, upload-time = "2026-03-05T15:55:50.61Z" }, - { url = "https://files.pythonhosted.org/packages/af/4d/3c820c6f4897afd25905270a9f2330a23f77a207ea7356f7aadace7273c0/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:623f938f6a039536cc02b7582a07a080f13fdfd48f87e63201d92d7e34d09a18", size = 110187, upload-time = "2026-03-05T15:55:52.143Z" }, - { url = "https://files.pythonhosted.org/packages/21/54/1d71cd143752361c0aebef16ad3f55926a6faf7b112d355745c1f8a25f7f/mmh3-5.2.1-cp314-cp314t-win32.whl", hash = "sha256:29bc3973676ae334412efdd367fcd11d036b7be3efc1ce2407ef8676dabfeb82", size = 41934, upload-time = "2026-03-05T15:55:53.564Z" }, - { url = "https://files.pythonhosted.org/packages/9d/e4/63a2a88f31d93dea03947cccc2a076946857e799ea4f7acdecbf43b324aa/mmh3-5.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:28cfab66577000b9505a0d068c731aee7ca85cd26d4d63881fab17857e0fe1fb", size = 43036, upload-time = "2026-03-05T15:55:55.252Z" }, - { url = "https://files.pythonhosted.org/packages/a0/0f/59204bf136d1201f8d7884cfbaf7498c5b4674e87a4c693f9bde63741ce1/mmh3-5.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:dfd51b4c56b673dfbc43d7d27ef857dd91124801e2806c69bb45585ce0fa019b", size = 40391, upload-time = "2026-03-05T15:55:56.697Z" }, -] - [[package]] name = "more-itertools" version = "11.1.0" @@ -3097,75 +2426,75 @@ wheels = [ [[package]] name = "msgpack" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/92/23/6139781ca7aadf656fa8e384fa84693ffb13f299e6931b6526427fe5e297/msgpack-1.2.0.tar.gz", hash = "sha256:8e17af38197bf58e7e819041678f6178f4491493f5b8c8580414f40f7c2c3c41", size = 183017, upload-time = "2026-06-11T04:16:10.775Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/52/fed22bca455ff3ed28c0ee0d1117398b7cb3ce440270050e85b09240fa8d/msgpack-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ed8c9495a0f12d17a2b4b69e23f895b88f26aabe40911c86594d3fbddecfff08", size = 82473, upload-time = "2026-06-11T04:14:38.484Z" }, - { url = "https://files.pythonhosted.org/packages/3b/09/0b54d386024a9fa2073135212c11d1e83b059d98459d943d5a82ba9dcdc9/msgpack-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7384859c90b45a28a4b31aa50b49cca84504c9f27df459cea6e072627650dcb", size = 82150, upload-time = "2026-06-11T04:14:39.985Z" }, - { url = "https://files.pythonhosted.org/packages/44/ba/c6310a6f37e9bf9279b492640ec425e6f6e68a94e4cac4782ab518b05d64/msgpack-1.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63b35e8e65f04ff7ad5c9c70885da587c74f51e4b4eb3db624eac6d250e8cf59", size = 398355, upload-time = "2026-06-11T04:14:41.493Z" }, - { url = "https://files.pythonhosted.org/packages/d8/1b/f4bad0e9dea608b14d36065c44e347e4b10c0392f92cca441496cc0598ef/msgpack-1.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004c5a02acd3eca4e15e1ae7b461c32e3711105a28b1ad78be2f6facff4c523", size = 405162, upload-time = "2026-06-11T04:14:42.957Z" }, - { url = "https://files.pythonhosted.org/packages/63/34/4653bc7f426bd6ce9803f75133aa362232639e5adb8c6b99550107c71ed5/msgpack-1.2.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7e2032dacb0a973fcbf7bd088415a369dae31c5af40e199d234806be22e86765", size = 372720, upload-time = "2026-06-11T04:14:44.532Z" }, - { url = "https://files.pythonhosted.org/packages/13/3c/8c607e10db2225af52107ffa918280483248363819fecb4437a35a1f4ae2/msgpack-1.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c1feb100651fbe4b39826207cb20af065dfbfbfa43b1bafd7eaa2252abf7acfd", size = 390946, upload-time = "2026-06-11T04:14:46.054Z" }, - { url = "https://files.pythonhosted.org/packages/96/05/c4cb5fb30569cff4b4c7be4574adddb0faf7faaf3049bbab000b6f07da5b/msgpack-1.2.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:82487709d4c597d252311a65370220675fb1cc859e7da9269a3060c03ac02cf6", size = 374062, upload-time = "2026-06-11T04:14:47.817Z" }, - { url = "https://files.pythonhosted.org/packages/40/d7/b51b11e58277e6b678ba5a2f6608f88fdb0778973391a39d7f1a385f5bde/msgpack-1.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0268c67a74f5f913f545a0fdbbfaa3f6ebcf23b4c3209bb99704a2ea87e13f90", size = 405458, upload-time = "2026-06-11T04:14:49.618Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0e/9eca2961be302a6fc77a3fcb15faec749e325c9f0a8fe9c4c4576fc2cad5/msgpack-1.2.0-cp310-cp310-win32.whl", hash = "sha256:7df87173b0e13ddd134919731f13525dbbf75204145597decf1cb86887ebb492", size = 64010, upload-time = "2026-06-11T04:14:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/e7/e3/55b14ae13ed056ed35364ff71144c6a12af25227c20093045a945d08273a/msgpack-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:6371edb47788fbfd8a22016f9a97b5616dd9849bc50abcbb8e82d38f71efa096", size = 69863, upload-time = "2026-06-11T04:14:52.376Z" }, - { url = "https://files.pythonhosted.org/packages/ee/23/35de3182a647fcc84ab304160169edfa5dac7bbd8913fbed0a505ddc0d55/msgpack-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ec35cd3f127f50806aa10c3f74bf27b749f13ddf1d2217964ada8f38042d1653", size = 82368, upload-time = "2026-06-11T04:14:53.57Z" }, - { url = "https://files.pythonhosted.org/packages/aa/79/8d9bfdab933b1c7a02aba9518605a81aa30d38e9efd4915ec1a6b2d55778/msgpack-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:317eb298297121bfad9173d748124a04a36af27b6ac39c2bbc1db1ce57608dcf", size = 82095, upload-time = "2026-06-11T04:14:54.784Z" }, - { url = "https://files.pythonhosted.org/packages/d2/e1/b5accbc1354edbcee107fb35ec247db0547e91c3f90e4fabdeaee500a5a6/msgpack-1.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50fe6434de89073273026dd032a62e8b63f8857a261d7a2df5b07c9e72f3a8f7", size = 413818, upload-time = "2026-06-11T04:14:56.1Z" }, - { url = "https://files.pythonhosted.org/packages/82/31/1141cbbf7118d525834f20dcd614d1b85f1f2ffd33bc2a5ce710e6dd2516/msgpack-1.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106c6d333ff3d4eda075b7d4b9695d1752c5bcc635e40d0dbaf4e276c9ed80e1", size = 423790, upload-time = "2026-06-11T04:14:57.509Z" }, - { url = "https://files.pythonhosted.org/packages/04/e7/9582f2bd4d7546139fe297740de49bd1f7ef2d195eb0bb9fa5efeee88158/msgpack-1.2.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:67055a611e871cb1bd0acb732f2e9f64ca8155ca0bba1d0a5bb362e7209e5541", size = 387521, upload-time = "2026-06-11T04:14:59.08Z" }, - { url = "https://files.pythonhosted.org/packages/7d/12/5aadd08ff068bfd42e2ac0be6a20aa9819965df8622e87c1f0c6119c1c22/msgpack-1.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ceec7f8e633d5a4b4a32b0416bef90ee3cd1017ea36247f705e523072e576119", size = 406324, upload-time = "2026-06-11T04:15:00.686Z" }, - { url = "https://files.pythonhosted.org/packages/39/ee/3041564f0cc4c2fe7c53315aec0edf3d84807fc9b9ea714e6ac07dbdb1db/msgpack-1.2.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7ec5851160a3c2c0f77d68ddec620318cd8e7d88d94f9c058190e8ce0dfa1d31", size = 384242, upload-time = "2026-06-11T04:15:02.121Z" }, - { url = "https://files.pythonhosted.org/packages/5d/d4/de94b3dbc266229f4c2ce84485eeb221220351b7f1931029e875995bb232/msgpack-1.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dd7140f7b09dbe1984a0dff3189375d840247e3e4cf4ac45c5a499b3b599c8d2", size = 420392, upload-time = "2026-06-11T04:15:03.692Z" }, - { url = "https://files.pythonhosted.org/packages/f7/5d/c4a3fde69a292eecb202caaa87c29df7728644a65118614b821bcaddc05a/msgpack-1.2.0-cp311-cp311-win32.whl", hash = "sha256:cbfd54018d386da0951c7a2be13de0f58559d251313e613b2155e52ed1cbd8f1", size = 63976, upload-time = "2026-06-11T04:15:05.355Z" }, - { url = "https://files.pythonhosted.org/packages/18/fa/df47f83115375e7717c985265a30f3ba096c5331518e28fb647b55c46d31/msgpack-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:653373c4614c31463ba486a67776e4bb396af289921bd5353e209534b71467fa", size = 70273, upload-time = "2026-06-11T04:15:06.529Z" }, - { url = "https://files.pythonhosted.org/packages/54/d1/ffd02e54c064aa73b6b53aa08171f92dc406727077ff275d7050c6aca28a/msgpack-1.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:7a260aea1e5e7d6c7f1d9284c7360d29021627b61dc4dd7df144b81210810537", size = 64783, upload-time = "2026-06-11T04:15:07.677Z" }, - { url = "https://files.pythonhosted.org/packages/44/07/dcb13f37e670257c8d0e944f116c799c34ac6968ecb48c83619f7e91d8b5/msgpack-1.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2d6047ccd11a12c96a69f2bfe026471abef67334c3d0494a93e5310e45140a2", size = 82888, upload-time = "2026-06-11T04:15:08.992Z" }, - { url = "https://files.pythonhosted.org/packages/84/5f/6643b2a6a36ca4bc73c7674831be1d4d581cceecc7eb019dba1915951739/msgpack-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0347e3ac0dfee99086d3b68fe959da3f5f657c0019ddbaeaaa259a85f8603422", size = 82223, upload-time = "2026-06-11T04:15:10.182Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c8/9e1668b9897358e5ab39a18142e38be3cf15807e643757782da9f4a53cb3/msgpack-1.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25552ff1f2ff3dc8333e27eabb94f702da5929ed0e07969688194a3e9f12e151", size = 409700, upload-time = "2026-06-11T04:15:11.441Z" }, - { url = "https://files.pythonhosted.org/packages/38/ed/b7728573156d70b6b094233b0f38d876fc37340826cf852347ec2c7ca8ca/msgpack-1.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0d94420d9d52c56568159a69200af7e45eadb29615fa9d09fada140de1c38c7", size = 420090, upload-time = "2026-06-11T04:15:12.868Z" }, - { url = "https://files.pythonhosted.org/packages/3f/f7/5ea755a89868c04f9cdf6d96d2d99da4b3d198af10e76a6082dd0fceccc0/msgpack-1.2.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d16e1f2db4a9eebc07b7cc91898d71e710f2eed8358711a605fee802caff8923", size = 378538, upload-time = "2026-06-11T04:15:14.511Z" }, - { url = "https://files.pythonhosted.org/packages/80/2d/126e59332a439c94ffd682c38ca0102b23480e2784b3dac48d8959b0bbac/msgpack-1.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e9cb2e700e85f1e27bbb5c9de6cc1c9a4bc5ac64d5404bdcbcb37a0dc7a947a3", size = 399468, upload-time = "2026-06-11T04:15:16.133Z" }, - { url = "https://files.pythonhosted.org/packages/da/f9/7abcef683a0ad2e5ab3a4940344aad9f20cdf1f42057ecb0982cf55085d6/msgpack-1.2.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:717d0b166dd176a5f786aeafff081f6439680acf5af193eb63e6266c12b04d3d", size = 374212, upload-time = "2026-06-11T04:15:17.536Z" }, - { url = "https://files.pythonhosted.org/packages/27/23/2d62cf0e971678e96f8a3cfa9bd77fb719ddb98da73790f63c53fd847ad8/msgpack-1.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e87c7a21654d18111eb1a89bd5c42baba42e61887365d9e89585e112b4203f9e", size = 414361, upload-time = "2026-06-11T04:15:18.99Z" }, - { url = "https://files.pythonhosted.org/packages/32/fb/f5c153f614037aaf802d291a4653ba1bb731f56feacba886f7c21c109e56/msgpack-1.2.0-cp312-cp312-win32.whl", hash = "sha256:967e0c891f5f23ab65762f2e5dc95922759c79f1ef99ef4c7e1fdd863e0d0af9", size = 64389, upload-time = "2026-06-11T04:15:20.237Z" }, - { url = "https://files.pythonhosted.org/packages/90/af/8aafce6e5544b43b84cb670aca40c8bea7eb5ae8f42bfcbdc7098739987a/msgpack-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:6c23e33cee28dcffa112ae205661da4636fd7b06bd9ad1559a890623b92d060b", size = 71185, upload-time = "2026-06-11T04:15:21.51Z" }, - { url = "https://files.pythonhosted.org/packages/ba/08/9cc94be1fc1fe3d1379d439326259aef0344274f64623a8138feb54dff68/msgpack-1.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:6eeb771571f63f68045433b1a35c0256b946f31ed62f006997e40b8ad8b735af", size = 64481, upload-time = "2026-06-11T04:15:22.639Z" }, - { url = "https://files.pythonhosted.org/packages/7d/26/2902c6946ab5c8fe1e46e40842dfc32b8824464ad5cd4725364fd83f7a58/msgpack-1.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3a1d30df1f302f2b7a7404afbac2ab76d510036c34cf34dffb01f704a7288e45", size = 82621, upload-time = "2026-06-11T04:15:23.844Z" }, - { url = "https://files.pythonhosted.org/packages/c9/59/7e6b812629d2f919e586041bffc130e1af32079f71bb20699eed54ed6d92/msgpack-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:581e317112260d8ca488d490cad9290a5682276f309c41c7de237a85ed8799c8", size = 81866, upload-time = "2026-06-11T04:15:25.032Z" }, - { url = "https://files.pythonhosted.org/packages/31/13/8c291196e60aafdbae38f482205d79432297749ac5d412fe638154fb6f1d/msgpack-1.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6827d12eacc16873eba62408a1b7bbe8ecfb4a8f7ed78a631ae9bae6ad43cf2", size = 405618, upload-time = "2026-06-11T04:15:26.235Z" }, - { url = "https://files.pythonhosted.org/packages/fb/63/68f5d0ea81e167db5f59ddb94dc6f837667062113feff1c73fabf8907061/msgpack-1.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a186027e4279efa4c8bf06ce30605498d7d0d3af0fba0b9799dce85a3fd4a93c", size = 416468, upload-time = "2026-06-11T04:15:27.732Z" }, - { url = "https://files.pythonhosted.org/packages/73/58/567dddf5c5a2790f673bcd7d80c83466d68e5ee9a9674ebca3db8101c0c8/msgpack-1.2.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a96142c14a11cf1a509e8b9aaf72858a3b742b7613e095ce646913e88ce7bd99", size = 374464, upload-time = "2026-06-11T04:15:29.286Z" }, - { url = "https://files.pythonhosted.org/packages/0d/30/0c2342fc9092e4498045f5f60bca6ccbe4f4d87789778c2300e6fd6efe82/msgpack-1.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:50c220579b68a6085b95408b2eaa486b259520f55d8e363ddc9b5d7ba5a6ac6d", size = 395879, upload-time = "2026-06-11T04:15:30.973Z" }, - { url = "https://files.pythonhosted.org/packages/b9/11/9565b29b58ce3c33e177b490478b7aaeb8f726ecaaeda26d815893c1db5a/msgpack-1.2.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4dcb9d12ab100ecacdfaaf37a3d72fe8392eacc7054afc1916b12d1b747c8446", size = 371749, upload-time = "2026-06-11T04:15:32.418Z" }, - { url = "https://files.pythonhosted.org/packages/f2/da/7bade19d60b73e2ef73fb76aaf4504c112a70cb760951b7202a0c64b5111/msgpack-1.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a804727188ab0ebb237fadb303b743f04925a69d8c3247292d1e33e679767c15", size = 410416, upload-time = "2026-06-11T04:15:34.053Z" }, - { url = "https://files.pythonhosted.org/packages/6d/14/c0c619571c02432208a5977a8dbdd3fc65fe1369f8226ca4b6d08cca87d8/msgpack-1.2.0-cp313-cp313-win32.whl", hash = "sha256:1a1ac6ae1fe23298f79380e7b144c8a454e5d05616b0096584f353ba2d750114", size = 64357, upload-time = "2026-06-11T04:15:35.535Z" }, - { url = "https://files.pythonhosted.org/packages/50/a5/de06718460909aa965737fec4cfe8a15dedc6544a8c55feeb6956fa0d6e3/msgpack-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:1c3c80949d79578f9dc85fd9fb91edfe6694e8a729cd5744634d59d8455fdde3", size = 71057, upload-time = "2026-06-11T04:15:36.83Z" }, - { url = "https://files.pythonhosted.org/packages/c7/52/73446b0141c94a856e22b787c56709c0815fc34f185326577e15b26d8cfe/msgpack-1.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:fcf8f76fa587c2395fd0057c7232dbf071241f9ad280b235adb7ab585289989e", size = 64490, upload-time = "2026-06-11T04:15:38.001Z" }, - { url = "https://files.pythonhosted.org/packages/35/3d/a7e3cdafa8c0cf36c81e2fa848ec4d30cf089459af45b390ad03f9ce6f49/msgpack-1.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f854fa1a8b55d75d82ef9a905d9cdbeffdf7897c088f6020bd221867da5e56a5", size = 83032, upload-time = "2026-06-11T04:15:39.38Z" }, - { url = "https://files.pythonhosted.org/packages/ca/aa/53ddfba0e347cc4b484e95f629c5850b9e800ca8390c91ffc604407acf87/msgpack-1.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e90df581f80f53b372d5d9d9349078d729851a3a0d0bd74f53ccb598d01e45b8", size = 82600, upload-time = "2026-06-11T04:15:40.609Z" }, - { url = "https://files.pythonhosted.org/packages/59/fd/e64c2c776e6dbad0af3c963fe0c0dd1ee1ba09efac478b233ab1db41868f/msgpack-1.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b276ed50d8ac75d1f134a433ae79af8557d0fa25ee5b4737da533dfc2ce382e8", size = 404342, upload-time = "2026-06-11T04:15:41.87Z" }, - { url = "https://files.pythonhosted.org/packages/1b/60/fb9a08e6ccba882dfd370a5837fe3a07572938fdfe954f0f17fdf3e574b9/msgpack-1.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:544d972459c92aa32e63b800d07c2d9cf2734a3be29cee3a0b478a622850e9f5", size = 412351, upload-time = "2026-06-11T04:15:43.253Z" }, - { url = "https://files.pythonhosted.org/packages/37/4d/df5c575c274fedc68ac9c6c61d045161899efad2afcdc25138efa7edde69/msgpack-1.2.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a070147cc2cf6b8a891734e0f5c8fe8f70ed8739ab30ba140b058005a6e86af4", size = 373331, upload-time = "2026-06-11T04:15:44.754Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a4/c8b98f8191e985ed2003d87664ce3c95cca41db5d0cf6bf4f54327d32ec8/msgpack-1.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7685e23b0f51745a751629c31713fbefdef8896b31b2bb38299dfa4ae6c0740c", size = 394654, upload-time = "2026-06-11T04:15:46.423Z" }, - { url = "https://files.pythonhosted.org/packages/d4/49/76f036720a602ea24428cfec5ec806f2487c0380b1bff0a2aa3094e15f87/msgpack-1.2.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b9204daeee8d91a7ae5acf2d2a8e3983be9a3025f38aa21bfaefbd7eea84a7dc", size = 370624, upload-time = "2026-06-11T04:15:48.062Z" }, - { url = "https://files.pythonhosted.org/packages/9f/38/40af3d29232833705a43b0fce0d07425cc280a7b92ab2b29932425b40df4/msgpack-1.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bfc057248609742ebbabf6bcd27fea4fd99c4980584e613c168c9b002318298f", size = 408038, upload-time = "2026-06-11T04:15:49.669Z" }, - { url = "https://files.pythonhosted.org/packages/30/b2/f140ca450524dff4d8d0eb81eb9ed75f8f3e0b1f12e49c5b01617cfa0b1c/msgpack-1.2.0-cp314-cp314-win32.whl", hash = "sha256:a3faa7edf2388337ae849239878e92f0298b4dab4488e4f1834062f9d0c410c9", size = 65823, upload-time = "2026-06-11T04:15:51.062Z" }, - { url = "https://files.pythonhosted.org/packages/4d/13/6517bf966b841c7675ded30701a068ce141f3e698a27aaa35c702d8e078b/msgpack-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:1a3effc392a57744e4681e55d05f97d5ee7b598747d718340a9b4b8a970c40e1", size = 72484, upload-time = "2026-06-11T04:15:52.289Z" }, - { url = "https://files.pythonhosted.org/packages/45/8c/1d948420fdaa24de4efdb8012a6a5bebe09c82ee002b8c2ca745e9917f1f/msgpack-1.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:56a318f7df6bec7b40928d6b0519961f20a510d8baabf6baa393a70444588f0a", size = 66657, upload-time = "2026-06-11T04:15:53.583Z" }, - { url = "https://files.pythonhosted.org/packages/39/16/1674faa1b7bddc19e79b465fd8e88e2cf4e3f7cae90723740701e8541068/msgpack-1.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:afa4a65ab2097795e771a74a3a81ea49534aaeba874eaf426a3332268e045ae6", size = 86093, upload-time = "2026-06-11T04:15:54.98Z" }, - { url = "https://files.pythonhosted.org/packages/dd/24/f241bcfdd9e96b2246289357c5a5e5a496189fd41c5844bee802c116aac7/msgpack-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:409550770632bb28daa70a11d0ed5763f7db38f40b06f7db9f11dd2794d01102", size = 86372, upload-time = "2026-06-11T04:15:56.381Z" }, - { url = "https://files.pythonhosted.org/packages/94/c9/57f8ab98a1b21808c27b6dd6029053e0a796ffbb9b371e460dbe997011a9/msgpack-1.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf47e3cd11ce044965a9736a322afdd390b31ed602d1c1b10211d1a841f1d587", size = 428207, upload-time = "2026-06-11T04:15:57.739Z" }, - { url = "https://files.pythonhosted.org/packages/17/6b/4fd4aa739f131ded751ca7167c8ee87d2aab32506ebbeea893b60b51d343/msgpack-1.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:204bc9f5d6e59c1718c0a4a84fc8ff71b5b4562faac257c1a68bca611ecf9b72", size = 426082, upload-time = "2026-06-11T04:15:59.356Z" }, - { url = "https://files.pythonhosted.org/packages/f9/00/db88e9a08fcd6513decaad06cbd5c168142bc3e662fb2f1aca3a563b7aa1/msgpack-1.2.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:610154307b27267266368bc1d1c7bb8aeb71da7be9356d403cb2442d9e6399f5", size = 378355, upload-time = "2026-06-11T04:16:00.916Z" }, - { url = "https://files.pythonhosted.org/packages/54/84/eee4dd703d7a600cf46159d621c070b0b9468cf3dbade4ea8272bf5232a4/msgpack-1.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6799f157bb63e79f11e2e590cfdb28423fc18dd60c270c3914b5b4586ae36f7e", size = 410848, upload-time = "2026-06-11T04:16:02.745Z" }, - { url = "https://files.pythonhosted.org/packages/12/0a/195e2c549fd4631eb7f157d016ff15a10c4c1cf82b6d0a9b1edaef5174b1/msgpack-1.2.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:72bd844902cf0a5ac3af2ef742f253cd0b1e5bcd184f49b4fb9a6a1f7bf305e8", size = 376152, upload-time = "2026-06-11T04:16:04.041Z" }, - { url = "https://files.pythonhosted.org/packages/45/9b/bdd143fa79baec411dc658f5686fed680a18b36fcea5fccb6af1b8c7d832/msgpack-1.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3c0bd450f78d0d81722c80da6cdbf674a856967870a9db2f6c4debc4d8b3c67c", size = 417061, upload-time = "2026-06-11T04:16:05.63Z" }, - { url = "https://files.pythonhosted.org/packages/2d/ce/011ffcd8b919f55196ec53f12ae162e21c879d95afba226894314ff62c07/msgpack-1.2.0-cp314-cp314t-win32.whl", hash = "sha256:378caf74c4c718dfc17590ce68a6d710ed398ff6fcf08237de23b77755730b55", size = 70782, upload-time = "2026-06-11T04:16:07.105Z" }, - { url = "https://files.pythonhosted.org/packages/57/a8/9b8791ca96b1be6b9f659c718271e2cb7f99f73f58aad2dd0b30f750f6c0/msgpack-1.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:553b42598165c4dd3235994fd6e4b0dfb1ce5f3fd33d94ba9609442643015f38", size = 77899, upload-time = "2026-06-11T04:16:08.353Z" }, - { url = "https://files.pythonhosted.org/packages/5b/04/3fa2dffb87bf598696b86bde7cd642d0a7590520c3fa24cd19611dfebeb7/msgpack-1.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2825bb1da548d214ab8a810906b7dd69a10f3838b615a2cc46e5172d3cb44f6e", size = 71004, upload-time = "2026-06-11T04:16:09.556Z" }, +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/f9/c0a1c127f9049db9155afc316952ea571720dd01833ff5e4d7e8e6352dbb/msgpack-1.2.1.tar.gz", hash = "sha256:04c721c2c7448767e9e3f2520a475663d8ee0f09c31890f6d2bd70fd636a9647", size = 183960, upload-time = "2026-06-18T16:13:52.594Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/16/f70100614b69feb3ade7285f08c9c52d6cda0a5c03f3f5e2facd63acb211/msgpack-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c7b398c56ff125feae96c2737abfec5595f1fa0aa186df60c56040b8accb95c", size = 82926, upload-time = "2026-06-18T16:12:31.531Z" }, + { url = "https://files.pythonhosted.org/packages/e4/3c/08ecd5cdfe4e2de43aec79062028ad0f7b2d9b1fea5430068c198ba570da/msgpack-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1548006a91aa93c5da81f3bdcebc1a0d10cea2d25969754fbe848da622b2b895", size = 82730, upload-time = "2026-06-18T16:12:32.894Z" }, + { url = "https://files.pythonhosted.org/packages/19/9f/a70c9cb1a04ecc134005149367dcfe35d167284e8f65035a1e4156ad17b5/msgpack-1.2.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1dabedcd0f23559f3596428c6589c1cd8c6eaed3a0d720795b07b0225d769203", size = 400729, upload-time = "2026-06-18T16:12:34.052Z" }, + { url = "https://files.pythonhosted.org/packages/fa/7f/5ce020168cf0439041526e95aa068c722c016aee21624e331aeabeee2e8e/msgpack-1.2.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83efa1c898e0fc5380fc0cabbf75164c52e3b5cbb45973710d75821928380c73", size = 407625, upload-time = "2026-06-18T16:12:35.239Z" }, + { url = "https://files.pythonhosted.org/packages/79/70/fb7668ce0386819303047057aef6fc1da73b584291d9cff82b821744e2ef/msgpack-1.2.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01e2dd6c9b19d333a00282330cc8a73d38d8dabc306dc5b42cd668c3ac82e833", size = 377891, upload-time = "2026-06-18T16:12:36.684Z" }, + { url = "https://files.pythonhosted.org/packages/3d/dc/9ebe654a73c3aed2e40aa6b52e3c2a02b5f53ef0085fa235a45d5b367f87/msgpack-1.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:350cb813d0af6e65d2f7ef0d729f7ff5be5a8bce03665892f43e5883d4ecc1b8", size = 391987, upload-time = "2026-06-18T16:12:37.839Z" }, + { url = "https://files.pythonhosted.org/packages/42/eb/b67cf64218a2fa25e1c671fe1d3dbb06cbeb973e71bc4b822da079862d0b/msgpack-1.2.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ee1d9ed27d0497b848923746cf762ed2e7db24f4be7eec8e5cbe8c766aa707b7", size = 374603, upload-time = "2026-06-18T16:12:39.221Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2e/9ee200cde32fd1a0101b4006202fde554c1860adfb9bf7bff31ea4c08df8/msgpack-1.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:633727297ed063441fd1cda2288865487f33ad14eeb8831afb5f0c396a62cfce", size = 405121, upload-time = "2026-06-18T16:12:40.524Z" }, + { url = "https://files.pythonhosted.org/packages/43/b6/f10117be7ca7a51e8feed699a907b8e663a8cd66e115ae6b4fb30cc7945c/msgpack-1.2.1-cp310-cp310-win32.whl", hash = "sha256:298872ecf9e61950f1c6af4ca969b859ee91783bb920ef6e6172697d0c8aad74", size = 64088, upload-time = "2026-06-18T16:12:41.762Z" }, + { url = "https://files.pythonhosted.org/packages/ba/93/89976c696fb0224662239d952c47b4d1661b34d79a332ef5584facaa8579/msgpack-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2ff164c1b0bcb740b073b99e945234d0212852fa378e44a208c425379140dbeb", size = 70113, upload-time = "2026-06-18T16:12:42.78Z" }, + { url = "https://files.pythonhosted.org/packages/f4/6b/e9b1cdc042c4458801d2545ed782a95f3d6ba8e270cce8745b8603c7f748/msgpack-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:29a3f6e9667868429d8240dfd063ea5ffdc1321c13d783aa23827a38de0dcb22", size = 82812, upload-time = "2026-06-18T16:12:45.022Z" }, + { url = "https://files.pythonhosted.org/packages/0c/3a/dd518a1bf78ed1e9ad8afe57307c079a00eafe4b3068932a27ca1ea56b4f/msgpack-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aded5bdf32609dc7987a49bbbd15a8ef096193f96dd8bbeb791de729e650acf5", size = 82739, upload-time = "2026-06-18T16:12:46.025Z" }, + { url = "https://files.pythonhosted.org/packages/70/e0/7ba9e1542bf0771a27b8b37c1316e3f95ae9d748fd765284655c476ad4ef/msgpack-1.2.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:146ee4e9ce80b365c6d4c47073da9da7bcec473e58194ceee5dd7620ace77e06", size = 414233, upload-time = "2026-06-18T16:12:47.029Z" }, + { url = "https://files.pythonhosted.org/packages/03/8d/671d81534ea0e2b0e8a121be100020da09eb78861fe3aa8f3ef7dcd3bed1/msgpack-1.2.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a28d076ca7c82b9c8728ad90b7147489449557038bed50e4241eb832395169b4", size = 423843, upload-time = "2026-06-18T16:12:48.19Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b6/e5c737515ed1f166664b87601b532f58cbb73d8aa6a90b99f7c2c5037e8e/msgpack-1.2.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7d31c0ac0c640f877804c67cb2bc9f4e23dc2db97e96c2e67fa27d38283b41f8", size = 390772, upload-time = "2026-06-18T16:12:49.624Z" }, + { url = "https://files.pythonhosted.org/packages/a8/46/62ed8c2e87d7021eab19921594d961ef3aa3794eec76c716dc30f3bfd433/msgpack-1.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8ff92d7feeaf5bc26c51495b69e2f99ed97ab79346fb6555f44be7dd2ac6503b", size = 409559, upload-time = "2026-06-18T16:12:50.936Z" }, + { url = "https://files.pythonhosted.org/packages/70/ff/59aa3887b860bbf43532835e192b1c388a17590d6068ae4f8b2bc74c906e/msgpack-1.2.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:779197a6513bab3c3632265e3d0f7cb3227e62510841a6f34f1eaa37efbb345e", size = 387838, upload-time = "2026-06-18T16:12:52.161Z" }, + { url = "https://files.pythonhosted.org/packages/09/11/f8563e471093420cf6478cb3271a0175d8402b82d879783d4035d2d03360/msgpack-1.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:67f6dd22fa72a93752643f07889796d62739a13415ee630169a8ce764f86cf9f", size = 421732, upload-time = "2026-06-18T16:12:53.556Z" }, + { url = "https://files.pythonhosted.org/packages/57/cf/e673683c4c6c90c1022b24c65af4b03eda72b182a1176ef6449069d66acc/msgpack-1.2.1-cp311-cp311-win32.whl", hash = "sha256:91054a783328e0ea7954b8771095705c8d2243b814743fbaadf14552c9c52c5d", size = 64091, upload-time = "2026-06-18T16:12:54.821Z" }, + { url = "https://files.pythonhosted.org/packages/3f/07/ca212739d179f9083bff2c7c08c24101c3555a334fadc2b876b18768a3ae/msgpack-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2eda0b7ebb1283a98d3e4492ac933c8af6aff59fd3df1c3ed024f536af4b1dc8", size = 70462, upload-time = "2026-06-18T16:12:55.898Z" }, + { url = "https://files.pythonhosted.org/packages/6d/be/6798347b425e26f35db82e69dd83c09716c856a3714e7bffc4c0860fd830/msgpack-1.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:6ee967f7c7e1df2890c671ff2ee51a28ded0efc95da3e507176dee881ce36c66", size = 65059, upload-time = "2026-06-18T16:12:57.053Z" }, + { url = "https://files.pythonhosted.org/packages/bc/dd/9e8cbd8f5582ca4b590336f2b91ee5662f6a6ca562b565abaf696a0f81ff/msgpack-1.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2ef59c659f289eddf8aa6623823f19fa2f40a4029266889eac7a2505dd210c35", size = 83531, upload-time = "2026-06-18T16:12:58.249Z" }, + { url = "https://files.pythonhosted.org/packages/50/2e/ebdb85a8da151397a2790363676b7ed7c125924fe618e4c6d8befb0cc62c/msgpack-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d3567748a5107cb40cdf66a275430c2f87c07777698f4bfd25c35f44d533258c", size = 82657, upload-time = "2026-06-18T16:12:59.396Z" }, + { url = "https://files.pythonhosted.org/packages/26/aa/753ad8b007b464e1d8aa0c8e650b9c5f4f725e658fc5ac8a7635c55b7f6e/msgpack-1.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60926b75d00c8e816ef98f3034f484a8bc64242d66839cef4cf7e503142316a0", size = 410634, upload-time = "2026-06-18T16:13:00.383Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/6adabd4f6d5e686f97dd02ce7fce3fe4cf672cbac36b8f67ff4040e8ad8b/msgpack-1.2.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:020e881a764b20d8d7ca1a54fc01b8175519d108e3c3f194fddc200bda95951a", size = 419989, upload-time = "2026-06-18T16:13:01.776Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cc/85039b7b0eb168aaad7383a23c97e291a11f08351cb45a606ce865e4e3f1/msgpack-1.2.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4202c74688ca06591f78cb18988228bd4cca2cc75d57b60008372892d2f1e6e6", size = 377544, upload-time = "2026-06-18T16:13:03.637Z" }, + { url = "https://files.pythonhosted.org/packages/ed/bf/35963899493b32030c85fc513b723ae66144ac70c11ebc52e889e16e3d99/msgpack-1.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8b267ce94efb76fbd1b3373511420074ee3187f0f7811bf394531de13294735a", size = 400842, upload-time = "2026-06-18T16:13:05.012Z" }, + { url = "https://files.pythonhosted.org/packages/a6/df/8e2ac970c8f99264cd9997d1c73df5466bc19da3301d7dc5500862a9b089/msgpack-1.2.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e4f1d0f8f98ade9634e01fb704a408f9336c0a8f1117b369f5db83dc7551d8b1", size = 374108, upload-time = "2026-06-18T16:13:06.232Z" }, + { url = "https://files.pythonhosted.org/packages/17/dd/fa8bd265110dfa51c20cb529f9e6d240a16fafe7e645004c6af2d01353ba/msgpack-1.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f02cf17a6ca1abe29b5f980644f7551f94d71f2011509b26d8625ce038f0df64", size = 414939, upload-time = "2026-06-18T16:13:07.478Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b9/8377a5ad8953fc0437c70cc98d9ae29f27fe5ac5109fbec0812085865735/msgpack-1.2.1-cp312-cp312-win32.whl", hash = "sha256:0c0d9802354507bcba62af19c17918e3eb437cc25e6f50657d511b5856a77aac", size = 64504, upload-time = "2026-06-18T16:13:08.822Z" }, + { url = "https://files.pythonhosted.org/packages/57/7f/ce1e377df7e62461fefd9eb23bfb93a4a523f40a517b377b8f844d836828/msgpack-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:5c24aa15d5963051e1a5c62b12c50cd705992502b5ec1f3bece6046f33c9fc24", size = 71421, upload-time = "2026-06-18T16:13:09.828Z" }, + { url = "https://files.pythonhosted.org/packages/8f/32/ebfe84c9929f08f188d56c7a2fd913406a9ddad76a634697c1c43b8112e6/msgpack-1.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:4227224aaec8f7fbcbfbd4272319347b2bb4030366502600f8c45588c5187b07", size = 64775, upload-time = "2026-06-18T16:13:11.056Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ac/dcddcab6f6c20ecb387ca5e980371cdb3f87ff69aeca388be97eebc4c074/msgpack-1.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a70e3cf2804a300d921bb0940426e35f4e489a23adfb77a808892241db0a064", size = 83151, upload-time = "2026-06-18T16:13:12.173Z" }, + { url = "https://files.pythonhosted.org/packages/64/71/fbcfa83a1d6a9c6091942d1cfd070962244664b87427a9a49a6897b1b219/msgpack-1.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:491cc39455ca765fad51fb451bf2915eb2cf41192ab5801ce8d67c1d614fe056", size = 82351, upload-time = "2026-06-18T16:13:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/e3/10/ddf7b06db879e8792d13934ddda09ff20bd2a583fd84c9b59aae9b0e650b/msgpack-1.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f310233ef7fb9c14e201c93639fe5f5260b005f56f0b29048e999c30935596cc", size = 407518, upload-time = "2026-06-18T16:13:14.233Z" }, + { url = "https://files.pythonhosted.org/packages/79/d3/36a46a8ed992b781acbc05928bd5bee3c810cb0c3563bf81a7b0c04a1a76/msgpack-1.2.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:787c9bebb5833e8f6fc8abca3c0597683d8d87f56a8842b6b89c75a5f3176e2d", size = 416405, upload-time = "2026-06-18T16:13:15.435Z" }, + { url = "https://files.pythonhosted.org/packages/f9/84/e8e9598b557c0ba6ddae901a73780a4c75ac667dddf59414b1e56a42fb34/msgpack-1.2.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dc871b997a9370d855b7394465f2f350e847a5b806dd38dcc9c989e7d87da155", size = 376257, upload-time = "2026-06-18T16:13:17.022Z" }, + { url = "https://files.pythonhosted.org/packages/40/16/738fe6d875ad7e2a9429c165322a4ec088f4f273cdfae63d96a89c467961/msgpack-1.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85f57e960d877f2977f6430896191b04a21f8901b3b4baf2e4604329f4db5402", size = 397469, upload-time = "2026-06-18T16:13:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/ca/be/6d5952df75a7f24f35833af764c3a6860780364cb3a0030beb8099e1b2b4/msgpack-1.2.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1233ee2dd0cefba127583de50ea654677277047d238303521db35def3d7b2e7c", size = 372802, upload-time = "2026-06-18T16:13:19.685Z" }, + { url = "https://files.pythonhosted.org/packages/e1/39/e2ef7dbf0473bcb8dc7c50bf782a892d67414877b63e47fc88eb189ef5e6/msgpack-1.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e3dc2feb0876209d9c38aa56cb1de169bd6c4348f1aa48271f241226590993e6", size = 411273, upload-time = "2026-06-18T16:13:21.028Z" }, + { url = "https://files.pythonhosted.org/packages/ef/c5/133f4512a56e983a93445c836c9d94d88f3bc2e0980ff4b9e577bd8416ce/msgpack-1.2.1-cp313-cp313-win32.whl", hash = "sha256:6d09badf350af2be9d189184e04e64cf54ad93569ab3d96fca58bd3e84aad707", size = 64471, upload-time = "2026-06-18T16:13:22.293Z" }, + { url = "https://files.pythonhosted.org/packages/e2/98/577e10b055096a7dd40732358cabaf7180a20c79ed1dcdbb618e4b9deac7/msgpack-1.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:33f14fba63278b714efe6ad07e50ea5f03d91537aa6a1c5f1ceca4cf44013ca9", size = 71274, upload-time = "2026-06-18T16:13:23.455Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ee/0c0048e7cfbef23c6a94791b8959ab28155232e7956de8a305b5ff588f05/msgpack-1.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc5febcd4c99effbc02b528e49d6fd0760b2b7d48c05239e345a5fa6e743d9a", size = 64795, upload-time = "2026-06-18T16:13:24.687Z" }, + { url = "https://files.pythonhosted.org/packages/77/58/cce442852c6b9e1639c7c8ac8fd9143121cb32dab0f308df4d1426a8eb9c/msgpack-1.2.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:05f340e47e7e47d2da8db9b53e1bb1d294369e9ef45a747441309f6650b8351d", size = 83610, upload-time = "2026-06-18T16:13:25.724Z" }, + { url = "https://files.pythonhosted.org/packages/60/5c/15b4c7a0182f75ffa90751958ba36a9c01cafee367d49a3edc10ed140b01/msgpack-1.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:810b916696c86ef0deb3b74588480224df4c1b071136c34183e4a2a4284d7ac7", size = 83138, upload-time = "2026-06-18T16:13:26.781Z" }, + { url = "https://files.pythonhosted.org/packages/b8/a6/99e58722feaffc5f2fbcc0c8c0d1451ab9f84097f7af87291b46af2390f4/msgpack-1.2.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ca0dacff965c47afdc3749a8469d7302a8f801d6a28758d55120d75e66ce6889", size = 406090, upload-time = "2026-06-18T16:13:28.072Z" }, + { url = "https://files.pythonhosted.org/packages/19/03/8c63e8cf52958534ef688625965ab04c269a6cadd8caef16758b380a821a/msgpack-1.2.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e2bf9280bceb5efca998435904b5d3e9fdbcc11d90dc9df30aec7973252b720", size = 412106, upload-time = "2026-06-18T16:13:29.427Z" }, + { url = "https://files.pythonhosted.org/packages/63/d2/155d9e71b40e41fd934bc0c48b9b2770f22263e1ac20aad8e29fdca7be3f/msgpack-1.2.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6c4be5d1c02a42b066ca6ddb71adf36432868fdcdb6ee87e634e86e0674190", size = 374851, upload-time = "2026-06-18T16:13:30.631Z" }, + { url = "https://files.pythonhosted.org/packages/98/48/deaf2326262a8d5ea3295ce9649912ecd3f551ba7ec8e33c665d2ba583f3/msgpack-1.2.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec0e675d59150a6269ddc9139087c722292664a37d071a849c05c473350f1f2d", size = 396168, upload-time = "2026-06-18T16:13:31.977Z" }, + { url = "https://files.pythonhosted.org/packages/10/2a/b4410f906c2ec0008f1608d3ab5143afc3ad3f4e6da0fed3ea2231d0bef4/msgpack-1.2.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:dd3bfe82d53edfe4b7fc9a7ec9761e23a7a5b1dac22264505af428253c29ed24", size = 371959, upload-time = "2026-06-18T16:13:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/59/86/1edc67270099a528fa2093ea60fe191233cd238e4bd30cfacf7db79fc959/msgpack-1.2.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5ad5467fc3f68b5468e06c5f788d712e9f8ffc8b0cd1bcb160c105c1ee92dae7", size = 408457, upload-time = "2026-06-18T16:13:34.567Z" }, + { url = "https://files.pythonhosted.org/packages/82/90/8b630fef07d8c5ab457b71ff2c217910c83d333c7a68472c186e87cc504a/msgpack-1.2.1-cp314-cp314-win32.whl", hash = "sha256:98b58bdb89c46190e4609bb36abe17c6d4105ad13f9c5f8f6f64d320f8ced3fb", size = 65942, upload-time = "2026-06-18T16:13:36.056Z" }, + { url = "https://files.pythonhosted.org/packages/16/f1/467b81e98b24dd3885d7b1857728797b4ffc76a7a7483af4fb321a07de3c/msgpack-1.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:74847557e28ce71bd3c438a447ca90e4b507e997ddbdef8a12a7b283b86c156b", size = 72627, upload-time = "2026-06-18T16:13:37.079Z" }, + { url = "https://files.pythonhosted.org/packages/a7/1d/5d8c4c89985feb6acefb82a09e501c60392261856d2408d20bfe4f0360b1/msgpack-1.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:b50b727bd652bdc37d950336c848ef20ec54a4cafc38dce19b1cd86ad625d0f7", size = 66908, upload-time = "2026-06-18T16:13:38.23Z" }, + { url = "https://files.pythonhosted.org/packages/1b/02/ad2afb678b4de94496cd432b581759b756a92c1192d8c767edd6b132efdc/msgpack-1.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8d00f177ca88a77c1cf848d204a38f249751650b601cb6532acc68805d8a8273", size = 86000, upload-time = "2026-06-18T16:13:39.44Z" }, + { url = "https://files.pythonhosted.org/packages/54/74/0b797484013128837f3b1cbb6cea019277c4de4e377dc512b4d9a0f92940/msgpack-1.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5bb9c386f0a329c035ddbab4b72d1028bf9627add8dda41070288563d57ed1b1", size = 86544, upload-time = "2026-06-18T16:13:40.447Z" }, + { url = "https://files.pythonhosted.org/packages/a9/b4/b774d7eb95561739907fec675582f83203cf41c597a418c2589b4bfb8e9d/msgpack-1.2.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20466cca18c49c7292a8984bc15d65857b171e7264bdcb5f96baf8be238791fc", size = 427661, upload-time = "2026-06-18T16:13:41.574Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f9/3243191dc9937e00756c8bc1b0272fed8f23758e43df2a3b46f533e5090f/msgpack-1.2.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:196300e7e5d6e74d50f1607ab9c06c4a1484c383cd22defd727902591f7e8dde", size = 426375, upload-time = "2026-06-18T16:13:42.936Z" }, + { url = "https://files.pythonhosted.org/packages/23/c7/1693111db9944ba4ad4b67a1e788400d78a0b6af7a6523dc7e4e58f8274b/msgpack-1.2.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575957e79cd51903a4e8495a242442949641e08f1efd5197b43bebd3ea7682b4", size = 380495, upload-time = "2026-06-18T16:13:44.306Z" }, + { url = "https://files.pythonhosted.org/packages/3e/2b/92f86956a0c13e8662f7e2ad630c4eb4db07497b967589bd5245e018b2c1/msgpack-1.2.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8c2ed1e48cc0f460bf3c7780e7137ff21a4e18433451916f2442c1b21036cd7d", size = 410897, upload-time = "2026-06-18T16:13:45.629Z" }, + { url = "https://files.pythonhosted.org/packages/da/ea/1479f72d200313a76fc2f823a79d1e07ed052ab7b8a0280640aa7b95de42/msgpack-1.2.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5f6277e5f783c36786a145e0247fc189a03f35f84b251646e53592d2bc12b355", size = 378519, upload-time = "2026-06-18T16:13:46.998Z" }, + { url = "https://files.pythonhosted.org/packages/f5/4d/fa006060ffa1011d32bfae826fe766fe73e02982183601633b7121058ab3/msgpack-1.2.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f9389552ecf4784886345ead0647e4edc96bee37cbab05b75540f542f766c48c", size = 419815, upload-time = "2026-06-18T16:13:48.205Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/aab6c946570496b78e67804721f3d5e2d62a93081b9b37df77764ef56347/msgpack-1.2.1-cp314-cp314t-win32.whl", hash = "sha256:c1c79a604a2969a868a78b6ebd27a887e00c624f14f66b3038e0590cb23332d1", size = 70914, upload-time = "2026-06-18T16:13:49.385Z" }, + { url = "https://files.pythonhosted.org/packages/13/0a/e608956488a2af014cfe6e3d665e090b8ee42aa14b07f8f95b8880d66b09/msgpack-1.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f12038a35fabd52e56a3547bab42401af49a45caa6dd00b34c44de235bc93ee2", size = 77999, upload-time = "2026-06-18T16:13:50.467Z" }, + { url = "https://files.pythonhosted.org/packages/d2/8a/27e2e57055176e366a46b85d02d68e7a5bcfbdd8474c9706375d965f24d3/msgpack-1.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:0adcf06ffde0777c0e1a9b771a2b1c4226ba1bbf748c8efcc02fcdeca3299107", size = 71160, upload-time = "2026-06-18T16:13:51.498Z" }, ] [[package]] @@ -3413,36 +2742,36 @@ wheels = [ [[package]] name = "nh3" -version = "0.3.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/5f/1d19bdc7d27238e37f3672cdc02cb77c56a4a86d140cd4f4f23c90df6e16/nh3-0.3.5.tar.gz", hash = "sha256:45855e14ff056064fec77133bfcf7cd691838168e5e17bbef075394954dc9dc8", size = 20743, upload-time = "2026-04-25T10:44:16.066Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/b0/8587ac42a9627ab88e7e221601f1dfccbf4db80b2a29222ea63266dc9abc/nh3-0.3.5-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:23a312224875f72cd16bde417f49071451877e29ef646a60e50fcb69407cc18a", size = 1420126, upload-time = "2026-04-25T10:43:39.834Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1b/1dbc4d0c43f12e8c1784ede17eaee6f061d4fbe5505757c65c49b2ceab95/nh3-0.3.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:387abd011e81959d5a35151a11350a0795c6edeb53ebfa02d2e882dc01299263", size = 793943, upload-time = "2026-04-25T10:43:41.363Z" }, - { url = "https://files.pythonhosted.org/packages/47/9f/d6758d7a14ee964bf439cc35ae4fa24a763a93399c8ef6f22bd11d532d29/nh3-0.3.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:48f45e3e914be93a596431aa143dedf1582557bf41a58153c296048d6e3798c9", size = 841150, upload-time = "2026-04-25T10:43:43.007Z" }, - { url = "https://files.pythonhosted.org/packages/b6/36/d5d1ae8374612c98f390e1ea7c610fa6c9716259a03bbf4d15b269f40073/nh3-0.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0a09f51806fd51b4fedbf9ea2b61fef388f19aef0d62fe51199d41648be14588", size = 1008415, upload-time = "2026-04-25T10:43:44.324Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8f/d13a9c3fd2d9c131a2a281737380e9379eb0f8c33fea24c2b923aaafbb15/nh3-0.3.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c357f1d042c67f135a5e6babb2b0e3b9d9224ff4a3543240f597767b01384ffd", size = 1092706, upload-time = "2026-04-25T10:43:45.653Z" }, - { url = "https://files.pythonhosted.org/packages/bb/57/2f3add7f8680fcc896afa6a675cb2bab09982853ee8af40bad621f6b61c4/nh3-0.3.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:38748140bf76383ab7ce2dce0ad4cb663855d8fbc9098f7f3483673d09616a17", size = 1048346, upload-time = "2026-04-25T10:43:46.974Z" }, - { url = "https://files.pythonhosted.org/packages/c1/c3/2f9e4ffa82863074d1361bfe949bc46393d91b3411579dfbbd090b24cac5/nh3-0.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:84bdeb082544fbcb77a12c034dd77d7da0556fdc0727b787eb6214b958c15e29", size = 1029038, upload-time = "2026-04-25T10:43:48.569Z" }, - { url = "https://files.pythonhosted.org/packages/e8/10/2804deb3f3315184c9cae41702e293c87524b5a21f766b07d7fe3ffbcfbb/nh3-0.3.5-cp314-cp314t-win32.whl", hash = "sha256:c3aae321f67ae66cff2a627115f106a377d4475d10b0e13d97959a13486b9a88", size = 603263, upload-time = "2026-04-25T10:43:49.851Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a2/f6685248b49f7548fc9a8c335ab3a52f68610b72e8a61576447151e4e2e6/nh3-0.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c88605d8d468f7fc1b31e06129bc91d6c96f6c621776c9b504a0da9beac9df5f", size = 616866, upload-time = "2026-04-25T10:43:51.005Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b6/d8c9018635d4acfefde6b68470daa510eed715a350cbaa2f928ba0609f81/nh3-0.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:72c5bdedec27fa33de6a5326346ea8aa3fe54f6ac294d54c4b204fb66a9f1e79", size = 602566, upload-time = "2026-04-25T10:43:52.283Z" }, - { url = "https://files.pythonhosted.org/packages/85/30/d162e99746a2fb1d98bb0ef23af3e201b156cf09f7de867c7390c8fe1c06/nh3-0.3.5-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3bb854485c9b33e5bb143ff3e49e577073bc6bc320f0ff8fc316dd89c0d3c101", size = 1442393, upload-time = "2026-04-25T10:43:53.556Z" }, - { url = "https://files.pythonhosted.org/packages/25/8c/072120d506978ab053e1732d0efa7c86cb478fee0ee098fda0ac0d31cb34/nh3-0.3.5-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50d401ab2d8e86d59e2126e3ab2a2f45840c405842b626d9a51624b3a33b6878", size = 837722, upload-time = "2026-04-25T10:43:55.073Z" }, - { url = "https://files.pythonhosted.org/packages/52/86/d4e06e28c5ad1c4b065f89737d02631bd49f1660b6ebcf17a87ffcd201da/nh3-0.3.5-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acfd354e61accbe4c74f8017c6e397a776916dfe47c48643cf7fd84ade826f93", size = 822872, upload-time = "2026-04-25T10:43:56.581Z" }, - { url = "https://files.pythonhosted.org/packages/0a/62/50659255213f241ec5797ae7427464c969397373e83b3659372b341ae869/nh3-0.3.5-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:52d877980d7ca01dc3baf3936bf844828bc6f332962227a684ed79c18cce14c3", size = 1100031, upload-time = "2026-04-25T10:43:58.098Z" }, - { url = "https://files.pythonhosted.org/packages/00/7a/a12ae77593b2fcf3be25df7bc1c01967d0de448bdb4b6c7ec80fe4f5a74f/nh3-0.3.5-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:207c01801d3e9bb8ec08f08689346bdd30ce15b8bf60013a925d08b5388962a4", size = 1057669, upload-time = "2026-04-25T10:43:59.328Z" }, - { url = "https://files.pythonhosted.org/packages/2d/71/5647dc04c0233192a3956fc91708822b21403a06508cacf78083c68e7bf0/nh3-0.3.5-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea232933394d1d58bf7c4bb348dc4660eae6604e1ae81cd2ba6d9ed80d390f3b", size = 914795, upload-time = "2026-04-25T10:44:00.52Z" }, - { url = "https://files.pythonhosted.org/packages/1b/0e/bf298920729f216adcb002acf7ea01b90842603d2e4e2ce9b900d9ee8fab/nh3-0.3.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe3a787dc76b50de6bee54ef242f26c41dfe47654428e3e94f0fae5bb6dd2cc1", size = 806976, upload-time = "2026-04-25T10:44:01.743Z" }, - { url = "https://files.pythonhosted.org/packages/85/01/26761e1dc2b848e65a62c19e5d39ad446283287cd4afddc89f364ab86bc9/nh3-0.3.5-cp38-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:488928988caad25ba14b1eb5bc74e25e21f3b5e40341d956f3ce4a8bc19460dc", size = 834904, upload-time = "2026-04-25T10:44:03.454Z" }, - { url = "https://files.pythonhosted.org/packages/33/53/0766113e679540ac1edc1b82b1295aecd321eeb75d6fead70109a838b6ee/nh3-0.3.5-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c069570b06aa848457713ad7af4a9905691291548c4466a9ad78ee95808382b", size = 857159, upload-time = "2026-04-25T10:44:05.003Z" }, - { url = "https://files.pythonhosted.org/packages/58/36/734d353dfaf292fed574b8b3092f0ef79dc6404f3879f7faaa61a4701fad/nh3-0.3.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eeedc90ed8c42c327e8e10e621ccfa314fc6cce35d5929f4297ff1cdb89667c4", size = 1018600, upload-time = "2026-04-25T10:44:06.18Z" }, - { url = "https://files.pythonhosted.org/packages/6b/aa/d9c59c1b49669fcb7bababa55df82385f029ad5c2651f583c3a1141cfdd1/nh3-0.3.5-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:de8e8621853b6470fe928c684ee0d3f39ea8086cebafe4c416486488dea7b68d", size = 1103530, upload-time = "2026-04-25T10:44:07.68Z" }, - { url = "https://files.pythonhosted.org/packages/90/b0/cdd210bfb8d9d43fb02fc3c868336b9955934d8e15e66eb1d15a147b8af0/nh3-0.3.5-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:6ea58cc44d274c643b83547ca9654a0b1a817609b160601356f76a2b744c49ad", size = 1061754, upload-time = "2026-04-25T10:44:09.362Z" }, - { url = "https://files.pythonhosted.org/packages/ce/cb/7a39e72e668c8445bdd95e494b3e21cfdddc68329be8ea3522c8befb46c4/nh3-0.3.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e49c9b564e6bcb03ecd2f057213df9a0de15a95812ac9db9600b590db23d3ae9", size = 1040938, upload-time = "2026-04-25T10:44:10.775Z" }, - { url = "https://files.pythonhosted.org/packages/af/4c/fc2f9ed208a3801a319f59b5fea03cdc20cf3bd8af14be930d3a8de01224/nh3-0.3.5-cp38-abi3-win32.whl", hash = "sha256:559e4c73b689e9a7aa97ac9760b1bc488038d7c1a575aa4ab5a0e19ee9630c0f", size = 611445, upload-time = "2026-04-25T10:44:12.317Z" }, - { url = "https://files.pythonhosted.org/packages/db/1a/e4c9b5e2ae13e6092c9ec16d8ca30646cb01fcdea245f36c5b08fd21fbd5/nh3-0.3.5-cp38-abi3-win_amd64.whl", hash = "sha256:45e6a65dc88a300a2e3502cb9c8e6d1d6b831d6fba7470643333609c6aab1f30", size = 626502, upload-time = "2026-04-25T10:44:13.682Z" }, - { url = "https://files.pythonhosted.org/packages/80/7c/19cd0671d1ba2762fb388fc149697d20d0568ccfeef833b11280a619e526/nh3-0.3.5-cp38-abi3-win_arm64.whl", hash = "sha256:8f85285700a18e9f3fc5bff41fe573fa84f81542ef13b48a89f9fecca0474d3b", size = 611069, upload-time = "2026-04-25T10:44:14.934Z" }, +version = "0.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/1b/ef84624f14954d270f74060a19fc550dd4f06656399447569afb584d8c06/nh3-0.3.6.tar.gz", hash = "sha256:f3736c9dd3d1856f80cd031715b84ca75cda2bbb1ac802c3da26bfce590838d7", size = 24684, upload-time = "2026-06-22T00:47:02.008Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/3e/6506aa4f23dc7b7993a2d0a45dca3ce864ec48380adfe15a173e643c63e8/nh3-0.3.6-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2411e8c3cee81a1ddd62c2a5d50585c28aa5566d373ad1db92536b95ddb24ef2", size = 1421679, upload-time = "2026-06-22T00:46:20.248Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e1/e96e7864a7a53bd6b6fab7e9632467382a2a2c1f3fed951918ad131542fb/nh3-0.3.6-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e196fa70c2ff2eb4de7d3df3108f8f358c1d69dff20d45b11f20a5aa227ffb6d", size = 792570, upload-time = "2026-06-22T00:46:22.179Z" }, + { url = "https://files.pythonhosted.org/packages/59/62/5b6108bedaef2b2637fed04c87bdbcb5967b9961758b41f0e466ef22a022/nh3-0.3.6-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:34d2b0d934156b87ee114f599a3ba9b8b9e17b5d79652ba3a13fa50903de965e", size = 842243, upload-time = "2026-06-22T00:46:23.801Z" }, + { url = "https://files.pythonhosted.org/packages/4b/4a/526f199626bfcb496bc01a268051b44737962005553b158e985ed7e64865/nh3-0.3.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2f14b7ae1fca99c4a66c981aac3974e7fbc1ca30a12673d223ae1df76680917", size = 1001468, upload-time = "2026-06-22T00:46:25.481Z" }, + { url = "https://files.pythonhosted.org/packages/49/09/0d8e3101636d9ad88cdefb2914e764cb8e876ebdbb4286bfc251277d9c67/nh3-0.3.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:889932a97fb4abb6f95fef1914c0d269ebfb60011e67121c1163059b9449dbb4", size = 1082933, upload-time = "2026-06-22T00:46:27.15Z" }, + { url = "https://files.pythonhosted.org/packages/09/a1/ea83abe738a3fbaa203dfdb836ca7cbab0e7e9609faaee4fe1d4652599c0/nh3-0.3.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:edb2b4a1a27523e6cc7c417f8d21ce3d005243548b93e56b762b66b0c7f589f9", size = 1043120, upload-time = "2026-06-22T00:46:28.89Z" }, + { url = "https://files.pythonhosted.org/packages/66/69/0654482b8635012fbae67826bd6c381abb05d841ac7388b9b4666300fdad/nh3-0.3.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:43bc1ed3fa0716295fabee29ba42b2667e4a51d140b0a68e092170a765474fa6", size = 1023824, upload-time = "2026-06-22T00:46:30.453Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a6/1f7285ffadc8307c4dbeb08d21b920536d5117785056d1079e998c4dfa44/nh3-0.3.6-cp314-cp314t-win32.whl", hash = "sha256:597a8e843bea00b2eb5520658dc24a9bb032e7fc9e7c2c0c4cd29420220c9796", size = 599253, upload-time = "2026-06-22T00:46:32.072Z" }, + { url = "https://files.pythonhosted.org/packages/36/ea/5542f3c45da4c00290d9d67a65e996702e23e613c4b627de3e09cb9fe357/nh3-0.3.6-cp314-cp314t-win_amd64.whl", hash = "sha256:4713502748f564fee0633b37b3403783ce0a3af3a3d148ad91025a5bdadb7bc6", size = 612553, upload-time = "2026-06-22T00:46:33.53Z" }, + { url = "https://files.pythonhosted.org/packages/66/35/26bd47e6af5915a628281dccdac354ddf4e32f7397047894270acd8c9870/nh3-0.3.6-cp314-cp314t-win_arm64.whl", hash = "sha256:69bbb92865a693d909db3a700d3c01537533844d0948c1e9323561ce06ecda41", size = 595151, upload-time = "2026-06-22T00:46:34.878Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ab/a7653bce9a3b204be6a6931767a9e23595807bb84790ce6685e4d7e5bd08/nh3-0.3.6-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a43ebd7543555c3ac1bc353023d0794e75cb76f6f18f19c32e95441496c0cc25", size = 1443564, upload-time = "2026-06-22T00:46:36.66Z" }, + { url = "https://files.pythonhosted.org/packages/41/21/e1084ab18eb589506335c7c7576f2d4643e9a0c0e33983ef0e549a256b96/nh3-0.3.6-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1b160831c9cdb06a6c79c2f9cdb11386602938f9af260d1c457a85add4f6f69", size = 838002, upload-time = "2026-06-22T00:46:38.101Z" }, + { url = "https://files.pythonhosted.org/packages/b0/94/f48d08e6f72a406300fa11d8acd929fea1a80d4bf750fa292cb10785f126/nh3-0.3.6-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d14bf7982e7a77c0c775634c29c07ce08b38a046df73e1c1f139b3e82f18a38e", size = 823045, upload-time = "2026-06-22T00:46:39.495Z" }, + { url = "https://files.pythonhosted.org/packages/25/bb/431615ba1d1d3eb63cde0f974f2114edf863a8a3f6049a12fed23fc241d3/nh3-0.3.6-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:44673b27010051ab5a5e438a86ec31bbda61d4a77d7e900af6b7be3037c1abae", size = 1093171, upload-time = "2026-06-22T00:46:41.21Z" }, + { url = "https://files.pythonhosted.org/packages/0e/24/a0d80182a18919665fefd19c1c06f1d1df1c9a6455d0252de40c034a0bc3/nh3-0.3.6-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6b7beece07525dc6e6b0fc2f104442de2ba328360ad00e50cbe2e1fd620447d", size = 1049217, upload-time = "2026-06-22T00:46:42.804Z" }, + { url = "https://files.pythonhosted.org/packages/0a/13/6f1e302ca674ac74362e150848ad56a1be5145391204f74facdb8e94df12/nh3-0.3.6-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:455469a29951edc92bc48b47ac2281c3f2609e6c4f6a047056449f8c2c23facf", size = 917372, upload-time = "2026-06-22T00:46:44.495Z" }, + { url = "https://files.pythonhosted.org/packages/5b/67/314f6151bad77a93d751978a344033e1fc890822f05f0416079338e34231/nh3-0.3.6-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:905f877dc66dd7aea4a76e54bcb26acb5ff8216f720c0017ccf63e0e6035698e", size = 806699, upload-time = "2026-06-22T00:46:45.99Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a6/bfaa00046e58603507dcfc266c4778e3ab7adf68a5dedd73b6274b8d9314/nh3-0.3.6-cp38-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:25c733bee928530556b1db0ea46c52cf5aa686146e38e60a6fc7cb801ef91cec", size = 835165, upload-time = "2026-06-22T00:46:47.617Z" }, + { url = "https://files.pythonhosted.org/packages/30/a8/fb2c38845efb703a9173bffdfc745fc64d2b0e55cfc73a3647d2f028250c/nh3-0.3.6-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f90d9a0cfdbee218994fdaaeeb5a0fde62d08f35e4eef0378ec1e2200172fd0", size = 858282, upload-time = "2026-06-22T00:46:49.276Z" }, + { url = "https://files.pythonhosted.org/packages/68/17/06e72a18ee9b572914447338237ca7eb164c0df901f141bc10d1282247a2/nh3-0.3.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:82ca5bf427ad1b216b65ede1a2e2d87dc49bec417ceba0f297213107d3cd9d78", size = 1014328, upload-time = "2026-06-22T00:46:51.026Z" }, + { url = "https://files.pythonhosted.org/packages/11/f9/3966c61455668c08853bf5e33b4bed93c421f3194ce4de896dc248d6f6ce/nh3-0.3.6-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:f5ed5fe84aee7f39db95c214a7421bf0499fbf500fec6d86a4e29bfc37971438", size = 1098207, upload-time = "2026-06-22T00:46:52.674Z" }, + { url = "https://files.pythonhosted.org/packages/19/d3/479cb4ae440424825735d60525b53e3c77fd60fd6e6afc0e984f00eb0178/nh3-0.3.6-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:082675ff87b9385ec430ffe6d5847ba7456cc39b73720cd4add472f9f4cffd56", size = 1056961, upload-time = "2026-06-22T00:46:54.335Z" }, + { url = "https://files.pythonhosted.org/packages/17/0c/6cdb5ee1e127be50dc8391e54bddc1f64e87bf4bfad0c55633320e2e02db/nh3-0.3.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36d06341bd501240d320f5942481ed5e6846136b666e1ba4faf802b78ebc875f", size = 1033829, upload-time = "2026-06-22T00:46:56.258Z" }, + { url = "https://files.pythonhosted.org/packages/e9/55/9de666ad975d6ccd77d799ea0add55ee2347aa81286ce21b2a97c070746b/nh3-0.3.6-cp38-abi3-win32.whl", hash = "sha256:5276ef17bdba9ad8040575c74072008b13aae429436e9d0429e718bb5f90f4da", size = 609081, upload-time = "2026-06-22T00:46:57.665Z" }, + { url = "https://files.pythonhosted.org/packages/82/fa/2b5d684e3edf1e81bfd02d298c78c3e3da77ca1d8a2be3183a79544a7548/nh3-0.3.6-cp38-abi3-win_amd64.whl", hash = "sha256:f338ac7d594c067679f1e99b4f5ec3906842979560f9d8f15d6bdfa39a353b10", size = 624461, upload-time = "2026-06-22T00:46:59.163Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e5/7cafee2f0413ca4cb0ef3bd111e94d408a48810008b283ad8aee00dd1809/nh3-0.3.6-cp38-abi3-win_arm64.whl", hash = "sha256:69f365963f63a1e9bff53bdbb3c542c7c2efed3e163c9d5d83a772a2ac468c21", size = 603060, upload-time = "2026-06-22T00:47:00.596Z" }, ] [[package]] @@ -3472,7 +2801,7 @@ wheels = [ [[package]] name = "openai" -version = "2.41.1" +version = "2.45.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -3484,14 +2813,14 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/36/4c926a91554483977608951360c18c2e911592785eb87a6437813f6123f7/openai-2.41.1.tar.gz", hash = "sha256:23d617a0432457ad844973bee8f540be9da90894f7c5686852d2d365da058f57", size = 783584, upload-time = "2026-06-10T16:10:37.667Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/60/d4219875289b11d2c2f7da93c36283da224a2e55865ed865ab64e0ce9217/openai-2.45.0.tar.gz", hash = "sha256:10d34ca9c5643bce775852fddbfc172505cb1d4de1ccd101696c3ecff358765d", size = 1109653, upload-time = "2026-07-09T18:02:44.091Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/74/925d7b3892927e9804aaf58d374a45dc28e4420ff90e992272b77286343e/openai-2.41.1-py3-none-any.whl", hash = "sha256:a939565f350cb7443cb843b801b88c716ac8024b492fb94ca269d5f6b1bbefd6", size = 1353380, upload-time = "2026-06-10T16:10:35.756Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b0/2291689e3ec4723fbf5bbf3b54afcd7b160f9ddc98ca7aedfd0132af5677/openai-2.45.0-py3-none-any.whl", hash = "sha256:5df105f5f8c9b711fcb9d06d2d3888cebc82506db216484c14a4e53cdf651777", size = 1629470, upload-time = "2026-07-09T18:02:42.21Z" }, ] [[package]] name = "openai-agents" -version = "0.17.7" +version = "0.18.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "griffelib" }, @@ -3502,14 +2831,14 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/b2/235cbfdefe86623fc77f65fc1d016686372f61d4a1bf3fc66151de2eb847/openai_agents-0.17.7.tar.gz", hash = "sha256:ca76e7f882c9d8f06e3dfb8064cc33bcb5a5f34a29816cb9af863f395964ff0c", size = 5485068, upload-time = "2026-06-24T05:15:33.705Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/0c/52e9aeff5549b225d5666a0eb84a8a22b4c47db08b6f44dbd45876fcfba3/openai_agents-0.18.2.tar.gz", hash = "sha256:9f418bb563eddff1e01f245ae8a4964b7649396f444b569b4113d105e41ca1d3", size = 5546139, upload-time = "2026-07-11T01:08:18.537Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/2e/2e96ca6928951fe1d16744c22dc1355eda1dd5b0dd920ca1d3ab602929f8/openai_agents-0.17.7-py3-none-any.whl", hash = "sha256:51b5ae43756eea37032e430f95979ba3999af6b1ade397df6c0ffeaf1939646a", size = 856074, upload-time = "2026-06-24T05:15:31.741Z" }, + { url = "https://files.pythonhosted.org/packages/9c/23/b5b6b80a3e36f021ca2a8c4637684f0d722d646b19d3616768a68802c302/openai_agents-0.18.2-py3-none-any.whl", hash = "sha256:c7aea341b256a90b87b17b7e444bab29a12655864a2f0094f65561223d867185", size = 874310, upload-time = "2026-07-11T01:08:16.851Z" }, ] [package.optional-dependencies] litellm = [ - { name = "litellm", marker = "python_full_version < '3.14'" }, + { name = "litellm" }, ] [[package]] @@ -3548,7 +2877,7 @@ wheels = [ [[package]] name = "openinference-instrumentation" -version = "0.1.53" +version = "0.1.54" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "openinference-semantic-conventions" }, @@ -3556,14 +2885,14 @@ dependencies = [ { name = "opentelemetry-sdk" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f9/b6/c0e7e047ae4962f2755a3bc9141fdd6272c75c74e47dfc6aa71978a9b78f/openinference_instrumentation-0.1.53.tar.gz", hash = "sha256:3c0c145cf6e13cfa630b29d0e3ca806f3821470ffca7922f1590e3970fadd4da", size = 33712, upload-time = "2026-06-02T16:37:21.771Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/cc/62c1175ee7edc2cbdf95b5b73e0b0f305e759d407bf1bc353ff30a763365/openinference_instrumentation-0.1.54.tar.gz", hash = "sha256:9af9817bb38816ed32856fb4cd813c1a5d9f530ab589c3473b37e06cf406ab28", size = 33938, upload-time = "2026-06-30T19:23:15.648Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/bb/01262d9945c476e15aa21bb9ca05b18604e525d73d9761cadb677f485198/openinference_instrumentation-0.1.53-py3-none-any.whl", hash = "sha256:f43695080eded47b1e03ff1b19cb5c23ea4409459cfe16c5b5748d5656832eb1", size = 40958, upload-time = "2026-06-02T16:37:20.69Z" }, + { url = "https://files.pythonhosted.org/packages/38/e3/c7aa7bb4845e0cfdf477ff87f9a3ec0cd2aa55a34a9f31be84d724cabbb7/openinference_instrumentation-0.1.54-py3-none-any.whl", hash = "sha256:8bc991865c90c804ac9983ef93aa6081a7a2397dd113d3d38b5465cca17892bb", size = 41197, upload-time = "2026-06-30T19:23:14.315Z" }, ] [[package]] name = "openinference-instrumentation-google-adk" -version = "0.1.15" +version = "0.1.17" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "openinference-instrumentation" }, @@ -3574,9 +2903,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1b/1a/f35f3f38dba763e3ab41a73c15125de6107b3dd1aacbff50201b945e7d89/openinference_instrumentation_google_adk-0.1.15.tar.gz", hash = "sha256:1c0c73ad3b128858486f2066ceba3690061cbb8755bcd97a58220d9a5a42cf8e", size = 14739, upload-time = "2026-05-22T21:10:48.449Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/c2/8400125398ac568efe49bc8a9411a4c860b27112279d52d2bb8acbc70126/openinference_instrumentation_google_adk-0.1.17.tar.gz", hash = "sha256:8235d2cf3fce5edaf213136fbd00e876db183add362b2edede87cd42b21ab112", size = 14844, upload-time = "2026-07-01T15:44:19.524Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/24/dbddd058a5b837c5f50a42dd94b4d9e338dc362c3cf067a6620c18a7f5c3/openinference_instrumentation_google_adk-0.1.15-py3-none-any.whl", hash = "sha256:be6db6bb68922acae5103bbb72fda9b880a809309b49289fa23d685742de8ebe", size = 16661, upload-time = "2026-05-22T21:10:46.054Z" }, + { url = "https://files.pythonhosted.org/packages/27/c0/b0c301a0a1de9fe377e666e89b9e87fa368bd5ac1341ab125624e2bbc081/openinference_instrumentation_google_adk-0.1.17-py3-none-any.whl", hash = "sha256:3f45e38cfd5ffb41c18deddc747e48c68595d4dfa1a89b7b1aa1f3d31e46ce0d", size = 16755, upload-time = "2026-07-01T15:44:18.364Z" }, ] [[package]] @@ -3608,77 +2937,31 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.41.1" +version = "1.42.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/fc/b7564cbef36601aef0d6c9bc01f7badb64be8e862c2e1c3c5c3b43b53e4f/opentelemetry_api-1.41.1.tar.gz", hash = "sha256:0ad1814d73b875f84494387dae86ce0b12c68556331ce6ce8fe789197c949621", size = 71416, upload-time = "2026-04-24T13:15:38.262Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/59/3e7118ed140f76b0982ba4321bdaed1997a0473f9720de2d10788a577033/opentelemetry_api-1.41.1-py3-none-any.whl", hash = "sha256:a22df900e75c76dc08440710e51f52f1aa6b451b429298896023e60db5b3139f", size = 69007, upload-time = "2026-04-24T13:15:15.662Z" }, -] - -[[package]] -name = "opentelemetry-exporter-gcp-logging" -version = "1.12.0a0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-cloud-logging" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-resourcedetector-gcp" }, - { name = "opentelemetry-sdk" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/77/e4/95ecebaa1c5134adaa0d0374028b25e3b3c5c08535d29a66d39d372a3d11/opentelemetry_exporter_gcp_logging-1.12.0a0.tar.gz", hash = "sha256:586529dbbcae5e22b880f7c121fde3f0fe8ae997aba1bad53f13c20eeb27cb3a", size = 22521, upload-time = "2026-04-28T20:59:40.237Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/55/93/3a0a9a62db0b90029a8160774e791044c0566aa94d5160ce7bbce8abf242/opentelemetry_exporter_gcp_logging-1.12.0a0-py3-none-any.whl", hash = "sha256:2aca9b01b3248c2fa95d38d01aa71aca8e22f640c44dba36ca6b883930762971", size = 14207, upload-time = "2026-04-28T20:59:35.109Z" }, -] - -[[package]] -name = "opentelemetry-exporter-gcp-monitoring" -version = "1.12.0a0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-cloud-monitoring" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-resourcedetector-gcp" }, - { name = "opentelemetry-sdk" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/f82b2858d00be6f91b917dc67ccf71688fa822448b2d26ace69b809f5835/opentelemetry_exporter_gcp_monitoring-1.12.0a0.tar.gz", hash = "sha256:2b285078cddd4af78a363a55b5478e89f7df6f15bba9139d3f484099e534df4c", size = 20839, upload-time = "2026-04-28T20:59:40.982Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/b5/1623886d049095bb5abcec0cd67a0e40c00ff1672a25f82ed9867f88c1e7/opentelemetry_exporter_gcp_monitoring-1.12.0a0-py3-none-any.whl", hash = "sha256:1a7daf8c9350d55010fa33d2c2f646655a03a81d0d8073a2ae0e066791d6177d", size = 13608, upload-time = "2026-04-28T20:59:36.315Z" }, -] - -[[package]] -name = "opentelemetry-exporter-gcp-trace" -version = "1.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-cloud-trace" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-resourcedetector-gcp" }, - { name = "opentelemetry-sdk" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bf/55/32922e72d88421505383dfdba9c1ee6ad67253f94f2358f6e9dbc4ac3749/opentelemetry_exporter_gcp_trace-1.12.0.tar.gz", hash = "sha256:18c6e56fe123eed020d5005fdd819b196d64f651545bce1ca7e2e2cbaf9d343b", size = 18779, upload-time = "2026-04-28T20:59:41.974Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/1c/125e1c936c0873796771b7f04f6c93b9f1bf5d424cea90fda94a99f61da8/opentelemetry_api-1.42.1.tar.gz", hash = "sha256:56c63bea9f77b62856be8c47600474acad853b2924b99b1687c4cb6297166716", size = 72296, upload-time = "2026-05-21T16:32:49.335Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/68/c60e79992918eecb6de167e782c86946fdd5492bb163fe320f1a18959c3d/opentelemetry_exporter_gcp_trace-1.12.0-py3-none-any.whl", hash = "sha256:1538dab654bcb25e757ed34c94f27a2e30d90dc7deb3630f8d46d1111fcb3bad", size = 14013, upload-time = "2026-04-28T20:59:37.518Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ca/9520cc1f3dfbbd03ac5903bbf55833e257bc64b1cf30fa8b0d6df374d821/opentelemetry_api-1.42.1-py3-none-any.whl", hash = "sha256:51a69edacadbc03a8950ace1c4c21099cacc538820ac2c9e36277e78cebba714", size = 61311, upload-time = "2026-05-21T16:32:28.822Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.41.1" +version = "1.42.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-proto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/fa/f9e3bd3c4d692b3ce9a2880a167d1f79681a1bea11f00d5bf76adc03e6ea/opentelemetry_exporter_otlp_proto_common-1.41.1.tar.gz", hash = "sha256:0e253156ea9c36b0bd3d2440c5c9ba7dd1f3fb64ba7a08fc85fbac536b56e1fb", size = 20409, upload-time = "2026-04-24T13:15:40.924Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/9c/216acfeaedadf2e1937f4373929b20f73197c5c4a2546d4f584b7fa63813/opentelemetry_exporter_otlp_proto_common-1.42.1.tar.gz", hash = "sha256:04f1f01fb597c4249dfcd7f8b861c902c2102369d376d9d346ff38de4469a2ee", size = 21433, upload-time = "2026-05-21T16:32:55.526Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/48/bce76d3ea772b609757e9bc844e02ab408a6446609bf74fb562062ba6b71/opentelemetry_exporter_otlp_proto_common-1.41.1-py3-none-any.whl", hash = "sha256:10da74dad6a49344b9b7b21b6182e3060373a235fde1528616d5f01f92e66aa9", size = 18366, upload-time = "2026-04-24T13:15:18.917Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/2375e7612e1121a4518c17603b6e0b03ad94f565aafad53f464dc5be2bf6/opentelemetry_exporter_otlp_proto_common-1.42.1-py3-none-any.whl", hash = "sha256:f48d395ab815b444da118868977e9798ea354c25737d5cf39578ae894011c140", size = 17327, upload-time = "2026-05-21T16:32:33.387Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.41.1" +version = "1.42.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, @@ -3689,32 +2972,14 @@ dependencies = [ { name = "opentelemetry-sdk" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1e/9b/e4503060b8695579dbaad187dc8cef4554188de68748c88060599b77489e/opentelemetry_exporter_otlp_proto_grpc-1.41.1.tar.gz", hash = "sha256:b05df8fa1333dc9a3fda36b676b96b5095ab6016d3f0c3296d430d629ba1443b", size = 25755, upload-time = "2026-04-24T13:15:41.93Z" } +sdist = { url = "https://files.pythonhosted.org/packages/87/87/ca7fc790dfdbcf4f9e9aab14a39ef1b7508ead13707e283de0b3131478d2/opentelemetry_exporter_otlp_proto_grpc-1.42.1.tar.gz", hash = "sha256:975c4461f167dd8ed8857d68d3b6b25f3d272eab896f6a9470d0f5b90e2faf15", size = 27140, upload-time = "2026-05-21T16:32:56.162Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/f2/c54f33c92443d087703e57e52e55f22f111373a5c4c4aa349ea60efe512e/opentelemetry_exporter_otlp_proto_grpc-1.41.1-py3-none-any.whl", hash = "sha256:537926dcef951136992479af1d9cd88f25e33d56c530e9f020ed57774dca2f94", size = 20297, upload-time = "2026-04-24T13:15:20.212Z" }, -] - -[[package]] -name = "opentelemetry-exporter-otlp-proto-http" -version = "1.41.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "googleapis-common-protos" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-otlp-proto-common" }, - { name = "opentelemetry-proto" }, - { name = "opentelemetry-sdk" }, - { name = "requests" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/33/5b/9d3c7f70cca10136ba82a81e738dee626c8e7fc61c6887ea9a58bf34c606/opentelemetry_exporter_otlp_proto_http-1.41.1.tar.gz", hash = "sha256:4747a9604c8550ab38c6fd6180e2fcb80de3267060bef2c306bad3cb443302bc", size = 24139, upload-time = "2026-04-24T13:15:42.977Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/4d/ef07ff2fc630849f2080ae0ae73a61f67257905b7ac79066640bfa0c5739/opentelemetry_exporter_otlp_proto_http-1.41.1-py3-none-any.whl", hash = "sha256:1a21e8f49c7a946d935551e90947d6c3eb39236723c6624401da0f33d68edcb4", size = 22673, upload-time = "2026-04-24T13:15:21.313Z" }, + { url = "https://files.pythonhosted.org/packages/89/2b/28ba5b128f47fe8c3bab541000d6feb4b5a9bd26623ca013406f01c0fb60/opentelemetry_exporter_otlp_proto_grpc-1.42.1-py3-none-any.whl", hash = "sha256:0ae1177e2038b18a929b3098215243631ef91136cba26b7e2b12790ceb7e87cc", size = 19617, upload-time = "2026-05-21T16:32:34.278Z" }, ] [[package]] name = "opentelemetry-instrumentation" -version = "0.62b1" +version = "0.63b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -3722,64 +2987,49 @@ dependencies = [ { name = "packaging" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/cb/0523b92c112a6cc70be43724343dc45225d3af134419844d7879a07755d4/opentelemetry_instrumentation-0.62b1.tar.gz", hash = "sha256:90e92a905ba4f84db06ac3aec96701df6c079b2d66e9379f8739f0a1bdcc7f45", size = 34043, upload-time = "2026-04-24T13:22:31.997Z" } +sdist = { url = "https://files.pythonhosted.org/packages/da/6d/4de72d97ff54db1ed270c7a59c9b904b917c0ac7af429c086c388b824ddb/opentelemetry_instrumentation-0.63b1.tar.gz", hash = "sha256:32368d6ae52c8de20aa790a6ad86b10a76f09956092337ae37d675773990e541", size = 41081, upload-time = "2026-05-21T16:36:14.206Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/0f/45adbaea1f81b847cffdcee4f4b5f89297e42facf7fac78c7aaac4c38e75/opentelemetry_instrumentation-0.62b1-py3-none-any.whl", hash = "sha256:976fc6e640f2006599e97429c949e622c108d0c17c2059347d1e6c93c707f257", size = 34163, upload-time = "2026-04-24T13:21:31.722Z" }, + { url = "https://files.pythonhosted.org/packages/35/a1/9314e621c143e4d82a5bf7a43c2ff7a745d31023506336857607c8c543cc/opentelemetry_instrumentation-0.63b1-py3-none-any.whl", hash = "sha256:f1986716d52cc316ea5f60189098726a9071d8ecc0eee96c9ed110be08bade9c", size = 35577, upload-time = "2026-05-21T16:34:56.818Z" }, ] [[package]] name = "opentelemetry-instrumentation-threading" -version = "0.62b1" +version = "0.63b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-instrumentation" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/2d/2537d5990fa341198cbc8ae70b2c3637037061b8ab1196af1d924a275f55/opentelemetry_instrumentation_threading-0.62b1.tar.gz", hash = "sha256:4b3c876907657e3b8b977bfe15d248f2c02db56302c51883724e7ac2f8ce26d2", size = 9180, upload-time = "2026-04-24T13:23:06.15Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/90/7b0279192fab614d57af3d57584ef7ac9e38fa3df0b1d412224f6f55a85b/opentelemetry_instrumentation_threading-0.63b1.tar.gz", hash = "sha256:afa8c2cada8ed136f07b04dc8739bc861a15e9a5edea1a65e4c5e1919c62946c", size = 9080, upload-time = "2026-05-21T16:36:49.977Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/37/a80fb13b76f85b4e433ff44b4ba177615823c36c28dca12e94d2c37de681/opentelemetry_instrumentation_threading-0.62b1-py3-none-any.whl", hash = "sha256:4596e79c47de122eb2e85877c1a8bfed1cd6ab06bd2c29d120ebcf8a708a433a", size = 9335, upload-time = "2026-04-24T13:22:19.419Z" }, + { url = "https://files.pythonhosted.org/packages/c9/3d/3a991a4fcdf5ac82c04215e38cea4e73ad63713707014f9a70d1ab257f5f/opentelemetry_instrumentation_threading-0.63b1-py3-none-any.whl", hash = "sha256:33059298e68c94b13c38b562ad28799ec16a2fd06182ebfc762bb4e956e55d94", size = 8486, upload-time = "2026-05-21T16:35:58.084Z" }, ] [[package]] name = "opentelemetry-proto" -version = "1.41.1" +version = "1.42.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/e8/633c6d8a9c8840338b105907e55c32d3da1983abab5e52f899f72a82c3d1/opentelemetry_proto-1.41.1.tar.gz", hash = "sha256:4b9d2eb631237ea43b80e16c073af438554e32bc7e9e3f8ca4a9582f900020e5", size = 45670, upload-time = "2026-04-24T13:15:49.768Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/55/63eac3e1089b768ba014091fdd2ae8a9a440c821ef5e2b786909c94c8836/opentelemetry_proto-1.42.1.tar.gz", hash = "sha256:c6a51e6b4f05ae63565f3a113217f3d2bfaec68f78c02d7a6c85f9010d1cfca6", size = 45839, upload-time = "2026-05-21T16:33:03.937Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/1e/5cd77035e3e82070e2265a63a760f715aacd3cb16dddc7efee913f297fcc/opentelemetry_proto-1.41.1-py3-none-any.whl", hash = "sha256:0496713b804d127a4147e32849fbaf5683fac8ee98550e8e7679cd706c289720", size = 72076, upload-time = "2026-04-24T13:15:32.542Z" }, -] - -[[package]] -name = "opentelemetry-resourcedetector-gcp" -version = "1.12.0a0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "requests" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/21/ae/b62c5e986c9c7f908a15682ea173bcfcdc00403c0c85243ccbd30eca7fc2/opentelemetry_resourcedetector_gcp-1.12.0a0.tar.gz", hash = "sha256:d5e3f78283a272eb92547e00bbeff45b7332a34ae791a70ab4eba81af9bc3baf", size = 18797, upload-time = "2026-04-28T20:59:43.195Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/84/9db2999adbc41505af3e6717e8d958746778cbfc9e07ed9c670bf9d1e6db/opentelemetry_resourcedetector_gcp-1.12.0a0-py3-none-any.whl", hash = "sha256:e803688d14e2969fe816077be81f7b034368314d485863f12ce49daba7c81919", size = 18798, upload-time = "2026-04-28T20:59:39.257Z" }, + { url = "https://files.pythonhosted.org/packages/41/9d/171c02c84a76940b7e601805b3bb536985aded9168fbcc9ba52f0a730fa2/opentelemetry_proto-1.42.1-py3-none-any.whl", hash = "sha256:dedb74cba2886c59c7789b227a7a670613025a07489040050aedff6e5c0fb43c", size = 71782, upload-time = "2026-05-21T16:32:44.867Z" }, ] [[package]] name = "opentelemetry-sdk" -version = "1.41.1" +version = "1.42.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/58/d0/54ee30dab82fb0acda23d144502771ff76ef8728459c83c3e89ef9fb1825/opentelemetry_sdk-1.41.1.tar.gz", hash = "sha256:724b615e1215b5aeacda0abb8a6a8922c9a1853068948bd0bd225a56d0c792e6", size = 230180, upload-time = "2026-04-24T13:15:50.991Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/f7/b390bd9bfd703bf98a68fea1f27786c6872331fd617164a54b8a59bdc008/opentelemetry_sdk-1.42.1.tar.gz", hash = "sha256:8c834e8f8c9ba4171d4ec843d0cb8a67e4c7394d3f9e9297e582cbd9456ddbf7", size = 239262, upload-time = "2026-05-21T16:33:04.641Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/e7/a1420b698aad018e1cf60fdbaaccbe49021fb415e2a0d81c242f4c518f54/opentelemetry_sdk-1.41.1-py3-none-any.whl", hash = "sha256:edee379c126c1bce952b0c812b48fe8ff35b30df0eecf17e98afa4d598b7d85d", size = 180213, upload-time = "2026-04-24T13:15:33.767Z" }, + { url = "https://files.pythonhosted.org/packages/8f/6b/4287766cfbde577ae2272e8884abac325aeaac0d64f41c61d5b8cc595105/opentelemetry_sdk-1.42.1-py3-none-any.whl", hash = "sha256:083cd4bbfaa5aa7b5a9e552430d9951219967cfb27aa61feb13a77aba1fc839d", size = 170907, upload-time = "2026-05-21T16:32:45.894Z" }, ] [[package]] @@ -3796,15 +3046,15 @@ wheels = [ [[package]] name = "opentelemetry-semantic-conventions" -version = "0.62b1" +version = "0.63b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/de/911ac9e309052aca1b20b2d5549d3db45d1011e1a610e552c6ccdd1b64f8/opentelemetry_semantic_conventions-0.62b1.tar.gz", hash = "sha256:c5cc6e04a7f8c7cdd30be2ed81499fa4e75bfbd52c9cb70d40af1f9cd3619802", size = 145750, upload-time = "2026-04-24T13:15:52.236Z" } +sdist = { url = "https://files.pythonhosted.org/packages/93/99/4d7dd6df64795951413ce6e815f8cf1eb191daf7196ae86574589643d5f3/opentelemetry_semantic_conventions-0.63b1.tar.gz", hash = "sha256:3daf963611334b365e98a57438183eb012d3bfb40b2d931a9af613476b8701a9", size = 148340, upload-time = "2026-05-21T16:33:05.455Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/a6/83dc2ab6fa397ee66fba04fe2e74bdf7be3b3870005359ceb7689103c058/opentelemetry_semantic_conventions-0.62b1-py3-none-any.whl", hash = "sha256:cf506938103d331fbb78eded0d9788095f7fd59016f2bda813c3324e5a74a93c", size = 231620, upload-time = "2026-04-24T13:15:35.454Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7a/7fe66f5f3682b1dd47d88cc4e11f1c6c0966b737de2d16671146e23c39a5/opentelemetry_semantic_conventions-0.63b1-py3-none-any.whl", hash = "sha256:dfe5ef4dee82586b746f522b818ceb298d00b3d59f660042bd79404bff8d0682", size = 203713, upload-time = "2026-05-21T16:32:47.016Z" }, ] [[package]] @@ -3973,100 +3223,96 @@ wheels = [ [[package]] name = "pillow" -version = "12.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/aa/d0b28e1c811cd4d5f5c2bfe2e022292bd255ae5744a3b9ac7d6c8f72dd75/pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f", size = 5354355, upload-time = "2026-04-01T14:42:15.402Z" }, - { url = "https://files.pythonhosted.org/packages/27/8e/1d5b39b8ae2bd7650d0c7b6abb9602d16043ead9ebbfef4bc4047454da2a/pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97", size = 4695871, upload-time = "2026-04-01T14:42:18.234Z" }, - { url = "https://files.pythonhosted.org/packages/f0/c5/dcb7a6ca6b7d3be41a76958e90018d56c8462166b3ef223150360850c8da/pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff", size = 6269734, upload-time = "2026-04-01T14:42:20.608Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f1/aa1bb13b2f4eba914e9637893c73f2af8e48d7d4023b9d3750d4c5eb2d0c/pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec", size = 8076080, upload-time = "2026-04-01T14:42:23.095Z" }, - { url = "https://files.pythonhosted.org/packages/a1/2a/8c79d6a53169937784604a8ae8d77e45888c41537f7f6f65ed1f407fe66d/pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136", size = 6382236, upload-time = "2026-04-01T14:42:25.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/42/bbcb6051030e1e421d103ce7a8ecadf837aa2f39b8f82ef1a8d37c3d4ebc/pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c", size = 7070220, upload-time = "2026-04-01T14:42:28.68Z" }, - { url = "https://files.pythonhosted.org/packages/3f/e1/c2a7d6dd8cfa6b231227da096fd2d58754bab3603b9d73bf609d3c18b64f/pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3", size = 6493124, upload-time = "2026-04-01T14:42:31.579Z" }, - { url = "https://files.pythonhosted.org/packages/5f/41/7c8617da5d32e1d2f026e509484fdb6f3ad7efaef1749a0c1928adbb099e/pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa", size = 7194324, upload-time = "2026-04-01T14:42:34.615Z" }, - { url = "https://files.pythonhosted.org/packages/2d/de/a777627e19fd6d62f84070ee1521adde5eeda4855b5cf60fe0b149118bca/pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032", size = 6376363, upload-time = "2026-04-01T14:42:37.19Z" }, - { url = "https://files.pythonhosted.org/packages/e7/34/fc4cb5204896465842767b96d250c08410f01f2f28afc43b257de842eed5/pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5", size = 7083523, upload-time = "2026-04-01T14:42:39.62Z" }, - { url = "https://files.pythonhosted.org/packages/2d/a0/32852d36bc7709f14dc3f64f929a275e958ad8c19a6deba9610d458e28b3/pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024", size = 2463318, upload-time = "2026-04-01T14:42:42.063Z" }, - { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" }, - { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" }, - { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" }, - { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" }, - { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" }, - { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" }, - { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" }, - { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" }, - { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149, upload-time = "2026-04-01T14:43:05.274Z" }, - { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626, upload-time = "2026-04-01T14:43:08.557Z" }, - { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531, upload-time = "2026-04-01T14:43:10.743Z" }, - { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, - { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, - { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, - { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, - { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, - { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, - { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, - { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, - { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, - { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, - { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, - { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, - { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, - { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, - { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, - { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, - { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, - { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, - { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, - { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, - { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, - { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, - { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, - { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, - { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, - { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, - { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, - { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, - { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, - { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, - { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, - { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, - { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, - { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, - { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, - { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, - { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, - { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, - { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, - { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, - { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, - { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, - { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, - { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, - { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, - { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, - { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, - { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, - { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" }, - { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" }, - { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" }, - { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" }, - { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" }, - { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" }, +version = "12.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/c2/669d88644cddb1485bd9534e63e8cf476c8e51cb3c3a1297677023505c0e/pillow-12.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6c0016e7b354317c4e9e525b937ac8596c38d2d232b419529b9cd7a1cd46e39a", size = 5392418, upload-time = "2026-07-01T11:53:27.808Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ba/3762f376a2948e3036488d773a146e0ae6ecc2ca03ac20e2615bd0b2ba02/pillow-12.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bcc33feacfaefce60c12fd500a277533bdc02b10a19f7f6d348763d8140bbba7", size = 4785287, upload-time = "2026-07-01T11:53:29.761Z" }, + { url = "https://files.pythonhosted.org/packages/07/50/b5d688cc9c52d4482f3d5bcab6ce20bc2a74a85d2343841c907444a3be2c/pillow-12.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5594fc43d548a7ed94949d139aa1341b270f1863f11cfd37f5a6c8b778a6b67f", size = 6253754, upload-time = "2026-07-01T11:53:32.298Z" }, + { url = "https://files.pythonhosted.org/packages/4e/89/36f4cd76cf4baf05c50ababb976249153f18c959171c7f6ba09a6f217260/pillow-12.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0606c8bf2cdefea14a43530f7657cbbb7ecf1c4222512492ef4a4434a9501ec", size = 6925605, upload-time = "2026-07-01T11:53:34.487Z" }, + { url = "https://files.pythonhosted.org/packages/eb/c0/4de58cf6633b9e3a6061ef4be6fb91fc3c90b812ece886f531e3c523d777/pillow-12.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:85f998ea1848bc6757289e739cfbdda3a04adfd58b02fc018ce54d754a5ce468", size = 6327788, upload-time = "2026-07-01T11:53:36.433Z" }, + { url = "https://files.pythonhosted.org/packages/87/3c/14d53682a19550dbbaf3b598f807d5457646c510805a44c7d7891cd1cd1a/pillow-12.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:25b9b82bb22e6e2b3cd07b39c68b7b862001226cb3dff7130d1cb914121b39ed", size = 7036288, upload-time = "2026-07-01T11:53:38.712Z" }, + { url = "https://files.pythonhosted.org/packages/38/1d/36279e3c77efe034e4cc2b0393ee74ffdb5a62391dacbf9b916154f5f0b8/pillow-12.3.0-cp310-cp310-win32.whl", hash = "sha256:37dc8f7bbb66efe481bb60defacef820c950c24713fb44962ed6aa2a50966de1", size = 6472396, upload-time = "2026-07-01T11:53:40.781Z" }, + { url = "https://files.pythonhosted.org/packages/48/7c/8fa0039574c476d7c6fa57dd7c32a130436877c6ec1e5ce1cc8ec44878c1/pillow-12.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:300557495eb45ebb8aec96c2da9c4be642fbf7cd937278b4013ba894ea8eb0eb", size = 7226887, upload-time = "2026-07-01T11:53:42.764Z" }, + { url = "https://files.pythonhosted.org/packages/fa/17/e324be141d173c1c919428066c3259f21c1b8982e564e01a4a81e96dbdcf/pillow-12.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:514435a37670e3e5e08f3945b68718b6ed329bb84367777e16f9f4dfe1e61a0f", size = 2568039, upload-time = "2026-07-01T11:53:45.372Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c8/0a78b0e02d7ac54bc03e5321c9220da52f0c2ea83b21f7c40e7f3169c502/pillow-12.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756", size = 5392415, upload-time = "2026-07-01T11:53:47.162Z" }, + { url = "https://files.pythonhosted.org/packages/b2/5b/a02d30018abd97ced9f5a6c63d28597694a00d066516b9c1c6de45859fc9/pillow-12.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6", size = 4785266, upload-time = "2026-07-01T11:53:49.079Z" }, + { url = "https://files.pythonhosted.org/packages/c8/98/766667a4be768150a202836acd9fad19c06824ca86c4286d3cf6b274964e/pillow-12.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd", size = 6263814, upload-time = "2026-07-01T11:53:51.32Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2d/ede717bc1144f63886c21fd349bb95860b0d1a21149ff16f2bb362b612b6/pillow-12.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd", size = 6934408, upload-time = "2026-07-01T11:53:53.487Z" }, + { url = "https://files.pythonhosted.org/packages/a3/48/9c58b685e69d49c31af6c8eb9012055fab7e665785165c84796e2c73ce72/pillow-12.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4f883547d4b7f0495ebe7056b0cc2aea76094e7a4abc8e933540f3271df27d9c", size = 6337160, upload-time = "2026-07-01T11:53:55.457Z" }, + { url = "https://files.pythonhosted.org/packages/ff/fa/dc2a5c0ba6df93f67c31d34b808b7ce440b40cdbf96f0b81cde1d1e6fa93/pillow-12.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:236ff70b9312fb68943c703aa842ca6a758abfa45ac187a5e7c1452e96ef72b5", size = 7045172, upload-time = "2026-07-01T11:53:57.736Z" }, + { url = "https://files.pythonhosted.org/packages/86/a5/444817a4d4c4c2417df00513086ca196f388d8f9ef40c2e4ccd1ad1af54b/pillow-12.3.0-cp311-cp311-win32.whl", hash = "sha256:10e41f0fbf1eec8cfd234b8fe17a4caac7c9d0db4c204d3c173a8f9f6ef3232b", size = 6472232, upload-time = "2026-07-01T11:53:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/63/c6/4bad1b18d132a50b27e1365e1ab163616f7a5bb56d330f66f9d1d9d4f9d4/pillow-12.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8e95e1385e4998ae9694eeaa4730ba5457ff61185b3a55e2e7bea0880aef452a", size = 7233653, upload-time = "2026-07-01T11:54:02.066Z" }, + { url = "https://files.pythonhosted.org/packages/fd/16/00f91ab7760dc842f5aad55217e80fc4a7067a0604535249bc8a2d6d9870/pillow-12.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:ebaea975e03d3141d9d3a507df75c9b3ec90fa9d2ffd07567b3a978d9d790b26", size = 2568195, upload-time = "2026-07-01T11:54:04.622Z" }, + { url = "https://files.pythonhosted.org/packages/37/bf/fb3ebff8ddcb76aac5a01389251bbbb9519922a9b520d8247c1ca864a25d/pillow-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965", size = 5345969, upload-time = "2026-07-01T11:54:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/d8/66/9a386a92561f402389a4fc70c18838bf6d35eb5eb5c6850b4b2dc64f5048/pillow-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7", size = 4780323, upload-time = "2026-07-01T11:54:09.351Z" }, + { url = "https://files.pythonhosted.org/packages/25/27/ac8f99618ffd3dde21db0f4d4b1d2ab00c0880595bfd17df103f7f39fd0c/pillow-12.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9", size = 6266838, upload-time = "2026-07-01T11:54:11.71Z" }, + { url = "https://files.pythonhosted.org/packages/84/21/a35af28dcc61f37ed850a2d64c65c701321dfbf25085e469d5559360cbbf/pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91", size = 6940830, upload-time = "2026-07-01T11:54:13.732Z" }, + { url = "https://files.pythonhosted.org/packages/eb/51/8b08617af3ad95e33ce6d7dd2c99ed6c8298f7fb131636303956be022e25/pillow-12.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c", size = 6344383, upload-time = "2026-07-01T11:54:15.756Z" }, + { url = "https://files.pythonhosted.org/packages/1d/72/cf78ac9780bb93c28328f408973845a309d4d145041665f734572ced1b52/pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df", size = 7052934, upload-time = "2026-07-01T11:54:17.721Z" }, + { url = "https://files.pythonhosted.org/packages/20/20/25e0f4dc178a6bc0696793720055519a0de89e7661dae886992decbd2f81/pillow-12.3.0-cp312-cp312-win32.whl", hash = "sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f", size = 6472684, upload-time = "2026-07-01T11:54:19.839Z" }, + { url = "https://files.pythonhosted.org/packages/45/89/da2f7971a317f83d807fdd4065c0af40208e59e692cc43d315a71a0e96d1/pillow-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09", size = 7227137, upload-time = "2026-07-01T11:54:22.025Z" }, + { url = "https://files.pythonhosted.org/packages/de/47/4845a0a6c0dbf1db8456bd9fc791f13c5ced7ced20606d08a0aacfd25b49/pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510", size = 2568267, upload-time = "2026-07-01T11:54:24.051Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ac/31fb64e1e7efb5a4b50cd3d92049ba89ac6e4d8d3bb6a74e15048ca3353e/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89", size = 4161684, upload-time = "2026-07-01T11:54:25.934Z" }, + { url = "https://files.pythonhosted.org/packages/87/b4/9805e23d2b4d77842b468513841fda254ee42f0289d25088340e4ff46e2d/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace", size = 4255487, upload-time = "2026-07-01T11:54:27.935Z" }, + { url = "https://files.pythonhosted.org/packages/df/39/ecf519435a200c693fe053a6ee4d835b41cf963a4dfc2551c4e637cb2a71/pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec", size = 3696433, upload-time = "2026-07-01T11:54:29.813Z" }, + { url = "https://files.pythonhosted.org/packages/42/92/2fc3ffad878ae8dd5469ec1bc8eb83b71f48e13efdf68f02709003982a32/pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66", size = 5345889, upload-time = "2026-07-01T11:54:31.97Z" }, + { url = "https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35", size = 4780109, upload-time = "2026-07-01T11:54:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/e18aff37cb0b4aac47ac90f016d347a49aca667ef97f190b06ac2aabc928/pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65", size = 6263736, upload-time = "2026-07-01T11:54:36.131Z" }, + { url = "https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3", size = 6937129, upload-time = "2026-07-01T11:54:38.216Z" }, + { url = "https://files.pythonhosted.org/packages/70/4d/105627a13300c5e0df1d174230b32fd1273062c96f7745fd552b945d1e1d/pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a", size = 6339562, upload-time = "2026-07-01T11:54:40.354Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1d/f13de01a553988ab895ba1c722e06cf3144d4f57656fd5b81b6d881f1179/pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e", size = 7049439, upload-time = "2026-07-01T11:54:42.489Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f9/066794cca041b969964f779ee5fa66a9498bbf34248ac39c5d7954e4198f/pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f", size = 6473287, upload-time = "2026-07-01T11:54:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/7a58e61d62be561da3a356fe2384d4059a6345fc130e23ef1c36a5b81d24/pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8", size = 7239691, upload-time = "2026-07-01T11:54:47.141Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b0/c4ed4f0ef8f8fa5ee8351537db6650bb8189f7e118842978dd6589065692/pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b", size = 2568185, upload-time = "2026-07-01T11:54:49.137Z" }, + { url = "https://files.pythonhosted.org/packages/dc/01/001f65b68192f0228cc1dbbc8d2530ab5d58b61037ba0587f946fea607cd/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330", size = 4161736, upload-time = "2026-07-01T11:54:51.156Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d2/0219746d0fd16fc8a84498e79452375be3797d3ce4044596ce565164b84f/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217", size = 4255435, upload-time = "2026-07-01T11:54:53.414Z" }, + { url = "https://files.pythonhosted.org/packages/c8/02/8d0bc62ef0302318c46ff2a512822d2610e81c7aa46c9b3abe6cbaca5ad0/pillow-12.3.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930", size = 3696262, upload-time = "2026-07-01T11:54:55.739Z" }, + { url = "https://files.pythonhosted.org/packages/85/e2/73c77d218410b14f5f2d565e8a998d5317b7b9c75368d29985139f7a46f0/pillow-12.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8", size = 5350344, upload-time = "2026-07-01T11:54:57.657Z" }, + { url = "https://files.pythonhosted.org/packages/c7/da/32c752228ae345f489e3a42499d817b6c3996da7e8a3bc7a04fc806b243b/pillow-12.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0", size = 4780131, upload-time = "2026-07-01T11:54:59.713Z" }, + { url = "https://files.pythonhosted.org/packages/b1/9d/8b2c807dbef61a5197c047afe99823787eb66f63daf9fb2432f91d6f0462/pillow-12.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321", size = 6263757, upload-time = "2026-07-01T11:55:01.778Z" }, + { url = "https://files.pythonhosted.org/packages/5c/44/c85361f65dbe00eea8576ee467c768d25129989efb76e94f205e9ca9bb46/pillow-12.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b", size = 6936962, upload-time = "2026-07-01T11:55:03.93Z" }, + { url = "https://files.pythonhosted.org/packages/18/7e/e483414b35800b86b6f08dbbc7803fb5cd52c4d6f897f47d53ea2c7e6f65/pillow-12.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198", size = 6339171, upload-time = "2026-07-01T11:55:05.989Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f4/68c491844841ede6bed70189546b3ee9731cf9f2cbad396faff5e1ccba45/pillow-12.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130", size = 7048116, upload-time = "2026-07-01T11:55:08.131Z" }, + { url = "https://files.pythonhosted.org/packages/a3/34/77f3f793fed8efc7d243f21b33c5a3f0d1c97ee70346d3db855587e155ff/pillow-12.3.0-cp314-cp314-win32.whl", hash = "sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a", size = 6467209, upload-time = "2026-07-01T11:55:10.408Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e0/492879f69d94f91f60fc8cd05ba03650e9520afebb2fb7aa12777d7c7f38/pillow-12.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d", size = 7237707, upload-time = "2026-07-01T11:55:12.745Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ac/6b11f2875f1c2ac040d84e1bbf9cf22a88038f901ca1037898b280b38365/pillow-12.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838", size = 2565995, upload-time = "2026-07-01T11:55:14.736Z" }, + { url = "https://files.pythonhosted.org/packages/52/69/c2208e56af9bfc1913afb24020297a691eb1d4ef688474c8a04913f65e04/pillow-12.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e", size = 5352503, upload-time = "2026-07-01T11:55:17.076Z" }, + { url = "https://files.pythonhosted.org/packages/07/70/e5686d753e898a45d778ff1718dba8516ead6ab6b95d85fc8c4b70650cf2/pillow-12.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17", size = 4782956, upload-time = "2026-07-01T11:55:19.448Z" }, + { url = "https://files.pythonhosted.org/packages/d5/37/25c6692f06927ee973ff18c8d9ee98ad0b4d84ee67a09610c2dd1447958e/pillow-12.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385", size = 6322855, upload-time = "2026-07-01T11:55:21.613Z" }, + { url = "https://files.pythonhosted.org/packages/cc/91/420637fcb8f1bc11029e403b4538e6694744428d8246118e45719f944556/pillow-12.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c", size = 6989642, upload-time = "2026-07-01T11:55:24.006Z" }, + { url = "https://files.pythonhosted.org/packages/10/08/b94d7811281ccf0d143a1cf768d1c49e1e54af63e7b708ab2ee3eb87face/pillow-12.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d", size = 6391281, upload-time = "2026-07-01T11:55:26.252Z" }, + { url = "https://files.pythonhosted.org/packages/d2/87/24233f785f55474dc02ce3e739c5528a77e3a862e9333d1dd7a25cc31f70/pillow-12.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931", size = 7096716, upload-time = "2026-07-01T11:55:28.318Z" }, + { url = "https://files.pythonhosted.org/packages/23/26/fcb2f6e37175b04f53570b59937867e2b80ee1685e744023153028fc14f9/pillow-12.3.0-cp314-cp314t-win32.whl", hash = "sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7", size = 6474125, upload-time = "2026-07-01T11:55:30.956Z" }, + { url = "https://files.pythonhosted.org/packages/90/de/3634abee5f1c9e13c56787b7d5517b0ba8d6de51700b95578cf338349c9f/pillow-12.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c", size = 7242939, upload-time = "2026-07-01T11:55:34.044Z" }, + { url = "https://files.pythonhosted.org/packages/ce/2a/fd13f8eb24de5714a6eb444a3d67e2842c6c576e159a43793adf23051351/pillow-12.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45", size = 2567506, upload-time = "2026-07-01T11:55:35.988Z" }, + { url = "https://files.pythonhosted.org/packages/5d/dc/8fdce34ec725a33c81c6ba122b904d6b9024e50ea9ac7bede62fab54506c/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139", size = 4162063, upload-time = "2026-07-01T11:55:37.941Z" }, + { url = "https://files.pythonhosted.org/packages/76/66/2044b9a63d3b84ff048228dfcb7cd9bf0df983e8470971bf7d4c57b693de/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402", size = 4255549, upload-time = "2026-07-01T11:55:40.022Z" }, + { url = "https://files.pythonhosted.org/packages/52/7e/1f67e6f4ece6b582ee4b539decbcc9f848dc245a93ed8cd7338bafef72f1/pillow-12.3.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c", size = 3696331, upload-time = "2026-07-01T11:55:41.98Z" }, + { url = "https://files.pythonhosted.org/packages/12/40/d306fc2c8e4d45d7f175c77edca7063be7b86fe7fe6e68f4353bf71d808c/pillow-12.3.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f", size = 5350370, upload-time = "2026-07-01T11:55:44.028Z" }, + { url = "https://files.pythonhosted.org/packages/dd/44/668fb1437e8ce420f62d6106eb66e44a5971602a4d794615bdf79315d82d/pillow-12.3.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701", size = 4780147, upload-time = "2026-07-01T11:55:46.073Z" }, + { url = "https://files.pythonhosted.org/packages/0c/08/93fa2e70e30a2d81547e481b6ee2bb9522117221fb1e0ce4b5df70967677/pillow-12.3.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace", size = 6273659, upload-time = "2026-07-01T11:55:48.264Z" }, + { url = "https://files.pythonhosted.org/packages/f8/6d/043e96ff814fc31a33077e4cba86082167db520c93632afdf2042febbb0c/pillow-12.3.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4", size = 6947439, upload-time = "2026-07-01T11:55:50.503Z" }, + { url = "https://files.pythonhosted.org/packages/af/92/ba71d2ee2ac0edf3fa33bd9d5ee9ee080da70b1766f3ca3934f9938ddac9/pillow-12.3.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39", size = 6353577, upload-time = "2026-07-01T11:55:52.697Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ce/e63064e2122923ff687c8ad792d0d736a7b3920a56a46982e81a7fdd25d6/pillow-12.3.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71", size = 7060394, upload-time = "2026-07-01T11:55:55.149Z" }, + { url = "https://files.pythonhosted.org/packages/54/76/a09cc3ccc8d773a7283d34c38bec1708f9e3cc932093cbc4c5e71ac4060b/pillow-12.3.0-cp315-cp315-win32.whl", hash = "sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827", size = 6467375, upload-time = "2026-07-01T11:55:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/1846c49ba3b1d5550392a4bbd06d6fb4578e1cd91a803198b5c90f5f7d53/pillow-12.3.0-cp315-cp315-win_amd64.whl", hash = "sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5", size = 7237048, upload-time = "2026-07-01T11:55:59.975Z" }, + { url = "https://files.pythonhosted.org/packages/fb/bb/89f35dcc79610423f9f195504d7def7f0d1416a711541b42867e25fe3412/pillow-12.3.0-cp315-cp315-win_arm64.whl", hash = "sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658", size = 2566006, upload-time = "2026-07-01T11:56:02.143Z" }, + { url = "https://files.pythonhosted.org/packages/30/88/707027ba09942dfa2c28759b5c222d769290a41c6d20ea60ec250801941f/pillow-12.3.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf", size = 5352509, upload-time = "2026-07-01T11:56:04.2Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6d/00352fa25332c2569cd387851f568cc5a4b75a9adbfb37ac4fbce4c02eec/pillow-12.3.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64", size = 4783167, upload-time = "2026-07-01T11:56:06.631Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/9e049dfa21af7c22427275720e2490267ba8138120add5c4c574deb69782/pillow-12.3.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e", size = 6329237, upload-time = "2026-07-01T11:56:08.868Z" }, + { url = "https://files.pythonhosted.org/packages/36/16/cf6eeaae8d0fce8dd390a33437cf68c5d5bd73834a2bc6e2f14efda0ab45/pillow-12.3.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777", size = 6997047, upload-time = "2026-07-01T11:56:11.379Z" }, + { url = "https://files.pythonhosted.org/packages/1e/69/dbf769bdd55f48bf5733cac28edc6364ffaa072ec9ba336266e4fe66be55/pillow-12.3.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1", size = 6400440, upload-time = "2026-07-01T11:56:13.908Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e1/ffc9cfc2eea0d178da8018e18e959301ad9d6bc9f3edb7181e748a474b97/pillow-12.3.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9", size = 7105895, upload-time = "2026-07-01T11:56:16.575Z" }, + { url = "https://files.pythonhosted.org/packages/18/f0/a5595c1e8c3ae44b9828cb2f0fa8155e5095ef04d6327b8f61cf44a3df85/pillow-12.3.0-cp315-cp315t-win32.whl", hash = "sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8", size = 6474384, upload-time = "2026-07-01T11:56:18.855Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/62bcd9f844984c5938d3b05264a61d797a29d3e0812341a8204af70bbdee/pillow-12.3.0-cp315-cp315t-win_amd64.whl", hash = "sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418", size = 7243537, upload-time = "2026-07-01T11:56:21.214Z" }, + { url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" }, + { url = "https://files.pythonhosted.org/packages/75/18/2e8b40223153ccbc60df07f9e8928dc0c76202aa4e55ae9f53962b6510d6/pillow-12.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468", size = 5302510, upload-time = "2026-07-01T11:56:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/46/3e/51fabf59d5ab801ceab709453d3ab6b180083496579549de4c45ced6528a/pillow-12.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94", size = 4736058, upload-time = "2026-07-01T11:56:28.041Z" }, + { url = "https://files.pythonhosted.org/packages/bf/20/22fe9384b7949e25fb1293bcfc84fb82590ff4ea6b37c95b24d26d793d86/pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e", size = 5237776, upload-time = "2026-07-01T11:56:30.263Z" }, + { url = "https://files.pythonhosted.org/packages/08/14/f6ba68107680ffa74b39985f3f30884e41318fbc4250caa423c79b4788bb/pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7e480451b9fa137494bccd3a7d69adbe8ac65a87d97be61e11f1b1050a5bac3", size = 5860358, upload-time = "2026-07-01T11:56:32.68Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0169bc772ec491108b62f644f8ecf1fe5d8ae5ebafde2ee2142210166903/pillow-12.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:04f01d28a6aaff387bf842a13be313df23ba0597a44f1a976c9feb3c6ff4711a", size = 7231786, upload-time = "2026-07-01T11:56:35.046Z" }, ] [[package]] @@ -4236,18 +3482,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, ] -[[package]] -name = "proto-plus" -version = "1.28.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/56/e647b0c675392d2da368da7b6f158f7368b18542fd6f7d7400a2f39de000/proto_plus-1.28.0.tar.gz", hash = "sha256:38e5696342835b08fc116f30a25665b29531cda9d5d5643e9b81fc312385abd9", size = 57221, upload-time = "2026-05-07T08:04:50.811Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/20/b122d4626976acb81132036d2ad1bb35a1a8775fceb837ec30964622516a/proto_plus-1.28.0-py3-none-any.whl", hash = "sha256:a630604310899e73c59ec302e5765c058d412b2f090b9c79c8822589f14955b8", size = 50410, upload-time = "2026-05-07T08:03:31.962Z" }, -] - [[package]] name = "protobuf" version = "6.33.6" @@ -4286,70 +3520,13 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c9/33/a7cbfccc39056a5cf8126b7aab4c8bafbedd4f0ca68ae40ecb627a2d2cd3/py_partiql_parser-0.6.3-py2.py3-none-any.whl", hash = "sha256:deb0769c3346179d2f590dcbde556f708cdb929059fb654bad75f4cf6e07f582", size = 23752, upload-time = "2025-10-18T13:56:12.256Z" }, ] -[[package]] -name = "pyarrow" -version = "24.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261, upload-time = "2026-04-21T10:51:25.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/bf/a34fee1d624152124fa8355c42f34195ad5fe5233ce5bb87946432047d52/pyarrow-24.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb", size = 35076681, upload-time = "2026-04-21T08:51:46.845Z" }, - { url = "https://files.pythonhosted.org/packages/1d/41/64180033d7027afce12dc96d0fe1f504c6fa112190582b458acea2399530/pyarrow-24.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147", size = 36684260, upload-time = "2026-04-21T08:51:53.642Z" }, - { url = "https://files.pythonhosted.org/packages/57/02/9b9320e673dd8a99411fac78690f3df92f6dd6f59754c750110bca66d64e/pyarrow-24.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c", size = 45698566, upload-time = "2026-04-21T10:46:02.133Z" }, - { url = "https://files.pythonhosted.org/packages/67/33/f75e91b9a64c3f33c787e263c93b871ad91b8a4a68c1d5cebddd9840e835/pyarrow-24.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041", size = 48835562, upload-time = "2026-04-21T10:46:10.278Z" }, - { url = "https://files.pythonhosted.org/packages/a5/63/097510448e47e4091faa41c43ba92f97cecaab8f4535b56a3d149578f634/pyarrow-24.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491", size = 49394997, upload-time = "2026-04-21T10:46:18.08Z" }, - { url = "https://files.pythonhosted.org/packages/60/6b/c047d6222ab279024a062742d1807e2fbaf27bba88a98637299ff47b9236/pyarrow-24.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1", size = 51911424, upload-time = "2026-04-21T10:46:25.347Z" }, - { url = "https://files.pythonhosted.org/packages/3a/ba/464cc70761c2a525d97ebd84e21c31ebd47f3ef4bdcee117009f51c46f24/pyarrow-24.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591", size = 27251730, upload-time = "2026-04-21T10:46:30.913Z" }, - { url = "https://files.pythonhosted.org/packages/62/c9/a47ab7ece0d86cbe6678418a0fbd1ac4bb493b9184a3891dfa0e7f287ae0/pyarrow-24.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74", size = 35068898, upload-time = "2026-04-21T10:46:36.599Z" }, - { url = "https://files.pythonhosted.org/packages/d1/bc/8db86617a9a58008acf8913d6fed68ea2a46acb6de928db28d724c891a68/pyarrow-24.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3", size = 36679915, upload-time = "2026-04-21T10:46:42.602Z" }, - { url = "https://files.pythonhosted.org/packages/eb/8e/fb178720400ef69db251eb4a9c3ccf4af269bc1feb5055529b8fc87170d1/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868", size = 45697931, upload-time = "2026-04-21T10:46:48.403Z" }, - { url = "https://files.pythonhosted.org/packages/f3/27/99c42abe8e21b44f4917f62631f3aa31404882a2c41d8a4cd5c110e13d52/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e", size = 48837449, upload-time = "2026-04-21T10:46:55.329Z" }, - { url = "https://files.pythonhosted.org/packages/36/b6/333749e2666e9032891125bf9c691146e92901bece62030ac1430e2e7c88/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57", size = 49395949, upload-time = "2026-04-21T10:47:01.869Z" }, - { url = "https://files.pythonhosted.org/packages/17/25/c5201706a2dd374e8ba6ee3fd7a8c89fb7ffc16eed5217a91fd2bd7f7626/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c", size = 51912986, upload-time = "2026-04-21T10:47:09.872Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d2/4d1bbba65320b21a49678d6fbdc6ff7c649251359fdcfc03568c4136231d/pyarrow-24.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981", size = 27255371, upload-time = "2026-04-21T10:47:15.943Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559, upload-time = "2026-04-21T10:47:22.17Z" }, - { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654, upload-time = "2026-04-21T10:47:28.315Z" }, - { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394, upload-time = "2026-04-21T10:47:34.821Z" }, - { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122, upload-time = "2026-04-21T10:47:42.056Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032, upload-time = "2026-04-21T10:47:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490, upload-time = "2026-04-21T10:47:55.981Z" }, - { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660, upload-time = "2026-04-21T10:48:01.718Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759, upload-time = "2026-04-21T10:48:07.258Z" }, - { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471, upload-time = "2026-04-21T10:48:13.347Z" }, - { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981, upload-time = "2026-04-21T10:48:20.201Z" }, - { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172, upload-time = "2026-04-21T10:48:27.541Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733, upload-time = "2026-04-21T10:48:34.7Z" }, - { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335, upload-time = "2026-04-21T10:48:42.099Z" }, - { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748, upload-time = "2026-04-21T10:49:42.532Z" }, - { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554, upload-time = "2026-04-21T10:48:48.526Z" }, - { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301, upload-time = "2026-04-21T10:48:55.181Z" }, - { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929, upload-time = "2026-04-21T10:49:03.676Z" }, - { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365, upload-time = "2026-04-21T10:49:11.714Z" }, - { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819, upload-time = "2026-04-21T10:49:21.474Z" }, - { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252, upload-time = "2026-04-21T10:49:31.164Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127, upload-time = "2026-04-21T10:49:37.334Z" }, - { url = "https://files.pythonhosted.org/packages/ad/80/d022a34ff05d2cbedd8ccf841fc1f532ecfa9eb5ed1711b56d0e0ea71fc9/pyarrow-24.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838", size = 35007997, upload-time = "2026-04-21T10:49:48.796Z" }, - { url = "https://files.pythonhosted.org/packages/1a/ff/f01485fda6f4e5d441afb8dd5e7681e4db18826c1e271852f5d3957d6a80/pyarrow-24.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b", size = 36678720, upload-time = "2026-04-21T10:49:55.858Z" }, - { url = "https://files.pythonhosted.org/packages/9e/c2/2d2d5fea814237923f71b36495211f20b43a1576f9a4d6da7e751a64ec6f/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795", size = 45741852, upload-time = "2026-04-21T10:50:04.624Z" }, - { url = "https://files.pythonhosted.org/packages/8e/3a/28ba9c1c1ebdbb5f1b94dfebb46f207e52e6a554b7fe4132540fde29a3a0/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26", size = 48889852, upload-time = "2026-04-21T10:50:12.293Z" }, - { url = "https://files.pythonhosted.org/packages/df/51/4a389acfd31dca009f8fb82d7f510bb4130f2b3a8e18cf00194d0687d8ac/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde", size = 49445207, upload-time = "2026-04-21T10:50:20.677Z" }, - { url = "https://files.pythonhosted.org/packages/19/4b/0bab2b23d2ae901b1b9a03c0efd4b2d070256f8ce3fc43f6e58c167b2081/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76", size = 51954117, upload-time = "2026-04-21T10:50:29.14Z" }, - { url = "https://files.pythonhosted.org/packages/29/88/f4e9145da0417b3d2c12035a8492b35ff4a3dbc653e614fcfb51d9dedb38/pyarrow-24.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e", size = 28001155, upload-time = "2026-04-21T10:51:22.337Z" }, - { url = "https://files.pythonhosted.org/packages/79/4f/46a49a63f43526da895b1a45bbb51d5baf8e4d77159f8528fc3e5490007f/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05", size = 35250387, upload-time = "2026-04-21T10:50:35.552Z" }, - { url = "https://files.pythonhosted.org/packages/a0/da/d5e0cd5ef00796922404806d5f00325cdadc3441ce2c13fe7115f2df9a64/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a", size = 36797102, upload-time = "2026-04-21T10:50:42.417Z" }, - { url = "https://files.pythonhosted.org/packages/34/c7/5904145b0a593a05236c882933d439b5720f0a145381179063722fbfc123/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072", size = 45745118, upload-time = "2026-04-21T10:50:49.324Z" }, - { url = "https://files.pythonhosted.org/packages/13/d3/cca42fe166d1c6e4d5b80e530b7949104d10e17508a90ae202dac205ce2a/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931", size = 48844765, upload-time = "2026-04-21T10:50:55.579Z" }, - { url = "https://files.pythonhosted.org/packages/b0/49/942c3b79878ba928324d1e17c274ed84581db8c0a749b24bcf4cbdf15bd3/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699", size = 49471890, upload-time = "2026-04-21T10:51:02.439Z" }, - { url = "https://files.pythonhosted.org/packages/76/97/ff71431000a75d84135a1ace5ca4ba11726a231a8007bbb320a4c54075d5/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136", size = 51932250, upload-time = "2026-04-21T10:51:10.576Z" }, - { url = "https://files.pythonhosted.org/packages/51/be/6f79d55816d5c22557cf27533543d5d70dfe692adfbee4b99f2760674f38/pyarrow-24.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19", size = 28131282, upload-time = "2026-04-21T10:51:16.815Z" }, -] - [[package]] name = "pyasn1" -version = "0.6.3" +version = "0.6.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a4/9a/23310166d960def5897e91fe20e5b724601b02a22e84ba1f94232c0b7f67/pyasn1-0.6.4.tar.gz", hash = "sha256:9c447d8431c947fe4c8febc4ed9e760bc29011a5b01e5c74b67025bd9fb8ce81", size = 151262, upload-time = "2026-07-09T01:12:33.988Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3b/6163796d69c3977d1e4287bea4a6979161cbbdd170ebb430511e8e1999ce/pyasn1-0.6.4-py3-none-any.whl", hash = "sha256:deda9277cfd454080ec40b207fb6df82206a3a2688735233cdcd8d3d565f088b", size = 84410, upload-time = "2026-07-09T01:12:32.92Z" }, ] [[package]] @@ -4506,16 +3683,16 @@ wheels = [ [[package]] name = "pydantic-settings" -version = "2.14.1" +version = "2.14.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/60/1d1e59c9c90d54591469ada7d268251f71c24bdb765f1a8a832cee8c6653/pydantic_settings-2.14.1.tar.gz", hash = "sha256:e874d3bec7e787b0c9958277956ed9b4dd5de6a80e162188fdaff7c5e26fd5fa", size = 235551, upload-time = "2026-05-08T13:40:06.542Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/b5/8f48e906c3e0205276e8bd8cb7512217a87b2685304d64be27cad5b3019f/pydantic_settings-2.14.2.tar.gz", hash = "sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f", size = 237700, upload-time = "2026-06-19T13:44:56.324Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/8d/f1af3832f5e6eb13ba94ee809e72b8ecb5eef226d27ee0bef7d963d943c7/pydantic_settings-2.14.1-py3-none-any.whl", hash = "sha256:6e3c7edfd8277687cdc598f56e5cff0e9bfff0910a3749deaa8d4401c3a2b9de", size = 60964, upload-time = "2026-05-08T13:40:04.958Z" }, + { url = "https://files.pythonhosted.org/packages/77/c1/6e422f34e569cf8e18df68d1939c81c099d2b61e4f7d9621c8a77560799c/pydantic_settings-2.14.2-py3-none-any.whl", hash = "sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440", size = 61715, upload-time = "2026-06-19T13:44:55.02Z" }, ] [[package]] @@ -4577,19 +3754,6 @@ crypto = [ { name = "cryptography" }, ] -[[package]] -name = "pyopenssl" -version = "26.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/b7/da07bae88f5a9506b4def6f2f4903cf4c3b8831e560dba8fa18ca08f758f/pyopenssl-26.3.0.tar.gz", hash = "sha256:589de7fae1c9ea670d18422ed00fc04da787bbde8e1454aea872aa57b49ad341", size = 182024, upload-time = "2026-06-12T20:28:07.458Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/18/1dd71c9b43192ab83f1d531ad6002dc81108ac36c475f79fb7a295abe2f4/pyopenssl-26.3.0-py3-none-any.whl", hash = "sha256:46367f8f66b92271e6d218da9c87607e1ef5a0bc5c8dea5bb3db82f395c385a3", size = 56008, upload-time = "2026-06-12T20:28:05.999Z" }, -] - [[package]] name = "pyparsing" version = "3.3.2" @@ -4614,7 +3778,7 @@ wheels = [ [[package]] name = "pytest" -version = "9.0.3" +version = "9.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -4625,9 +3789,9 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, ] [[package]] @@ -4683,15 +3847,15 @@ wheels = [ [[package]] name = "pytest-rerunfailures" -version = "16.3" +version = "16.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4d/f0/74f8e685be7ecd1572c1256132f18fce3a665d7e07649a3f23b7eb2d3bec/pytest_rerunfailures-16.3.tar.gz", hash = "sha256:37c9b1231c8083e9f4e724f50f7a21241822f9516c15c700ebbf218d6452355c", size = 34148, upload-time = "2026-05-22T06:51:22.292Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/60/a90ca1cc6cffcb97b4260ed0ad2b7934b999d7c48abe4ea0840344862a3b/pytest_rerunfailures-16.4.tar.gz", hash = "sha256:8222d17c37eb7b9e4d6fc96a3c724ff4e1a5c97a5cc7cbb2c19e9282cfd21a11", size = 36635, upload-time = "2026-07-01T06:30:56.813Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/98/58a71d68d3126d7f6a6ed1944c37ec207a4ff3dc66cad3bed7b59d38df61/pytest_rerunfailures-16.3-py3-none-any.whl", hash = "sha256:6bdfb8ffb46c46072e6c16bdedee38b6c13eac620d9415ed5b63152cbf283170", size = 15396, upload-time = "2026-05-22T06:51:20.547Z" }, + { url = "https://files.pythonhosted.org/packages/ce/93/3cdcc4033444e822e01b573414b03fd37fd5533070c750477b8f5fa5224b/pytest_rerunfailures-16.4-py3-none-any.whl", hash = "sha256:f69b5beb39622c90d1e44bd945d826eff6db545dcf0b68f52b7e4ad15eaf6d6c", size = 16955, upload-time = "2026-07-01T06:30:55.333Z" }, ] [[package]] @@ -4868,7 +4032,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "rpds-py", version = "2026.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "rpds-py", version = "2026.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } @@ -4878,123 +4042,123 @@ wheels = [ [[package]] name = "regex" -version = "2026.5.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/0e/49aee608ad09480e7fd276898c99ec6192985fa331abe4eb3a986094490b/regex-2026.5.9.tar.gz", hash = "sha256:a8234aa23ec39894bfe4a3f1b85616a7032481964a13ac6fc9f10de4f6fca270", size = 416074, upload-time = "2026-05-09T23:15:19.37Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ed/0ad2c8edf634918eb4484365d3819fa7bd7f58daf807fe7fb21812c316e5/regex-2026.5.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a9e1328e17c84c1a5d22ec9f785ecef4a967fab9a42b6a8dc3bcbebd0a0c9e44", size = 489438, upload-time = "2026-05-09T23:11:29.374Z" }, - { url = "https://files.pythonhosted.org/packages/89/a9/4ed972ad263963b860b7c3e86e0e1bcc791def47b43b8c8efe57e710f139/regex-2026.5.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfe1ce50cbfb569d74e1e4337da6468961f31dbea55fd85aa5de59c0947a805a", size = 291270, upload-time = "2026-05-09T23:11:33.254Z" }, - { url = "https://files.pythonhosted.org/packages/16/81/075930d9fa28c4ea1f53398dd015ee7c882f623539759113cda1257f4b82/regex-2026.5.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15ee42209947f4ca045412eae98416317238163618ace2a8e54f99586a466733", size = 289198, upload-time = "2026-05-09T23:11:35.769Z" }, - { url = "https://files.pythonhosted.org/packages/d4/c8/5cdfbf0b5dc6599e1b6131eff43262e5275d4ec3469ce10216061659aadb/regex-2026.5.9-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4bb445ff3f725f59df8f6014edb547ee928ec7023a774f6a39a3f953038cbb2", size = 784765, upload-time = "2026-05-09T23:11:37.689Z" }, - { url = "https://files.pythonhosted.org/packages/cd/ca/ae5fd6edc59b7f84b904b31d6ec39a860cbcecd10f64bd5a062ca83a4864/regex-2026.5.9-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:446ddd671e43ab535810c4b21cff7104945c701d4a14d1e6d1cd6f4e445a8bea", size = 852115, upload-time = "2026-05-09T23:11:39.973Z" }, - { url = "https://files.pythonhosted.org/packages/f6/ce/a91cf555afb51f3b74a182e24ba073b91ea7bb64592fc4b315c111bb19fd/regex-2026.5.9-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b92817338591505f282cf3864c145244b1edcf5381d237038df955001091538", size = 899503, upload-time = "2026-05-09T23:11:42.48Z" }, - { url = "https://files.pythonhosted.org/packages/55/7f/725a0a2b245a4cf0c4bab29d0e97c74285d94136a65d1b55a6459a583502/regex-2026.5.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6b8a143aca6c39b446ea8092cde25cc8fe9304d4f5fecfbc1a9dbb0282703c2", size = 794093, upload-time = "2026-05-09T23:11:44.681Z" }, - { url = "https://files.pythonhosted.org/packages/e3/2a/996efbd59ce6b5d4a09e3af6180ceb62af171f4a9a6fb557d2f0ae0d462b/regex-2026.5.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0f03aa6898aaaac4592479821df16e68e8d0e29e903e65d8f2dfb2f19028a989", size = 786234, upload-time = "2026-05-09T23:11:46.882Z" }, - { url = "https://files.pythonhosted.org/packages/4b/0a/8731e8b8806174c9cdd5903f80a14990331c1f42fc4209b540952e9e010d/regex-2026.5.9-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ed457d8e98ae812ed7732bef7bf78de78e834eae0372a74e23ca90ef21d910f9", size = 769895, upload-time = "2026-05-09T23:11:49.324Z" }, - { url = "https://files.pythonhosted.org/packages/9a/0b/932473194bd563f342a412ae2ffbbd6da608306a2bc4e99249a41c2b0b92/regex-2026.5.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71b61c5bfe1c806332defc42ad6c780b3c55f661986d7f40283a3a88274b4c00", size = 774991, upload-time = "2026-05-09T23:11:51.261Z" }, - { url = "https://files.pythonhosted.org/packages/98/80/9523d196010031df25f7177ee0a467efbee436324038e5d99def17a57515/regex-2026.5.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3b1e39888c5e0c7d92cea4fc777396c4a90363b05de75d02eb459a4752200808", size = 848790, upload-time = "2026-05-09T23:11:53.232Z" }, - { url = "https://files.pythonhosted.org/packages/3c/07/56987b35e89edf47e4a38cf2845aeee476bfa688a6bdbd3e820cda461dc1/regex-2026.5.9-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:6ba42b2e7e7f46cf68cc6a5ca36fa07959f9bbd9c6bdcc47b6ee76549a590248", size = 757679, upload-time = "2026-05-09T23:11:55.82Z" }, - { url = "https://files.pythonhosted.org/packages/04/2a/ff713fff0c566507c06a4ce2dc0ae8e7eeebc88811a95fc81cf1e7d534dd/regex-2026.5.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c010eb8caca74bdb40c07498d7ece26b4428fd3f04aa8a72c9ac6f79e8faaac6", size = 837116, upload-time = "2026-05-09T23:11:57.934Z" }, - { url = "https://files.pythonhosted.org/packages/77/90/df6d982b03e3614785c6937ba51b57f6733d97d2ee1c9bc7531dbfab3a54/regex-2026.5.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a6a563446a41adc451393dc6b8e6ad87979efaee3c8738690a8d1b08ebead1b4", size = 782081, upload-time = "2026-05-09T23:11:59.607Z" }, - { url = "https://files.pythonhosted.org/packages/c7/8a/4e88a5f7c3e98489aac4dd23142723d907b2a595b4a6abcbacabefeded09/regex-2026.5.9-cp310-cp310-win32.whl", hash = "sha256:954cc214c04663ee6d266fc61739cad83054683048de65c5bd1d640ad28098ac", size = 266247, upload-time = "2026-05-09T23:12:01.116Z" }, - { url = "https://files.pythonhosted.org/packages/6a/40/4b224cb0582b2dca1786726e6cdabe26abbf757d7f6718332f186da155d2/regex-2026.5.9-cp310-cp310-win_amd64.whl", hash = "sha256:b310768746dd314ea6e2ff4cc89ef215426813396ff4e94ee8e6f7096c8b6e03", size = 278416, upload-time = "2026-05-09T23:12:03.2Z" }, - { url = "https://files.pythonhosted.org/packages/12/4d/014fbe803204cab0947ee428f09f658a29632053dde1d3c6176bb4f0fd4c/regex-2026.5.9-cp310-cp310-win_arm64.whl", hash = "sha256:19c16ceb4a267a8789e25733e583983eeab9f0f8664e66b0bd1c5d21f14c2d4b", size = 270413, upload-time = "2026-05-09T23:12:04.649Z" }, - { url = "https://files.pythonhosted.org/packages/c2/dc/c1f2df4027e82fc54b5a473e4b250f5139faca49a0fbe29a48668d228f34/regex-2026.5.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ccf5249114cc3e772ecdd88a98a86eca0fd74c61ce32a94743758c083fc05d48", size = 489445, upload-time = "2026-05-09T23:12:06.111Z" }, - { url = "https://files.pythonhosted.org/packages/03/d2/59f01110660081cce9c0bc30ebd0b5ee250dacf658e3248ed92f01e0e8ee/regex-2026.5.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46f1326ca6e65b0879d23ca302c0f2415aad42ff0309b9c818e7949fe19a41d8", size = 291271, upload-time = "2026-05-09T23:12:07.731Z" }, - { url = "https://files.pythonhosted.org/packages/58/b6/14b2c84ff90ddb370c81d27503f4a0fcf071496416f4855f6cc8c5d81c35/regex-2026.5.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef31cbfe458e21c6122ba8150ff060e0c7789ed0d26eb423f25472584920b555", size = 289212, upload-time = "2026-05-09T23:12:09.266Z" }, - { url = "https://files.pythonhosted.org/packages/03/d0/4db86529117320de0c84afd90e70bb47434625875e34fcef9d8c127c5b16/regex-2026.5.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:992604d02e6d9c6d786c24a706a71ecffe1020fc1ef264044474cd81fa2c3919", size = 792310, upload-time = "2026-05-09T23:12:11.416Z" }, - { url = "https://files.pythonhosted.org/packages/07/78/fe4800cd322f862ecffd2d553409b20d80650e5ed71b9d178f853d020b82/regex-2026.5.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9411dd64ca95477225734a93dfc8583b51916b8d5942f99d6cac21e09965451", size = 861721, upload-time = "2026-05-09T23:12:13.681Z" }, - { url = "https://files.pythonhosted.org/packages/b5/d0/b3618a895dd8feb897c61bb2954edd265e1767d82a01d53065d5871127a3/regex-2026.5.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4a3ff360dfb836fecdb93a4598f9d6e2ac81e3e397125145c6221bf58cf4c", size = 906460, upload-time = "2026-05-09T23:12:15.443Z" }, - { url = "https://files.pythonhosted.org/packages/33/6f/1481597e859ef19508b345eec4afd1416ed6e6b459c75a64026ef193aecf/regex-2026.5.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a661a7d270a61f7cf460caee8b9fa2d5ef9e5c681234bcb9e0fe14f488e7dfc", size = 799843, upload-time = "2026-05-09T23:12:16.892Z" }, - { url = "https://files.pythonhosted.org/packages/73/59/955734c803f59108deccba3597ae440c76b62a652733c0006e6243758420/regex-2026.5.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f079e50a0d3cc3cd5091fa9ff45869a2e6b2cd35895731edafb0327901a8d86d", size = 773610, upload-time = "2026-05-09T23:12:19.127Z" }, - { url = "https://files.pythonhosted.org/packages/68/8f/70c04a236d651c81881dac42ef8538bddda6121434509d0a22d9e601503b/regex-2026.5.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4ebe8f0b5ec5a5024dc4a4c59f444c4e9afc5f2abdbb8962065b75d27fb971f9", size = 781645, upload-time = "2026-05-09T23:12:20.806Z" }, - { url = "https://files.pythonhosted.org/packages/1d/96/05c7434d88185e5d27fe54aeb74df86bd77cd79f52f0b4eae54faa8fea70/regex-2026.5.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:97cf3bc1b7d7d2306772ec07366c80d9df00ff79e79cea32898883a646d2fae2", size = 854473, upload-time = "2026-05-09T23:12:22.465Z" }, - { url = "https://files.pythonhosted.org/packages/4e/c1/6e3d8202d981f3117004bf341ee74893ba4ba8a9fbaf4b94615846550a08/regex-2026.5.9-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0f9eede6a5cbdc02d4978090186390936e1776a7d1359b21e41014c609880bcf", size = 763311, upload-time = "2026-05-09T23:12:24.351Z" }, - { url = "https://files.pythonhosted.org/packages/93/c7/e7737f1526b3fb32bd4c337fd6c71c3ebb5c8296fc34d11197e0955d2e35/regex-2026.5.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:01f0f5f55f4b64dacec85dc116d3c05fd23ad3ff037bbc73a2085775953c2611", size = 844593, upload-time = "2026-05-09T23:12:26.341Z" }, - { url = "https://files.pythonhosted.org/packages/a5/27/0daffb1a535bb39f422c3d200f4ab023c71110ad66a32b366bee708baba0/regex-2026.5.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1268eddd8486dc561d08eee1156e40aa3a8fe10f4bdec8fa653b455fcbffd12c", size = 789167, upload-time = "2026-05-09T23:12:27.975Z" }, - { url = "https://files.pythonhosted.org/packages/ce/fc/294fe4fac4f2ed67207b17471815870c1c45b3a489e08e0ac96daea16ef6/regex-2026.5.9-cp311-cp311-win32.whl", hash = "sha256:8676474c07469d6f33dd1085ca2cd45f65785f32518f2b20e36d9953ca07f994", size = 266249, upload-time = "2026-05-09T23:12:30.141Z" }, - { url = "https://files.pythonhosted.org/packages/d0/b0/8dce459f6245bcf8f6e9f23ac9569f1a0f15c131cc0745e82b43226204cf/regex-2026.5.9-cp311-cp311-win_amd64.whl", hash = "sha256:246de9d60aa3f8538b519834dd95cbf276ea263d6a7bd5a3666dc3fa0230505b", size = 278423, upload-time = "2026-05-09T23:12:31.676Z" }, - { url = "https://files.pythonhosted.org/packages/db/8d/f9aeff6ad63a3ef720386f2907e6d34a35a510a6e498ebad28b0fb3f6ab6/regex-2026.5.9-cp311-cp311-win_arm64.whl", hash = "sha256:d726ca3f0d76969bf1e8e477d160d3d666bbf999f6860bd314889e5345782046", size = 270420, upload-time = "2026-05-09T23:12:33.194Z" }, - { url = "https://files.pythonhosted.org/packages/50/9b/6550044bc44e17c84d312c031c2ec42fbdb6a4ec4e29093be3a172d08772/regex-2026.5.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57eeeb05db7979413dec5438f2db21d7ecbba787cde7a711df1a6f6df672aa06", size = 490451, upload-time = "2026-05-09T23:12:34.72Z" }, - { url = "https://files.pythonhosted.org/packages/1e/95/fc7ba4303b5a0f92446a12ee6778ef2c6c799233f5060042a31bf390cfe9/regex-2026.5.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:398c521292f4c7fb807001dcd54694d3a1fcafc179a36ad9cc56f98df85930b6", size = 292112, upload-time = "2026-05-09T23:12:36.285Z" }, - { url = "https://files.pythonhosted.org/packages/54/4b/ee27938d1b2c443e89a9a10e00d2d19aa5ee300cd3d61140644e93bb083e/regex-2026.5.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7a7c26137296beba7784de6eba69c6a93a63ccebc385e4962fe67e267a91225", size = 289599, upload-time = "2026-05-09T23:12:38.089Z" }, - { url = "https://files.pythonhosted.org/packages/d8/dd/ba103dc19614e25f3880800ca67ce093d6e21b325d72b8383c7bf906e9fa/regex-2026.5.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6441cc660d76107934a09c22167200839a0e89604a6297f78a974e66e931d2c0", size = 796732, upload-time = "2026-05-09T23:12:40.062Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e7/f035b4fd858b050b0080bf302968dc0f59ba34e391872d54936758e6844e/regex-2026.5.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:91328f1c23d47595ca3ef0a7557fa129c5a23404b775c770697d2f35b33e0107", size = 865440, upload-time = "2026-05-09T23:12:42.059Z" }, - { url = "https://files.pythonhosted.org/packages/0a/51/8cd301ecc899aea28124357f729f4272f44de7806fc7ca02490bfbe253e8/regex-2026.5.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:93a7860539414dddaefba2b40f8771765ae17949d4c7182b876ce429e11a8309", size = 912329, upload-time = "2026-05-09T23:12:44.373Z" }, - { url = "https://files.pythonhosted.org/packages/cc/1e/3fbe2fa1e8cebd62f3bb7d3321cff1640aca2e240b51d9bd624aad949260/regex-2026.5.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd2810d22146b6d838acc5ec15602cb6b47920aa4e33015df3868eedfd20bab8", size = 801239, upload-time = "2026-05-09T23:12:46.268Z" }, - { url = "https://files.pythonhosted.org/packages/17/2f/6f6008682bf2cf98040a0d3153a8e557b6ab728d7713d045cee4ce544ab8/regex-2026.5.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:daff2bdbaf1d23e52fdff7c0b7bc2048b68f978df6a4d107ac981f94caef2e66", size = 777054, upload-time = "2026-05-09T23:12:48.051Z" }, - { url = "https://files.pythonhosted.org/packages/19/2b/eee0d20a6842ba04df4b8847a920b57ef56853f14ef85405473e586b605a/regex-2026.5.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4eeb011098fcb77af513dcef521a3dbecbf8849b1e38940759d293b7a93f5026", size = 785098, upload-time = "2026-05-09T23:12:49.851Z" }, - { url = "https://files.pythonhosted.org/packages/4a/98/6fc1e6410feefb92159edaed5041992bfe390e8d26c721865434acbca558/regex-2026.5.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ea9c8ecfa1b73c73b626534d6626e5340d429630943672b8480724f44e84b962", size = 860095, upload-time = "2026-05-09T23:12:51.666Z" }, - { url = "https://files.pythonhosted.org/packages/18/a3/bd855e0f2cb1a978ecf6fa6bb69632dd9c3f6ea3b81cde62fde14c9daec7/regex-2026.5.9-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cd2846168eb9ee3c513902bc8225409cb1caab31d04728b145171fa1625d9621", size = 765762, upload-time = "2026-05-09T23:12:53.413Z" }, - { url = "https://files.pythonhosted.org/packages/dc/66/0ae8c092e60b14c79d24f8e0b7f0aea5bfbffdcab00b5483d13404d3c3a5/regex-2026.5.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39617fb0cde9c0e6306dc70e3bfc096f3da793219879f7ae7aa341a69fbdcf6d", size = 852100, upload-time = "2026-05-09T23:12:55.256Z" }, - { url = "https://files.pythonhosted.org/packages/21/de/8dfde60fc1b21c946a893ba273403b72617edb261370cb1087099a83f088/regex-2026.5.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd03c4f0e33280d15cae17159b899245d6b7c53d21def19b263b39655061f5ce", size = 789479, upload-time = "2026-05-09T23:12:57.573Z" }, - { url = "https://files.pythonhosted.org/packages/c3/1c/bdcc98f9a4af4fdd166c74941174619ccff4726d3ce32faa8e9a2ecd38dd/regex-2026.5.9-cp312-cp312-win32.whl", hash = "sha256:164eba9b755ea6f244b0d881196fbc1fac09714e9782c9e2732b813142033c8e", size = 266699, upload-time = "2026-05-09T23:12:59.14Z" }, - { url = "https://files.pythonhosted.org/packages/78/87/240d36864f9e48ace85f72e79ced97ceb7f27ce87739a947dcb834b4e6bc/regex-2026.5.9-cp312-cp312-win_amd64.whl", hash = "sha256:86f40a5d6444db30a125c9c9177e6b25dad981cbc37451fd838f145e6edac92e", size = 277783, upload-time = "2026-05-09T23:13:00.789Z" }, - { url = "https://files.pythonhosted.org/packages/4f/b5/7b30f312b0669dff5beebe5b0989dc2d1a312b1a44fab852199c387a5b96/regex-2026.5.9-cp312-cp312-win_arm64.whl", hash = "sha256:96f5f58b54a063d7ea9dca08e1cf57bfe10499c4d579ee672da284f57f5f0070", size = 270513, upload-time = "2026-05-09T23:13:02.426Z" }, - { url = "https://files.pythonhosted.org/packages/aa/da/797e91ecec6f84135da778ddce78c20e0af5d2a15c26f87a81bc3eadb6db/regex-2026.5.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d626b84406444b165fc0ba981604edea39f0588ff1f92baa23fe50799ea9afdb", size = 490303, upload-time = "2026-05-09T23:13:04.382Z" }, - { url = "https://files.pythonhosted.org/packages/44/da/bf30abaaa737b58f4a4b8c4a03659e02fd92092c822e0197ed9e0daab917/regex-2026.5.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d7bdc0ab8f3dd7e1b4f9ab88634e13374669db86bb3c72e8292f07ae313f539f", size = 292019, upload-time = "2026-05-09T23:13:06.022Z" }, - { url = "https://files.pythonhosted.org/packages/2d/e7/d0eaf5713828417b9e5648cf81fa9bacd4961f6ab98c380c2034f8716e35/regex-2026.5.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a8820737949116ffff55fe18f9fc644530063ba6ebfcb8314239416e78f1347c", size = 289468, upload-time = "2026-05-09T23:13:08.214Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9b/b3fdd62b003baa1a9b593cd8c8699c9651c2e80cc21a5c715707983c42d7/regex-2026.5.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0fbdbac82cb3e4450d0ccde7d7a35607f4cb2dd9fba4b8b69bfaf8c9fa6aed", size = 796749, upload-time = "2026-05-09T23:13:10.573Z" }, - { url = "https://files.pythonhosted.org/packages/d4/30/66ab84588765f5b4b271a9ca09ef7ce2b87caa95176ec3d2ad65d7bc4902/regex-2026.5.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57e8915c7986aa33d25e4d3629cef711cd2863f2961b10409f0c04cb8b7d9020", size = 865445, upload-time = "2026-05-09T23:13:12.523Z" }, - { url = "https://files.pythonhosted.org/packages/1a/89/f05169e8588aac365f35ffc7f3bc3184f095ef4cfded7cfaa3c7fd5dbd89/regex-2026.5.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508f56a89ba9cb26e4168cbc37dbd60a28d82430a9e18ad1d25fe0883c314ca2", size = 912322, upload-time = "2026-05-09T23:13:14.281Z" }, - { url = "https://files.pythonhosted.org/packages/30/e1/c93444052cf41581f3c884ab3fb5823daf0992f11cd4388d4275ca610558/regex-2026.5.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d189041f15691cfa2b6c4290448ec221244d225b3f5fe9e7771b34ffcdf6e2", size = 801269, upload-time = "2026-05-09T23:13:16.569Z" }, - { url = "https://files.pythonhosted.org/packages/50/fe/0cf96b882f540e62e8b9956599798203d599c44cf4c77917ca27400ff69b/regex-2026.5.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e82db382b44d0111b22601c509c89f64434816c9e0eef9d1989cda8cc6ff1c04", size = 777085, upload-time = "2026-05-09T23:13:18.675Z" }, - { url = "https://files.pythonhosted.org/packages/23/5c/d78d4924e7fc875557b9e9b768423925fdfaac5549d06da7810019a9bd26/regex-2026.5.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2acfb48634f64996b57f90f39afa692ff362162722581921fe92239a59960f3c", size = 785153, upload-time = "2026-05-09T23:13:20.525Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e0/5214774090e7b4524dcea3e3c4aa74141d43043f8beb49c1599db1c8b53a/regex-2026.5.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d29eebfc9525db68cad3c97eedd7f754fa265aa5cd0cf4f863b2421e1b48fc9f", size = 860164, upload-time = "2026-05-09T23:13:22.263Z" }, - { url = "https://files.pythonhosted.org/packages/6e/e1/4a57a83350319b1271f0d7a249b8672513ed928b237a741631270de6caea/regex-2026.5.9-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:debb893095e944091c16e641a6e33c1b0f4cb61ab945ec5afbf53ce7068834d8", size = 765731, upload-time = "2026-05-09T23:13:24.277Z" }, - { url = "https://files.pythonhosted.org/packages/12/f4/499e74a20c156fc75836ee04a72a38d1a063978f600937f9760467beb1b0/regex-2026.5.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d659eee77986549c9ea45b861c7567e44d6287c3dc9a4565478853f7b9fe2ff6", size = 852062, upload-time = "2026-05-09T23:13:26.125Z" }, - { url = "https://files.pythonhosted.org/packages/5b/92/7eebc0d0a01e78629695f342ba17e0deaff8fb45e79cc0d7b98287da6e3e/regex-2026.5.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2efa205e6d98b24d1f3ab395c11aa15cdf10935bca283d0285e0499c284fba21", size = 789577, upload-time = "2026-05-09T23:13:27.814Z" }, - { url = "https://files.pythonhosted.org/packages/05/a4/018e71f7d2ad48c1ebe6d3ae0026f9b7cb4802fd15c7cc02fdf724355102/regex-2026.5.9-cp313-cp313-win32.whl", hash = "sha256:f3844f134e834076677dd369976e9f5068679fcb8e50102fdf6b7ac96a3ec127", size = 266691, upload-time = "2026-05-09T23:13:29.549Z" }, - { url = "https://files.pythonhosted.org/packages/e6/1d/861a93719fb9ee7dbfc3761b3797b7a3e112a5d42c6129459d2d741be9b5/regex-2026.5.9-cp313-cp313-win_amd64.whl", hash = "sha256:3527bb4942d2c14552155406cdedd906567456821848aed1cb4933a391bf5eca", size = 277747, upload-time = "2026-05-09T23:13:31.859Z" }, - { url = "https://files.pythonhosted.org/packages/d9/c6/0a2436ae4da1ba76e51cb98943c6838a9a721faa40ebe2dce07694ae34e3/regex-2026.5.9-cp313-cp313-win_arm64.whl", hash = "sha256:56a33f191f17d8c417f99945ebdc1e691d3af9605d86ec68c7e54a57e3e17af6", size = 270500, upload-time = "2026-05-09T23:13:33.525Z" }, - { url = "https://files.pythonhosted.org/packages/e8/e9/d21346f7b60ed58789371358ed66b09d00f832e1bd7c06e55d9da5679882/regex-2026.5.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:01f28d868834624c934b8d2e0aa1c8341337e37831f4a012f18a5afcba4cbaf3", size = 494172, upload-time = "2026-05-09T23:13:35.935Z" }, - { url = "https://files.pythonhosted.org/packages/c4/43/fd1177a2032037c681baecdb3422ee4e1424aec4e4f470ef47793d325274/regex-2026.5.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:48036f6374aaa79eb3b754ec29c61d1c6b1606749d705a13f8854fa2539671f6", size = 293952, upload-time = "2026-05-09T23:13:38.307Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7d/9fbf919768368d3f8a4f6c692cf2aa61e482b2b81ec6a298ace4cbf02480/regex-2026.5.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b96350aa424e79d4fd6b567b344dcbe2b2d6bfc48dfe7717587e1fa6d43da6ff", size = 292314, upload-time = "2026-05-09T23:13:40.353Z" }, - { url = "https://files.pythonhosted.org/packages/e2/6c/e41bfeecb589716843e7c4df09ba46ff2a42961457afece19059d85caeef/regex-2026.5.9-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f3af7a4903c5c04a11a196a5aa75cdd7dd3f8508132f9fb3259d9f5908e3b88", size = 811681, upload-time = "2026-05-09T23:13:42.543Z" }, - { url = "https://files.pythonhosted.org/packages/87/83/a5c1c525fba0aa656e88ad0face0b1829788ef4c2fb6b26df58aa1151b84/regex-2026.5.9-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e87577720152d2caae19fe2baaf1f8d5ca12091e9e229f03915c37d1e4b9178", size = 871135, upload-time = "2026-05-09T23:13:44.326Z" }, - { url = "https://files.pythonhosted.org/packages/18/d4/80882e799e440dd878b0979cbebf8fa4d54624a332c83037c7a701649e3f/regex-2026.5.9-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c8b9b9d294cfea3cd19c718ade7cc93492b2c4991abd9a68d0b3477ae6d8e100", size = 917265, upload-time = "2026-05-09T23:13:47.295Z" }, - { url = "https://files.pythonhosted.org/packages/ae/ff/8db60211e2286e396aad7dc7725356c502bff0901ea05bd6cdc2e1a042b9/regex-2026.5.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:728d8bfd28a8845c8b6bc5dc7ce010453d206396786c0765c2740cb65f37791e", size = 816311, upload-time = "2026-05-09T23:13:49.885Z" }, - { url = "https://files.pythonhosted.org/packages/4c/47/742ef579c61730f8d268e5cf1f9ce0e37e2ea041ad0f5644724f2378e463/regex-2026.5.9-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7e30b874d341fac767d7df5a0870540541c2c054b80cfaac116e8d367a8a7ff2", size = 785498, upload-time = "2026-05-09T23:13:52.25Z" }, - { url = "https://files.pythonhosted.org/packages/7f/ab/cb0999802dcb0fb95b1ab005e8d4163d8afdd67efc2cb6b6630ac13f8cb1/regex-2026.5.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fd190e88a895a8901325fad284a3f74ea52b1da8525b76cc811fa9b1edf0ce2b", size = 801348, upload-time = "2026-05-09T23:13:54.127Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/8ca59a24c55bc34d166eefaf3717bd77772f329fdbf984d86581e0a3571c/regex-2026.5.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:8e76e8161ad00694cfce6767d5dea860c6391ac5b83e5c3a39661e696f11fc7e", size = 866493, upload-time = "2026-05-09T23:13:56.067Z" }, - { url = "https://files.pythonhosted.org/packages/8d/3d/30f2ae62cef3278bb5bb821f467277a55fb73f01032cf85997e15e8289a8/regex-2026.5.9-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ddda5340e6c01a293027dd46232fa79eaff1b48058ce7a98f572b6445b088041", size = 772811, upload-time = "2026-05-09T23:13:57.867Z" }, - { url = "https://files.pythonhosted.org/packages/d8/ae/7d2089bcd78ad0c0161bc684339df50032acb438a7bd3305e7ddb1193cec/regex-2026.5.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:205109e96b3cf5adf8f4cd62bedde9487feb282b9497a3535451e5a24cd706a0", size = 856584, upload-time = "2026-05-09T23:13:59.679Z" }, - { url = "https://files.pythonhosted.org/packages/a9/29/92ff47f75990131ea4f24ba17819e5a9d141e10819807e09addd73409af6/regex-2026.5.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbe4579b9f08036aa7d101d1835437a20783574ac66327e6b29b4018a138081", size = 803453, upload-time = "2026-05-09T23:14:01.978Z" }, - { url = "https://files.pythonhosted.org/packages/04/99/eff29f1037dcab36702c9ee5d6858cf1ce2336ea8ea2987f64245b99ea5e/regex-2026.5.9-cp313-cp313t-win32.whl", hash = "sha256:ed2c9e8068b614c574d8d30e543d617cf5379b0535d46f97ef00e904745a08b5", size = 269951, upload-time = "2026-05-09T23:14:03.661Z" }, - { url = "https://files.pythonhosted.org/packages/0e/9d/8870b8981d27b22cda77bb26a5ac7ebfa9c7d9e0dea195a834a82380e748/regex-2026.5.9-cp313-cp313t-win_amd64.whl", hash = "sha256:b46b0f094dc1d3b90356c85a0bd2c9bafc4a6a190b9d6f8ddd5a033b6e088ed4", size = 281240, upload-time = "2026-05-09T23:14:05.56Z" }, - { url = "https://files.pythonhosted.org/packages/72/b1/3379415e8f135c13ac551353397cc4fe97b4978f3cac73c5fcbcded548b8/regex-2026.5.9-cp313-cp313t-win_arm64.whl", hash = "sha256:872acc074bd29ffc9913ecdfedf6ea77502312ca44a4aa0d3779089c6069d8de", size = 272383, upload-time = "2026-05-09T23:14:07.843Z" }, - { url = "https://files.pythonhosted.org/packages/13/3e/9c3cd292d8808b3645a2ce517e200179b6d0e903f176300bd8b542e14de5/regex-2026.5.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:1bd7587a2948b4085195d5a3374eaf4a425dc3e55784c038175355ecf3bbbf8a", size = 490376, upload-time = "2026-05-09T23:14:09.64Z" }, - { url = "https://files.pythonhosted.org/packages/60/70/d43ee8a2ca0a8b68d167f21658b85520ac0574617c7f320367c5047f7556/regex-2026.5.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dea2e88e1cce4522496cce630e11e67b98b7076620bc4336c3f674bc21a375f4", size = 291964, upload-time = "2026-05-09T23:14:11.424Z" }, - { url = "https://files.pythonhosted.org/packages/21/91/9d50b433828d8e74196904e168a43abf1e6e88b2a15d47ed742456720c37/regex-2026.5.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2099f7e7ff7b6aa3192312650a56e91cc091e49d50b04e4f6f8b6e28b3b27f1c", size = 289682, upload-time = "2026-05-09T23:14:13.123Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/b835e3cafbb9d977736912436259ff551d60919f7d7b3d37d46659c63564/regex-2026.5.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecd353045824e4477562a2ac718c25799cdaaa41f7aa925a806a8a3e6848a5b9", size = 796996, upload-time = "2026-05-09T23:14:14.923Z" }, - { url = "https://files.pythonhosted.org/packages/2c/a6/9f992d00019166b9de01c546dd4549bc679f2a68df11b877740b0760b7c2/regex-2026.5.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65c8c8c37377794bd5b2f3ebe51919042bf17aec802e23c833d89782ed0c78af", size = 866089, upload-time = "2026-05-09T23:14:17.757Z" }, - { url = "https://files.pythonhosted.org/packages/e0/08/4d32af657e049b19cb62b02e46e38fe1518797bfb2203ee93a510b21b0dc/regex-2026.5.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b73ab8afcf66c622db143d1c6fda4e58e4d537ee4f125229ad47b1ab80f34c0", size = 911530, upload-time = "2026-05-09T23:14:20.353Z" }, - { url = "https://files.pythonhosted.org/packages/d9/27/2af43dd1dc201d1fecefda64a45f4ad0995855b92724f795a777b402ee69/regex-2026.5.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0de5cf193997384ed2ca6f1cd4f78055b255d93d82d5a8cd6ba0d11c10b167e4", size = 800643, upload-time = "2026-05-09T23:14:22.265Z" }, - { url = "https://files.pythonhosted.org/packages/a4/dd/23a249047013b5321d4a60c4d2437462086f601b061776a525e5fba2a59f/regex-2026.5.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d641a8c9a61618047796d572a39a79b26167b0411d2c3031937b2fe2d081e2cf", size = 777223, upload-time = "2026-05-09T23:14:24.179Z" }, - { url = "https://files.pythonhosted.org/packages/94/6a/e85ed9538cd19586d0465076a4578a12e093ce776d15f3f8ce92733a8dd6/regex-2026.5.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:24b2355ef5cc9aa5b8f07d17704face1c166fdcc2290fa7bd6e6c925655a8346", size = 785760, upload-time = "2026-05-09T23:14:26.065Z" }, - { url = "https://files.pythonhosted.org/packages/2a/c4/f25473209438638e947c55f9156fd8f236f74169229028cc99116380868e/regex-2026.5.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a24852d3c29ad9e47593593d8a247c44ccc3d0548ef12c822d6ed0810affe676", size = 860891, upload-time = "2026-05-09T23:14:28.17Z" }, - { url = "https://files.pythonhosted.org/packages/f9/f7/f4f86e3c74419c37370e91f150ae0c2ef7d34b2e0e4cdd5da046a02e4022/regex-2026.5.9-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:916714069da19329ef7de197dcbc77bb3104145c7c2c864dbfbe318f46b88b14", size = 765891, upload-time = "2026-05-09T23:14:30.06Z" }, - { url = "https://files.pythonhosted.org/packages/26/70/704d8e13765939146b1cd0ef4e2feb71d7929727d2290f026eed10095955/regex-2026.5.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:fa411799ca8da32a8d38d020a88faa5b6f91657d284761352940ecf9f7c3bbdd", size = 851380, upload-time = "2026-05-09T23:14:32.123Z" }, - { url = "https://files.pythonhosted.org/packages/26/29/1a13582a8460038edc38e49f64ceb0dd7c60f5caba77571f4bf6601965d9/regex-2026.5.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e6da47d679b7010ef27556b6e0f99771b744936db1792a10ceac6547ae1503e", size = 789350, upload-time = "2026-05-09T23:14:34.799Z" }, - { url = "https://files.pythonhosted.org/packages/73/56/3dcafe34fc72e271d62ad9a291801e88a1457bb251c132f15fcc2e5aad1a/regex-2026.5.9-cp314-cp314-win32.whl", hash = "sha256:98bd73080e8756255137e1bd3f3f00295bbc5aa383c0e0f973920e9134d7c4ad", size = 272130, upload-time = "2026-05-09T23:14:36.729Z" }, - { url = "https://files.pythonhosted.org/packages/d0/9c/02eebf0be95efe416c664db7fb8b6b05b7a0b06a7544f2884f2558b0526f/regex-2026.5.9-cp314-cp314-win_amd64.whl", hash = "sha256:ff8d372ac2acdc048d1c19916f27ee61bc5722728458ba6ca5052f2c72d51763", size = 280999, upload-time = "2026-05-09T23:14:39.126Z" }, - { url = "https://files.pythonhosted.org/packages/70/5a/1dd1abee76cb7a846a0bcf42fdc87e5720c3c33c24f3e37814310a513d9f/regex-2026.5.9-cp314-cp314-win_arm64.whl", hash = "sha256:e1d93bf647916292e8edcec150c07ddf3dc50179ccaf770c04a7f9e452155372", size = 273500, upload-time = "2026-05-09T23:14:41.059Z" }, - { url = "https://files.pythonhosted.org/packages/86/c1/c5f619b0057a7965cb78ec559c1d7a45ce8c99a35bea95483d64959a93d9/regex-2026.5.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:83d0ee4a57d1c87cb549e195ec300b8f0ec3a82eba66d835e4e2ed8634fe4499", size = 494269, upload-time = "2026-05-09T23:14:42.869Z" }, - { url = "https://files.pythonhosted.org/packages/05/2c/5d01f1aee33de4bbe60c8452945bfc8477ca7c5ae4450f6bfe711036cb36/regex-2026.5.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d3d7eb5c9a7f6df82ed3cfac9beb93882a5cbcb5b8b157b56cb2b3b276574ac1", size = 293954, upload-time = "2026-05-09T23:14:44.822Z" }, - { url = "https://files.pythonhosted.org/packages/7a/fe/e8988b2ae2108c6ef71bd4aa8d87fbe257976dd0810e826cd75f701c68b6/regex-2026.5.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:075160bf16658e16d35233300b8453aac25de4cbea808d22348b6979668e924d", size = 292405, upload-time = "2026-05-09T23:14:47.211Z" }, - { url = "https://files.pythonhosted.org/packages/79/34/d2b0937faa7859263f7f0a3c6b103a1296306be6952dc173d0154e9a2f49/regex-2026.5.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45375819235558a4ff1c4971dc32881f022613abdb180128f5cb4768c1765a1c", size = 811855, upload-time = "2026-05-09T23:14:49.21Z" }, - { url = "https://files.pythonhosted.org/packages/80/fe/daf53a47457a8486db66c66c01ceb9c2303eecee3f87197f1e77eb1a736d/regex-2026.5.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ead4b163ac30a29574510cd4b3e2e985ac5290c05fc7095557d6a5f403fc31b5", size = 871189, upload-time = "2026-05-09T23:14:51.555Z" }, - { url = "https://files.pythonhosted.org/packages/1c/75/058fc4470cbfbf57d800aff1a0022b929a3f9fa553ee10a0cdf2070eb31f/regex-2026.5.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c6e4218fbdfbcd4f6c19efca40930d24a621bf4b48cb76bc6640543bd28ef20", size = 917485, upload-time = "2026-05-09T23:14:53.633Z" }, - { url = "https://files.pythonhosted.org/packages/88/e7/179cfda3a28bc843b5c6cfe7f79f23489c791ed95f151083803660878432/regex-2026.5.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6351571c8a42b505eb555c0dc47d740d0fb66977dc142919eea6f4325b7c56a0", size = 816369, upload-time = "2026-05-09T23:14:56.198Z" }, - { url = "https://files.pythonhosted.org/packages/41/90/6f0cc422071688266d344fca8462d787cba0a2c144acb25721f9a61ec265/regex-2026.5.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:002205cafd2a9e78c6290c7d1df277bf3277b3b7a30e0b4bb0dac2e2e3f7cb2d", size = 785869, upload-time = "2026-05-09T23:14:58.602Z" }, - { url = "https://files.pythonhosted.org/packages/02/67/a31f1760f09c27b251ef39e9beb541f462cf977381d067faa764c2c0e393/regex-2026.5.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8abd33fef90b2a9efac5557d6033ca82d1195ed3a15fea5af15ba7b463c6a63b", size = 801427, upload-time = "2026-05-09T23:15:00.642Z" }, - { url = "https://files.pythonhosted.org/packages/e3/c4/1a80654597b6bc1e1ea0494824c31200e8a956abe290afae9b19a166a148/regex-2026.5.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:31037c82eccb44b7ea2e9e221d7c01429430e989a1f4b91ea5a855f6017b509a", size = 866482, upload-time = "2026-05-09T23:15:03.384Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/960724e06482c08466ff5611e242e86f80062949cdf6b4b9cc317b9dd93d/regex-2026.5.9-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5604dfd046dc37eca90250fc3be938b076c8059fa772ac0ed6f499b0f0fb0415", size = 773022, upload-time = "2026-05-09T23:15:05.625Z" }, - { url = "https://files.pythonhosted.org/packages/50/a8/a9979c3e7918280e93159ebcab5ef1a65116dd4f3bd6091be0eae4a126e8/regex-2026.5.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e1b1b4e496afbb24f4a62aba855ee4f88f25578927697b340702e48c9ee6bc2", size = 856642, upload-time = "2026-05-09T23:15:07.966Z" }, - { url = "https://files.pythonhosted.org/packages/fe/d4/a9b732f2f0072c0ab12227483abb24fffcb9f73f8a2b203df0a6d0434735/regex-2026.5.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:be3372b9df6ddecff6486d37e19095a7b4973137caf5512407a89f4455361f41", size = 803552, upload-time = "2026-05-09T23:15:10.215Z" }, - { url = "https://files.pythonhosted.org/packages/d5/fe/1b3113817447a1d4155e4ac76d2e072f42c0bcba2f43fa8a0e756ea2cd91/regex-2026.5.9-cp314-cp314t-win32.whl", hash = "sha256:3ddd90103f9e5c471c49c7852ecc1fe27c7e45eb99e977aefe7caa4e779f4f58", size = 275746, upload-time = "2026-05-09T23:15:12.609Z" }, - { url = "https://files.pythonhosted.org/packages/92/73/93d42045302636c91f2e5ef588b65b84b01428f28ec77de256b1dfdfbe5c/regex-2026.5.9-cp314-cp314t-win_amd64.whl", hash = "sha256:ca518ed29c46eecba6010b15f1b9a479314d2de409536e71b6a13aa04e3b8a77", size = 285685, upload-time = "2026-05-09T23:15:15.086Z" }, - { url = "https://files.pythonhosted.org/packages/da/80/35b4c33c804a165a7f55289afda3ea9e3eb6d15800341a2d66455c0f1f30/regex-2026.5.9-cp314-cp314t-win_arm64.whl", hash = "sha256:5e41809d2683fcde7d5a8c87a6567ba1fb1ce0de9f31bff578de00a4b2d76daa", size = 275713, upload-time = "2026-05-09T23:15:16.98Z" }, +version = "2026.7.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/37/451aaddbf50922f34d744ad5ca919ae1fcfac112123885d9728f52a484b3/regex-2026.7.10.tar.gz", hash = "sha256:1050fedf0a8a92e843971120c2f57c3a99bea86c0dfa1d63a9fac053fe54b135", size = 416282, upload-time = "2026-07-10T19:49:46.267Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/69/62bb7d63f26698949c905cb7ebe29c7b0659e2a7f2a50c35cc29640b0852/regex-2026.7.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:799a369bdab91dcf0eb424ebd7aa9650897025ce22f729248d8f2c72002c4daa", size = 494652, upload-time = "2026-07-10T19:46:28.394Z" }, + { url = "https://files.pythonhosted.org/packages/3a/f2/eed2ce38cc38def9c366d060ec739ff5f235a33647ceb73ae6be37306d39/regex-2026.7.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f0192e5f1cfc70e3cb35347135dd02e7497b3e7d83e378aa226d8b3e53a93f19", size = 295920, upload-time = "2026-07-10T19:46:30.342Z" }, + { url = "https://files.pythonhosted.org/packages/36/57/4d724eeb1c440d71ccd6400d33b62b911bb62ca05385fe1961556e628319/regex-2026.7.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:221f2771cb780186b94bbf125a151bbeb242fa1a971da6ad59d7b0370f19de9a", size = 290696, upload-time = "2026-07-10T19:46:31.953Z" }, + { url = "https://files.pythonhosted.org/packages/c7/9d/76c6779e424c64740d2a564b7ecb389a62c77b6294127d34f52008c91ea7/regex-2026.7.10-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab2fb1f7a2deb4ca3ddebbae6b93905d21480a3b4e11de28d79d9fb0d316fcf8", size = 784833, upload-time = "2026-07-10T19:46:33.145Z" }, + { url = "https://files.pythonhosted.org/packages/54/ec/f777841d88f0c9d46699daf16f86a8086e077e506603be212de2c4584f85/regex-2026.7.10-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f98ef73a13791a387d5c841416ad7f52040ae5caf10bcf46fa12bd2b3d63745", size = 852182, upload-time = "2026-07-10T19:46:34.368Z" }, + { url = "https://files.pythonhosted.org/packages/d1/de/5ba208a0826117851f6c12af9ae7fae5838ccfde69b999b26c5fdc1cedc3/regex-2026.7.10-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9a094ed44a22f9da497453137c3118b531fd783866ab524b0b0fc146e7395e1d", size = 899571, upload-time = "2026-07-10T19:46:35.564Z" }, + { url = "https://files.pythonhosted.org/packages/7f/46/602b7b81d26a53113d3cec6dd845fd664d7b854b0ea45245bfdd6b9dc9ef/regex-2026.7.10-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53bbbd6c610489700f7110db1d85f3623924c3f7c760f987eca033867360788a", size = 794164, upload-time = "2026-07-10T19:46:36.972Z" }, + { url = "https://files.pythonhosted.org/packages/53/cc/c21ebc520c3e17d93e495eb2de2b1c5ae5f6780ccdb298b2d8ce1e940820/regex-2026.7.10-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:87b776cf2890e356e4ab104b9df846e169da3eb5b0f110975547091f4e51854e", size = 786304, upload-time = "2026-07-10T19:46:38.238Z" }, + { url = "https://files.pythonhosted.org/packages/65/d8/69ec8c062bbba8d4d153f9eb70ab43564f591035b81fc4ea7eb059fdf71c/regex-2026.7.10-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ab39d2c967aae3b48a412bff9cdbe7cd7559cd1e277599aceaeada7bc82b7200", size = 769958, upload-time = "2026-07-10T19:46:39.541Z" }, + { url = "https://files.pythonhosted.org/packages/af/82/c56db326ac5f852865bd78d49a275757cc367e8114b13bc1015ed2491db1/regex-2026.7.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b56416091bfd7a429f958f69aaf6823c517be9a49cb5bf1daa3767ce8bf8095e", size = 775056, upload-time = "2026-07-10T19:46:40.808Z" }, + { url = "https://files.pythonhosted.org/packages/af/2a/1ba62462f679eb598d7325ff20798a264ddb9aa34a3f5d2ac388d54c6e4b/regex-2026.7.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:617e8f10472e34a8477931f978ff3a88d46ae2ba0e41927e580b933361f60948", size = 848857, upload-time = "2026-07-10T19:46:42.02Z" }, + { url = "https://files.pythonhosted.org/packages/84/5b/37bf2e2fe810540ca90bbfe457a2f61088721c95d442eee8bb1257165ad7/regex-2026.7.10-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:31fa17378b29519bfd0a1b8ba4e9c10cf0baf1cf4099b39b0689429e7dc2c795", size = 757747, upload-time = "2026-07-10T19:46:43.282Z" }, + { url = "https://files.pythonhosted.org/packages/9a/c5/131dd41f73f766af6b08492073b5f28bc4f907b5571230c9f9e895e88302/regex-2026.7.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5c363de7c0339d39341b6181839ed32509820b85ef506deafcf2e7e43baadab4", size = 837183, upload-time = "2026-07-10T19:46:44.91Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ee/00b9332c3c5d7460639f2c10fdc7197a64de6eaafff69478ec3332815335/regex-2026.7.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ed7c886a2fcbf14493ceaf9579394b33521730c161ebb8dad7db9c3e9fcab1a8", size = 782151, upload-time = "2026-07-10T19:46:46.385Z" }, + { url = "https://files.pythonhosted.org/packages/e5/a7/31ce26ec6465c12e7136ea9efcc716f5c55cac616f7958c33f0bf171781e/regex-2026.7.10-cp310-cp310-win32.whl", hash = "sha256:b04583e8867136ae66353fa274f45121ab3ec3166dc45aaff3655a5db90d9f0e", size = 266770, upload-time = "2026-07-10T19:46:47.608Z" }, + { url = "https://files.pythonhosted.org/packages/71/00/22a554bb83203eef88a40ec2fe7cf9a6e5df8765d57f7a96ebe1af5d60c3/regex-2026.7.10-cp310-cp310-win_amd64.whl", hash = "sha256:e21e888a6b471b2bb1cdd4247e8d86632672232f29be583e7eafaa5f4634d34c", size = 277941, upload-time = "2026-07-10T19:46:48.915Z" }, + { url = "https://files.pythonhosted.org/packages/76/46/596a7084918ddf18cea6fb0cc047af1f00cec8790962e8f2edee9b6ec749/regex-2026.7.10-cp310-cp310-win_arm64.whl", hash = "sha256:081acf191b4d614d573a56cab69f948b6864daa5e3cc69f209ee92e26e454c2f", size = 276923, upload-time = "2026-07-10T19:46:50.829Z" }, + { url = "https://files.pythonhosted.org/packages/3d/16/bfd13770be1acd1c05506b93fc6be15c759d6417595d1ba334d355efbf26/regex-2026.7.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:66d2c35587cd601c95965d5c0415058ba5cfd6ffbab7624ce198bd967102b341", size = 494639, upload-time = "2026-07-10T19:46:52.207Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b4/0086215709f0f705661f13ba81516287538886ef0d589c545c12b0484669/regex-2026.7.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28a0973eeffff4292f5a7ee498ab65d5e94ee8cc9cea364239251eb4a260a0f1", size = 295920, upload-time = "2026-07-10T19:46:53.63Z" }, + { url = "https://files.pythonhosted.org/packages/8e/9e/8e07d0eea46d2cf36bf4d3794634bb0a820f016d31bc349dfef008d96b02/regex-2026.7.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8331484450b3894298bef8abecce532171ff6ac60b71f999eed10f2c01941a8a", size = 290673, upload-time = "2026-07-10T19:46:54.863Z" }, + { url = "https://files.pythonhosted.org/packages/b7/56/d83c446de21c70ff49d2f1b2ff2196ac79a4ac6373d2cfe496011a250600/regex-2026.7.10-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0639b2488b775a0109f55a5a2172deebdedb4b6c5ab0d48c90b43cbf5de58d17", size = 792378, upload-time = "2026-07-10T19:46:56.116Z" }, + { url = "https://files.pythonhosted.org/packages/dc/0e/d265e0cc6da47aea97e90eb896be2d2e8f92d16add13bac04fa46a0fd972/regex-2026.7.10-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:be4223af640d0aa04c05db81d5d96ada3ead9c09187d892fd37f4f97829480be", size = 861790, upload-time = "2026-07-10T19:46:57.611Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a5/62655f6208d1170a3e9188d6a45d4af0a5ae3b9da8b87d474818ac5ff016/regex-2026.7.10-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d3c75d57a00109255e60bc9c623b6ececaf7905eaab845c79f036670ed4750a2", size = 906530, upload-time = "2026-07-10T19:46:59.142Z" }, + { url = "https://files.pythonhosted.org/packages/86/b7/d65aa2e9ffb18677cd0afbcf5990da8519a4e50778deb1bca49f043c5174/regex-2026.7.10-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:724ee9379568658ec06362cf24325c5315cc5a67f61dfe585bfeff58300a355b", size = 799912, upload-time = "2026-07-10T19:47:00.534Z" }, + { url = "https://files.pythonhosted.org/packages/8f/19/3a5ce23ea2eb1fe36306aef49c79746ce297e4b434aeb981b525c661413a/regex-2026.7.10-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:732c19e5828eb287d01edb83b2eb87f283ba8e5fc3441c732709d3e8cbd14aaa", size = 773675, upload-time = "2026-07-10T19:47:01.999Z" }, + { url = "https://files.pythonhosted.org/packages/fe/76/3c0eaa426700dd2ba14f2335f2b700a4e1484202254192ae440b83b8352a/regex-2026.7.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:982d07727c809b42a3968785354f11c3728414e4e90af0754345b431b2c32561", size = 781711, upload-time = "2026-07-10T19:47:03.425Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a8/a5a3fad84f9a7f897619f0f8e0a2c64946e9709044a186a8f869fb5c332f/regex-2026.7.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4574feca202f8c470bf678aed8b5d89df04aaf8dc677f3b83d92825051301c0f", size = 854539, upload-time = "2026-07-10T19:47:04.999Z" }, + { url = "https://files.pythonhosted.org/packages/f8/c7/47e9b8c8ee77723b9eda74f517b6b25d2f555cf276c063a9eeea35bd86d5/regex-2026.7.10-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:80151ca5bfc6c4524186b3e08b499e97319b2001fc265ed2d4fc12c0d5692cdf", size = 763378, upload-time = "2026-07-10T19:47:06.845Z" }, + { url = "https://files.pythonhosted.org/packages/36/09/e27e42d9d42edf71205c7e6f5b2902bc874ea03c557c80da03b8ed16c9bf/regex-2026.7.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bb52e10e453b5493afe1f7702a2973bc10f4dd8901c0f2ed869ffaa3f8319296", size = 844663, upload-time = "2026-07-10T19:47:08.923Z" }, + { url = "https://files.pythonhosted.org/packages/a1/b5/2423acb98362184ad9c8eebabafa15188d6a177daab919add8f2120fc6cd/regex-2026.7.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e37aba1994d73b4944053ab65a15f313bd5c28c885dd7f0d494a11749d89db6e", size = 789236, upload-time = "2026-07-10T19:47:10.303Z" }, + { url = "https://files.pythonhosted.org/packages/60/ed/b387e84c8a3d6aa115dfb56865437a3fbaf28f4a6fb3b76cc6cce38ced70/regex-2026.7.10-cp311-cp311-win32.whl", hash = "sha256:6cbedeb5112f59dbd169385459b9943310bdd241c6966c19c5f6e2295055c93a", size = 266774, upload-time = "2026-07-10T19:47:11.904Z" }, + { url = "https://files.pythonhosted.org/packages/c4/64/f30a163a65ed1f07ad12c53af00a6bd2a7251a5329fba5a08adc6f9e81a3/regex-2026.7.10-cp311-cp311-win_amd64.whl", hash = "sha256:b1963ec5ba4d52788fb0eac6aca6eb8040e8e318c7e47ebbdfc09440c802919c", size = 277959, upload-time = "2026-07-10T19:47:13.231Z" }, + { url = "https://files.pythonhosted.org/packages/2b/de/61c8174171134cebb834ca9f8fe2ff8f49d8a3dd43453b48b537d0fbb49b/regex-2026.7.10-cp311-cp311-win_arm64.whl", hash = "sha256:3750c42d47712e362158a04d0fd80131f73a55e8c715b2885442a0ff6f9fc3fc", size = 276918, upload-time = "2026-07-10T19:47:14.693Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9c/2503d4ccf3452dc323f8baa3cf3ee10406037d52735c76cfced81423f183/regex-2026.7.10-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7252b48b0c60100095088fbeb281fca9a4fcf678a4e04b1c520c3f8613c952c4", size = 497114, upload-time = "2026-07-10T19:47:16.22Z" }, + { url = "https://files.pythonhosted.org/packages/91/eb/04534f4263a4f658cd20a511e9d6124350044f2214eb24fee2db96acf318/regex-2026.7.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:da6ef4cb8d457aab0482b50120136ae94238aaa421863eaa7d599759742c72d6", size = 297422, upload-time = "2026-07-10T19:47:17.794Z" }, + { url = "https://files.pythonhosted.org/packages/ca/2d/35809de392ab66ba439b58c3187ae3b8b53c883233f284b59961e5725c99/regex-2026.7.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fe7ff456c22725c9d9017f7a2a7df2b51af6df77314176760b22e2d05278e181", size = 292110, upload-time = "2026-07-10T19:47:19.188Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1e/5ce0fbe9aab071893ce2b7df020d0f561f7b411ec334124302468d587884/regex-2026.7.10-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3463a5f26be513a49e4d497debcf1b252a2db7b92c77d89621aa90b83d2dd38", size = 796800, upload-time = "2026-07-10T19:47:20.639Z" }, + { url = "https://files.pythonhosted.org/packages/d4/67/c1ccbada395c10e334763b583e1039b1660b142303ebb941d4269130b22f/regex-2026.7.10-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:948dfc62683a6947b9b486c4598d8f6e3ecc542478b6767b87d52be68aeb55c6", size = 865509, upload-time = "2026-07-10T19:47:22.135Z" }, + { url = "https://files.pythonhosted.org/packages/0e/06/f0b31afc16c1208f945b66290eb2a9936ab8becdfb23bbcedb91cc5f9d9b/regex-2026.7.10-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c2cbd385d82f63bb35edb60b09b08abad3619bd0a4a492ae59e55afaf98e1b9d", size = 912395, upload-time = "2026-07-10T19:47:24.128Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1c/8687de3a6c3220f4f872a9bf4bcd8dc249f2a96e7dddfa93de8bd4d16399/regex-2026.7.10-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f6222cafe00e072bb2b8f14142cd969637411fbc4dd3b1d73a90a3b817fa046f", size = 801308, upload-time = "2026-07-10T19:47:25.696Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e3/60a40ec02a2315d826414a125640aceb6f30450574c530c8f352110ece0e/regex-2026.7.10-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:65ee5d1ac3cd541325f5ac92625b1c1505f4d171520dd931bda7952895c5321a", size = 777120, upload-time = "2026-07-10T19:47:27.158Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9a/ec579b4f840ac59bc7c192b56e66abd4cbf385615300d59f7c94bf6863ae/regex-2026.7.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa34473fbcc108fea403074f3f45091461b18b2047d136f16ffaa4c65ad46a68", size = 785164, upload-time = "2026-07-10T19:47:28.732Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1c/60d88afd5f98d4b0fb1f8b8969270628140dc01c7ff93a939f2aa83f31a6/regex-2026.7.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9d028d189d8f38d7ff292f22187c0df37f2317f554d2ed9a2908ada330af57c0", size = 860161, upload-time = "2026-07-10T19:47:30.605Z" }, + { url = "https://files.pythonhosted.org/packages/2a/40/08ae3ba45fe79e48c9a888a3389a7ee7e2d8c580d2d996da5ece02dfdcb9/regex-2026.7.10-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:396ea70e4ea1f19571940add3bad9fd3eb6a19dc610d0d01f692bc1ba0c10cb4", size = 765829, upload-time = "2026-07-10T19:47:32.06Z" }, + { url = "https://files.pythonhosted.org/packages/12/e6/e613c6755d19aca9d977cdc3418a1991ffc8f386779752dd8fdfa888ea89/regex-2026.7.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ebbf0d83ed5271991d666e54bb6c90ac2c55fb2ef3a88740c6af85dc85de2402", size = 852170, upload-time = "2026-07-10T19:47:33.567Z" }, + { url = "https://files.pythonhosted.org/packages/03/33/89072f2060e6b844b4916d5bc40ef01e973640c703025707869264ec75ab/regex-2026.7.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:58a4571b2a093f6f6ee4fd281faa8ebf645abcf575f758173ea2605c7a1e1ecb", size = 789550, upload-time = "2026-07-10T19:47:35.395Z" }, + { url = "https://files.pythonhosted.org/packages/e3/3c/4bc8be9a155035e63780ccac1da101f36194946fdc3f6fce90c7179fc6df/regex-2026.7.10-cp312-cp312-win32.whl", hash = "sha256:eac1207936555aa691ce32df1432b478f2729d54e6d93a1f4db9215bcd8eb47d", size = 267151, upload-time = "2026-07-10T19:47:37.047Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/9f5aade65bb98cc6e99c336e45a49a658300720c16721f3e687f8d754fec/regex-2026.7.10-cp312-cp312-win_amd64.whl", hash = "sha256:ecae626449d00db8c08f8f1fc00047a32d6d7eb5402b3976f5c3fda2b80a7a4f", size = 277751, upload-time = "2026-07-10T19:47:38.488Z" }, + { url = "https://files.pythonhosted.org/packages/36/6f/d069dd12872ea1d50e17319d342f89e2072cae4b62f4245009a1108c74d8/regex-2026.7.10-cp312-cp312-win_arm64.whl", hash = "sha256:87794549a3f5c1c2bdfba2380c1bf87b931e375f4133d929da44f95e396bf5fe", size = 277063, upload-time = "2026-07-10T19:47:40.023Z" }, + { url = "https://files.pythonhosted.org/packages/e0/88/0c977b9f3ba9b08645516eca236388c340f56f7a87054d41a187a04e134c/regex-2026.7.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:4db009b4fc533d79af3e841d6c8538730423f82ea8508e353a3713725de7901c", size = 496868, upload-time = "2026-07-10T19:47:41.675Z" }, + { url = "https://files.pythonhosted.org/packages/f6/51/600882cd5d9a3cf083fd66a4064f5b7f243ba2a7de2437d42823e286edaf/regex-2026.7.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b96341cb29a3faa5db05aff29c77d141d827414f145330e5d8846892119351c1", size = 297306, upload-time = "2026-07-10T19:47:43.521Z" }, + { url = "https://files.pythonhosted.org/packages/52/6f/48a912054ffcb756e374207bb8f4430c5c3e0ffa9627b3c7b6661844b30a/regex-2026.7.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:14d27f6bd04beb01f6a25a1153d73e58c290fd45d92ba56af1bb44199fd1010d", size = 291950, upload-time = "2026-07-10T19:47:45.267Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c8/8e1c3c86ebcee7effccbd1f7fc54fe3af22aa0e9204503e2baea4a6ff001/regex-2026.7.10-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6b6a11bf898cca3ce7bfaa17b646901107f3975677fbd5097f36e5eb5641983", size = 796817, upload-time = "2026-07-10T19:47:48.054Z" }, + { url = "https://files.pythonhosted.org/packages/65/39/3e49d9ff0e0737eb8180a00569b47aabb59b84611f48392eba4d998d91a0/regex-2026.7.10-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:234f8e0d65cf1df9becadae98648f74030ee85a8f12edcb5eb0f60a22a602197", size = 865513, upload-time = "2026-07-10T19:47:49.855Z" }, + { url = "https://files.pythonhosted.org/packages/70/57/6511ad809bb3122c65bbeeffa5b750652bb03d273d29f3acb0754109b183/regex-2026.7.10-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:91b916d495db3e1b473c7c8e68733beec4dce8e487442db61764fff94f59740e", size = 912391, upload-time = "2026-07-10T19:47:51.776Z" }, + { url = "https://files.pythonhosted.org/packages/cc/29/a1b0c109c9e878cb04b931bfe4c54332d692b93c322e127b5ae9f25b0d9e/regex-2026.7.10-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f0d4ccf70b1d13711242de0ba78967db5c35d12ac408378c70e06295c3f6644", size = 801338, upload-time = "2026-07-10T19:47:53.38Z" }, + { url = "https://files.pythonhosted.org/packages/33/be/171c3dad4d77000e1befeff2883ca88734696dfd97b2951e5e074f32e4dd/regex-2026.7.10-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c622f4c638a725c39abcb2e680b1bd592663c83b672a4ed350a17f806d75618e", size = 777149, upload-time = "2026-07-10T19:47:54.944Z" }, + { url = "https://files.pythonhosted.org/packages/33/61/41ab0de0e4574da1071c151f67d1eb9db3d92c43e31d64d2e6863c3d89bf/regex-2026.7.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:41a47c2b28d9421e2509a4583a22510dc31d83212fcf38e1508a7013140f71a8", size = 785216, upload-time = "2026-07-10T19:47:56.56Z" }, + { url = "https://files.pythonhosted.org/packages/66/28/372859ea693736f07cf7023247c7eca8f221d9c6df8697ff9f93371cca08/regex-2026.7.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:13fba679fe035037e9d5286620f88bbfd105df4d5fcd975942edd282ab986775", size = 860229, upload-time = "2026-07-10T19:47:58.278Z" }, + { url = "https://files.pythonhosted.org/packages/50/b1/e1d32cd944b599534ae655d35e8640d0ec790c0fa12e1fb29bf434d50f55/regex-2026.7.10-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8e26a075fa9945b9e44a3d02cc83d776c3b76bb1ff4b133bbfa620d5650131da", size = 765797, upload-time = "2026-07-10T19:48:00.291Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/79a2cd9556a3329351e370929743ef4f0ccc0aaff6b3dc414ae5fa4a1302/regex-2026.7.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d0834c84ae8750ae1c4cede59b0afd4d2f775be958e11b18a3eea24ed9d0d9f1", size = 852130, upload-time = "2026-07-10T19:48:01.972Z" }, + { url = "https://files.pythonhosted.org/packages/66/58/76fec29898cf5d359ab63face50f9d4f7135cc2eca3477139227b1d09952/regex-2026.7.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64722a5031aeace7f6c8d5ea9a9b22d9368af0d6e8fa532585da8158549ea963", size = 789644, upload-time = "2026-07-10T19:48:03.748Z" }, + { url = "https://files.pythonhosted.org/packages/f6/06/3c7cec7817bda293e13c8f88aed227bbcf8b37e5990936ff6442a8fdf11a/regex-2026.7.10-cp313-cp313-win32.whl", hash = "sha256:74ae61d8573ecd51b5eeee7be2218e4c56e99c14fa8fcf97cf7519611d4be92e", size = 267130, upload-time = "2026-07-10T19:48:05.677Z" }, + { url = "https://files.pythonhosted.org/packages/88/6c/e2a6f9a6a905f923cfc912298a5949737e9504b1ca24f29eda8d04d05ece/regex-2026.7.10-cp313-cp313-win_amd64.whl", hash = "sha256:5e792367e5f9b4ffb8cad93f1beaa91837056b94da98aa5c65a0db0c1b474927", size = 277722, upload-time = "2026-07-10T19:48:07.318Z" }, + { url = "https://files.pythonhosted.org/packages/00/a6/9d8935aaa940c388496aa1a0c82669cc4b5d06291c2712d595e3f0cf16d3/regex-2026.7.10-cp313-cp313-win_arm64.whl", hash = "sha256:82ab8330e7e2e416c2d42fcec67f02c242393b8681014750d4b70b3f158e1f08", size = 277059, upload-time = "2026-07-10T19:48:08.977Z" }, + { url = "https://files.pythonhosted.org/packages/7d/e9/26decfd3e85c09e42ff7b0d23a6f51085ca4c268db15f084928ca33459c6/regex-2026.7.10-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:2b93eafd92c4128bab2f93500e8912cc9ecb3d3765f6685b902c6820d0909b6b", size = 501508, upload-time = "2026-07-10T19:48:10.668Z" }, + { url = "https://files.pythonhosted.org/packages/38/a5/5b167cebde101945690219bf34361481c9f07e858a4f46d9996b80ec1490/regex-2026.7.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3f03b92fb6ec739df042e45b06423fc717ecf0063e07ffe2897f7b2d5735e1e8", size = 299705, upload-time = "2026-07-10T19:48:12.544Z" }, + { url = "https://files.pythonhosted.org/packages/f6/20/7909be4b9f449f8c282c14b6762d59aa722aeaeebe7ee4f9bb623eeaa5e0/regex-2026.7.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bb5aab464a0c5e03a97abad5bdf54517061ebbf72340d576e99ff661a42575cc", size = 294605, upload-time = "2026-07-10T19:48:14.495Z" }, + { url = "https://files.pythonhosted.org/packages/82/88/e52550185d6fda68f549b01239698697de47320fd599f5e880b1986b7673/regex-2026.7.10-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fadb07dbe36a541283ff454b1a268afd54b077d917043f2e1e5615372cb5f200", size = 811747, upload-time = "2026-07-10T19:48:16.197Z" }, + { url = "https://files.pythonhosted.org/packages/06/98/16c255c909714de1ee04da6ae30f3ee04170f300cdc0dcf57a314ee4816a/regex-2026.7.10-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:21150500b970b12202879dfd82e7fd809d8e853140fff84d08e57a90cf1e154e", size = 871203, upload-time = "2026-07-10T19:48:18.12Z" }, + { url = "https://files.pythonhosted.org/packages/3b/32/423ed27c9bae2092a453e853da2b6628a658d08bb5a6117db8d591183d85/regex-2026.7.10-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a68b637451d64ba30ed8ae125c973fa834cc2d37dfa7f154c2b479015d477ba8", size = 917334, upload-time = "2026-07-10T19:48:19.952Z" }, + { url = "https://files.pythonhosted.org/packages/73/87/74dac8efb500db31cb000fda6bae2be45fc2fbf1fa9412f445fbb8acbe37/regex-2026.7.10-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e23458d8903e33e7d27196d7a311523dc4e2f4137a5f34e4dbd30c8d37ff33e", size = 816379, upload-time = "2026-07-10T19:48:21.616Z" }, + { url = "https://files.pythonhosted.org/packages/a8/9f/1859403654e3e030b288f06d49233c6a4f889d62b84c4ef3f3a28653173d/regex-2026.7.10-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae27622c094558e519abf3242cf4272db961d12c5c9a9ffb7a1b44b2627d5c6", size = 785563, upload-time = "2026-07-10T19:48:23.643Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d8/35d30d6bdf1ef6a5430e8982607b3a6db4df1ddedbe001e43435585d88ba/regex-2026.7.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ee877b6d78f9dff1da94fef51ae8cf9cce0967e043fdcc864c40b85cf293c192", size = 801415, upload-time = "2026-07-10T19:48:25.499Z" }, + { url = "https://files.pythonhosted.org/packages/f7/22/630f31f5ea4826167b2b064d9cac2093a5b3222af380aa432cfe1a5dabcd/regex-2026.7.10-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:2c66a8a1969cfd506d1e203c0005fd0fc3fe6efc83c945606566b6f9611d4851", size = 866560, upload-time = "2026-07-10T19:48:27.789Z" }, + { url = "https://files.pythonhosted.org/packages/8d/14/f5914a6d9c5bc63b9bed8c9a1169fb0be35dbe05cdc460e17d953031a366/regex-2026.7.10-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:2bc350e1c5fa250f30ab0c3e38e5cfdffcd82cb8af224df69955cab4e3003812", size = 772877, upload-time = "2026-07-10T19:48:29.563Z" }, + { url = "https://files.pythonhosted.org/packages/c1/0f/7c13999eef3e4186f7c79d4950fa56f041bf4de107682fb82c80db605ff9/regex-2026.7.10-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:53f54993b462f3f91fea0f2076b46deb6619a5f45d70dbd1f543f789d8b900ef", size = 856648, upload-time = "2026-07-10T19:48:31.282Z" }, + { url = "https://files.pythonhosted.org/packages/a4/71/a48e43909b6450fb48fa94e783bef2d9a37179258bc32ef2283955df7be7/regex-2026.7.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cfcec18f7da682c4e2d82112829ce906569cb8d69fa6c26f3a50dfbed5ceb682", size = 803520, upload-time = "2026-07-10T19:48:33.275Z" }, + { url = "https://files.pythonhosted.org/packages/e0/b8/f037d1bf2c133cb24ceb6e7d81d08417080390eddab6ddfd701aa7091874/regex-2026.7.10-cp313-cp313t-win32.whl", hash = "sha256:a2d6d30be35ddd70ce0f8ee259a4c25f24d6d689a45a5ac440f03e6bcc5a21d1", size = 269168, upload-time = "2026-07-10T19:48:35.353Z" }, + { url = "https://files.pythonhosted.org/packages/b6/9c/eaac34f8452a838956e7e89852ad049678cdc1af5d14f72d3b3b658b1ea5/regex-2026.7.10-cp313-cp313t-win_amd64.whl", hash = "sha256:c57b6ad3f7a1bdd101b2966f29dc161adf49727b1e8d3e1e89db2eda8a75c344", size = 280004, upload-time = "2026-07-10T19:48:37.106Z" }, + { url = "https://files.pythonhosted.org/packages/cd/a9/e22e997587bc1d588b0b2cd0572027d39dd3a006216e40bbf0361688c51c/regex-2026.7.10-cp313-cp313t-win_arm64.whl", hash = "sha256:3d8ef9df02c8083c7b4b855e3cb87c8e0ebbcfea088d98c7a886aaefdf88d837", size = 279308, upload-time = "2026-07-10T19:48:38.907Z" }, + { url = "https://files.pythonhosted.org/packages/6a/4a/a7fa3ada9bd2d2ce20d56dfceec6b2a51afeed9bf3d8286355ceec5f0628/regex-2026.7.10-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:39f81d1fdf594446495f2f4edd8e62d8eda0f7a802c77ac596dc8448ad4cc5ca", size = 497087, upload-time = "2026-07-10T19:48:40.543Z" }, + { url = "https://files.pythonhosted.org/packages/0f/7e/ca0b1a87192e5828dbc16f16ae6caca9b67f25bf729a3348468a5ff52755/regex-2026.7.10-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:441edc66a54063f8269d1494fc8474d06605e71e8a918f4bcfd079ebda4ce042", size = 297307, upload-time = "2026-07-10T19:48:42.213Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/fb40bb34275d3cd4d7a376d5fb2ea1f0f4a96fd884fa83c0c4ae869001bf/regex-2026.7.10-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cfeb11990f59e59a0df26c648f0adfcbf27be77241250636f5769eb08db662be", size = 292163, upload-time = "2026-07-10T19:48:43.929Z" }, + { url = "https://files.pythonhosted.org/packages/18/0b/34cbea16c8fea9a18475a7e8f5837c70af451e738bfeb4eb5b029b7dc07a/regex-2026.7.10-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:460176b2db044a292baaee6891106566739657877af89a251cded228689015a6", size = 797064, upload-time = "2026-07-10T19:48:45.623Z" }, + { url = "https://files.pythonhosted.org/packages/87/77/f6805d97f15f5a710bdfd56a768f3468c978239daf9e1b15efd8935e1967/regex-2026.7.10-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9dc55698737aca028848bde418d6c51d74f2a5fd44872d3c8b56b626729adb89", size = 866155, upload-time = "2026-07-10T19:48:47.589Z" }, + { url = "https://files.pythonhosted.org/packages/a2/e3/a2a905807bba3bcd90d6ebbb67d27af2adf7d41708175cbc6b956a0c75f1/regex-2026.7.10-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d3e10779f60c000213a5b53f518824bd07b3dc119333b26d70c6be1c27b5c794", size = 911596, upload-time = "2026-07-10T19:48:49.473Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ca/a3126888b2c6f33c7e29144fedf85f6d5a52a400024fa045ad8fc0550ef1/regex-2026.7.10-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:38a5926601aaccf379512746b86eb0ac1d29121f6c776dac6ac5b31077432f2c", size = 800713, upload-time = "2026-07-10T19:48:51.452Z" }, + { url = "https://files.pythonhosted.org/packages/66/19/9d252fd969f726c8b56b4bacf910811cc70495a110907b3a7ccb96cd9cad/regex-2026.7.10-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a72ecf5bfd3fc8d57927f7e3ded2487e144472f39010c3acaec3f6f3ff53f361", size = 777286, upload-time = "2026-07-10T19:48:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/40/7a/5f1bf433fa446ecb3aab87bb402603dc9e171ef8052c1bb8690bb4e255a3/regex-2026.7.10-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d50714405845c1010c871098558cfe5718fe39d2a2fab5f95c8863caeb7a82b3", size = 785826, upload-time = "2026-07-10T19:48:55.381Z" }, + { url = "https://files.pythonhosted.org/packages/99/ca/69f3a7281d86f1b592338007f3e535cc219d771448e2b61c0b56e4f9d05b/regex-2026.7.10-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ec1c44cf9bd22079aac37a07cb49a29ced9050ab5bddf24e50aba298f1e34d90", size = 860957, upload-time = "2026-07-10T19:48:57.962Z" }, + { url = "https://files.pythonhosted.org/packages/bc/11/487ff55c8d515ec9dd60d7ba3c129eeaa9e527358ed9e8a054a9e9430f81/regex-2026.7.10-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:9e9aaef25a40d1f1e1bbb1d0eb0190c4a64a7a1750f7eb67b8399bed6f4fd2a6", size = 765959, upload-time = "2026-07-10T19:49:00.27Z" }, + { url = "https://files.pythonhosted.org/packages/73/e1/fa034e6fa8896a09bd0d5e19c81fdc024411ab37980950a0401dccee8f6d/regex-2026.7.10-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:e54e088dc64dd2766014e7cfe5f8bc45399400fd486816e494f93e3f0f55da06", size = 851447, upload-time = "2026-07-10T19:49:02.128Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a5/b9427ed53b0e14c540dc436d56aaf57a19fb9183c6e7abd66f4b4368fbad/regex-2026.7.10-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:834271b1ff2cfa1f67fcd65a48bf11d11e9ab837e21bf79ce554efb648599ae8", size = 789418, upload-time = "2026-07-10T19:49:03.949Z" }, + { url = "https://files.pythonhosted.org/packages/ba/52/aab92420c8aa845c7bcbe68dc65023d4a9e9ea785abf0beb2198f0de5ba1/regex-2026.7.10-cp314-cp314-win32.whl", hash = "sha256:f988a1cec68058f71a38471813fba9e87dffe855582682e8a10e40ece12567a2", size = 272538, upload-time = "2026-07-10T19:49:05.833Z" }, + { url = "https://files.pythonhosted.org/packages/99/16/5c7050e0ef7dd8889441924ff0a2c33b7f0587c0ccb0953fe7ca997d673b/regex-2026.7.10-cp314-cp314-win_amd64.whl", hash = "sha256:2129e4a5e86f26926982d883dff815056f2e98220fdf630e59f961b578a26c43", size = 280796, upload-time = "2026-07-10T19:49:07.593Z" }, + { url = "https://files.pythonhosted.org/packages/e8/1a/4f6099d2ba271502fdb97e697bae2ed0213c0d87f2273fe7d21e2e401d12/regex-2026.7.10-cp314-cp314-win_arm64.whl", hash = "sha256:9cd5b6805396157b4cf993a6940cbb8663161f29b4df2458c1c9991f099299c5", size = 281017, upload-time = "2026-07-10T19:49:09.767Z" }, + { url = "https://files.pythonhosted.org/packages/19/02/4061fc71f64703e0df61e782c2894c3fbc089d277767eff6e16099581c73/regex-2026.7.10-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:103e8f3acc3dcede88c0331c8612766bdcfc47c9250c5477f0e10e0550b9da49", size = 501467, upload-time = "2026-07-10T19:49:11.952Z" }, + { url = "https://files.pythonhosted.org/packages/73/a5/8d42b2f3fd672908a05582effd0f88438bf9bb4e8e02d69a62c723e23601/regex-2026.7.10-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:538ddb143f5ca085e372def17ef3ed9d74b50ad7fc431bd85dc50a9af1a7076f", size = 299700, upload-time = "2026-07-10T19:49:14.067Z" }, + { url = "https://files.pythonhosted.org/packages/65/70/36fa4b46f73d268c0dbe77c40e62da2cd4833ee206d3b2e438c2034e1f36/regex-2026.7.10-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e3448e86b05ce87d4eb50f9c680860830f3b32493660b39f43957d6263e2eba", size = 294590, upload-time = "2026-07-10T19:49:15.883Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a7/b6db1823f3a233c2a46f854fdc986f4fd424a84ed557b7751f2998efb266/regex-2026.7.10-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5eab9d3f981c423afd1a61db055cfe83553c3f6455949e334db04722469dd0a2", size = 811925, upload-time = "2026-07-10T19:49:17.97Z" }, + { url = "https://files.pythonhosted.org/packages/b6/7d/f8bee4c210c42c7e8b952bb9fb7099dd7fb2f4bd0f33d0d65a8ab08aafc0/regex-2026.7.10-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:177f930af3ad72e1045f8877540e0c43a38f7d328cf05f31963d0bd5f7ecf067", size = 871257, upload-time = "2026-07-10T19:49:19.943Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/22adf72e614ba0216b996e9aaef5712c23699e360ea127bb3d5ee1a7666f/regex-2026.7.10-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dd3b6d97beb39afb412f2c79522b9e099463c31f4c49ab8347c5a2ca3531c478", size = 917551, upload-time = "2026-07-10T19:49:22.069Z" }, + { url = "https://files.pythonhosted.org/packages/03/f7/ebc15a39e81e6b58da5f913b91fc293a25c6700d353c14d5cd25fc85712a/regex-2026.7.10-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8679f0652a183d93da646fcec8da8228db0be40d1595da37e6d74c2dc8c4713c", size = 816436, upload-time = "2026-07-10T19:49:24.131Z" }, + { url = "https://files.pythonhosted.org/packages/5c/33/20bc2bdd57f7e0fcc51be37e4c4d1bca7f0b4af8dc0a148c23220e689da8/regex-2026.7.10-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:494b19a5805438aeb582de99f9d97603d8fd48e6f4cc74d0088bb292b4da3b70", size = 785935, upload-time = "2026-07-10T19:49:26.265Z" }, + { url = "https://files.pythonhosted.org/packages/b4/51/87ff99c849b56309c40214a72b54b0eef320d0516a8a516970cc8be1b725/regex-2026.7.10-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0911e34151a5429d0325dae538ba9851ec0b62426bdfd613060cda8f1c36ec7f", size = 801494, upload-time = "2026-07-10T19:49:28.493Z" }, + { url = "https://files.pythonhosted.org/packages/16/11/fde67d49083fef489b7e0f841e2e5736516795b166c9867f05956c1e494b/regex-2026.7.10-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b862572b7a5f5ed47d2ba5921e63bf8d9e3b682f859d8f11e0e5ca46f7e82173", size = 866549, upload-time = "2026-07-10T19:49:30.592Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b5/31a156c36acf10181d88f55a66c688d5454a344e53ccc03d49f4a48a2297/regex-2026.7.10-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:3f361215e000d68a4aff375106637b83c80be36091d83ee5107ad3b32bd73f48", size = 773089, upload-time = "2026-07-10T19:49:32.661Z" }, + { url = "https://files.pythonhosted.org/packages/27/bb/734e978c904726664df47ae36ce5eca5065de5141185ae46efec063476a2/regex-2026.7.10-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:4533af6099543db32ef26abc2b2f824781d4eebb309ab9296150fd1a0c7eb07d", size = 856710, upload-time = "2026-07-10T19:49:35.289Z" }, + { url = "https://files.pythonhosted.org/packages/6d/e5/dc35cea074dbdcb9776c4b0542a3bc326ff08454af0768ef35f3fc66e7fa/regex-2026.7.10-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:668ab85105361d0200e3545bec198a1acfc6b0aeb5fff8897647a826e5a171be", size = 803621, upload-time = "2026-07-10T19:49:37.704Z" }, + { url = "https://files.pythonhosted.org/packages/a6/b2/124564af46bc0b592785610b3985315610af0a07f4cf21fa36e06c2398dd/regex-2026.7.10-cp314-cp314t-win32.whl", hash = "sha256:dd7715817a187edd7e2a2390908757f7ba42148e59cad755fb8ee1160c628eca", size = 274558, upload-time = "2026-07-10T19:49:39.926Z" }, + { url = "https://files.pythonhosted.org/packages/4e/9c/cd813ce9f3404c0443915175c1e339c5afd8fcda04310102eaf233015eef/regex-2026.7.10-cp314-cp314t-win_amd64.whl", hash = "sha256:78712d4954234df5ca24fdadb65a2ab034213f0cdfde376c272f9fc5e09866bb", size = 283687, upload-time = "2026-07-10T19:49:41.872Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d3/3dae6a6ce46144940e64425e32b8573a393a009aeaf75fa6752a35399056/regex-2026.7.10-cp314-cp314t-win_arm64.whl", hash = "sha256:749b92640e1970e881fdf22a411d74bf9d049b154f4ef7232eeb9a90dd8be7f3", size = 283377, upload-time = "2026-07-10T19:49:43.985Z" }, ] [[package]] @@ -5026,16 +4190,16 @@ wheels = [ [[package]] name = "responses" -version = "0.26.1" +version = "0.26.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, { name = "requests" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/58/1fb6de3503428196df78638f991ec8095274f1ee9723e272ee4d9ff0092b/responses-0.26.1.tar.gz", hash = "sha256:2eb3218553cc8f79b57d257bac23af5e1bf381f5b9390b1767816f0843e01dc2", size = 83088, upload-time = "2026-05-21T19:56:39.747Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/1a/4af3e6d659394b809838490b144e4ab8d7ed3b9fecc7ca78f5d2f79b1a3d/responses-0.26.2.tar.gz", hash = "sha256:9c9259b46a8349197edebf43cfa68a87e1a2802ef503ff8b2fecbabc0b45afd8", size = 84030, upload-time = "2026-07-03T16:44:50.325Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/31/6a620b4427d546b9e7cca8b3b8c5f0559d9cef2bb9eedcda7f73c1473c19/responses-0.26.1-py3-none-any.whl", hash = "sha256:8aacc4586eb08fb2208ef64a9eb4258d9b0c6e6f4260845f2f018ab847495345", size = 35502, upload-time = "2026-05-21T19:56:38.046Z" }, + { url = "https://files.pythonhosted.org/packages/7c/28/693e1d9ebf72baa062ded80d837a035b86ce75eda5a269379e9e2b1008a8/responses-0.26.2-py3-none-any.whl", hash = "sha256:6fdfeabd58e5ec473b98dfe02e6d46d3173bd8dd573eff2ccccf1a05a5135364", size = 35609, upload-time = "2026-07-03T16:44:49.1Z" }, ] [[package]] @@ -5199,169 +4363,155 @@ wheels = [ [[package]] name = "rpds-py" -version = "2026.5.1" +version = "2026.6.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14'", "python_full_version == '3.13.*'", "python_full_version >= '3.11' and python_full_version < '3.13'", ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/43/25a8dcd3feedd735039a8f0b5b7e3b118232b5eae288c4fd9ab200d41094/rpds_py-2026.5.1.tar.gz", hash = "sha256:07b24fea40541e28570e5b795a4a38fbdcd12550c06bd0748005ecc8116ca256", size = 64459, upload-time = "2026-05-28T12:02:13.232Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/a0/acf8b6fc20bfdcd3a45bd3f57680fb198e157b7e997b9123b10763798bd2/rpds_py-2026.5.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3397a5ed7174dc2786bb214030232fc36fe8e5584fec43a9952cc542b1a12036", size = 355609, upload-time = "2026-05-28T11:58:50.78Z" }, - { url = "https://files.pythonhosted.org/packages/b6/95/f8203fd997484b1690a6869cd0e503b6c3c6be55b0ecc36d1a491fe742f0/rpds_py-2026.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:99ab6ba7bfa2cb0f96a04e3652355bf04e3f51aceb1e943b8541dab7ba4828cc", size = 348460, upload-time = "2026-05-28T11:58:52.374Z" }, - { url = "https://files.pythonhosted.org/packages/33/8c/b47326ad2f0be545a5e5c1a55937a12afaea7d392ba2837bb9680f57e6c9/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0efbe45632665e53e3db8fe1e5692db58fc5cb9bab4459d570b83efefe11164", size = 381031, upload-time = "2026-05-28T11:58:53.775Z" }, - { url = "https://files.pythonhosted.org/packages/22/0b/e83bbd97ffac6f6389b605cd4e1c8ac5761dc7e977769c9255d8c5adb7bd/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:01d17b29c0c23d82b1f4751147ec49cf451f1fc2554eb9ef5f957e55d2656ead", size = 387121, upload-time = "2026-05-28T11:58:55.243Z" }, - { url = "https://files.pythonhosted.org/packages/fd/0e/d285d1bc8864245919c61e1ca82263e4a66d337759c3a4cef72766ff9afc/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7559f72b94ae52659086c595dfa017cde03155f7832071d30959049052cb3ece", size = 501026, upload-time = "2026-05-28T11:58:56.788Z" }, - { url = "https://files.pythonhosted.org/packages/86/06/ccb2109a1e543437b5e43816f2b43b9554cc6783145528a4e3711e05c011/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e25b7088f9ccbfc0dfcaa52bf969300ca229e10ecf758974ebcbb080a4b37bb", size = 391865, upload-time = "2026-05-28T11:58:58.298Z" }, - { url = "https://files.pythonhosted.org/packages/3d/33/237173db1cfef10105b3839a24de00eb8d2a523711add4632447cdf0aedd/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613fc4ee9eaef26dc5840666214dd6fbcebcf32f46e76f4abc473059f4e13dda", size = 378012, upload-time = "2026-05-28T11:58:59.589Z" }, - { url = "https://files.pythonhosted.org/packages/97/64/1eae54e34d5161f9969295e80bd6b62a55f2b6ac5f2a5b60d02c2140e758/rpds_py-2026.5.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:85264a90ff4c05c1568dd65f5921c837614b67c60358fb4c17df3b7f2e90690a", size = 391111, upload-time = "2026-05-28T11:59:01.104Z" }, - { url = "https://files.pythonhosted.org/packages/d8/34/5bb334a5a0f65d77869217c4654f34c78a7d11b93938a3c076a2edeafc52/rpds_py-2026.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe71bca7d547acb17027c7fd1624ff8aae623499c498d3e7011182c4de5c25e0", size = 409225, upload-time = "2026-05-28T11:59:02.433Z" }, - { url = "https://files.pythonhosted.org/packages/16/0f/007ec21283b5b040b4ec3bd95e0402591e22bfa7d5c93dfe01c465c2d2d7/rpds_py-2026.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05fa4f41f37ec97c9c260441a940450a192f78d774d2b097eee1379f1e1246a", size = 556487, upload-time = "2026-05-28T11:59:04.012Z" }, - { url = "https://files.pythonhosted.org/packages/ff/10/5437c94508169b6b22d8418fef7a66e9ffb5f3b9e9c94460f2eedafe06ff/rpds_py-2026.5.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df1d2a1996755b24b9ecee92cb4d36c28f86f464a6a173349c26bab41e94b8c2", size = 620798, upload-time = "2026-05-28T11:59:05.485Z" }, - { url = "https://files.pythonhosted.org/packages/e0/d5/9937dce4d6bda74157b954e7d1460db05a22f5929dccfeeba1ed27a93df0/rpds_py-2026.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8895840ac4809e5f60c88fd07617cd71326e73d6e5a8aa783c5c0f7c24985de2", size = 584053, upload-time = "2026-05-28T11:59:06.837Z" }, - { url = "https://files.pythonhosted.org/packages/6c/31/750617dd0ae1752471bf43f9e41d263398fae7cde7849d23b8574a70e617/rpds_py-2026.5.1-cp311-cp311-win32.whl", hash = "sha256:3684a59b158a7683aaeb8e25352e9a9dd2122cec78f2d8530266e4f91b4c7b3f", size = 214390, upload-time = "2026-05-28T11:59:08.402Z" }, - { url = "https://files.pythonhosted.org/packages/3c/bb/3dcab0e1d9516303f2eb672a5d6f62eca5a69e2886301e9c8c54b520c39b/rpds_py-2026.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:7bd530e6a530bb3ea892f194fafa455f3516ac25ecf7143fd33c09be62b0470a", size = 231097, upload-time = "2026-05-28T11:59:09.786Z" }, - { url = "https://files.pythonhosted.org/packages/49/d6/c6bbf5cb1cf12b9732df8074b57f6ef8341ba884c95d40632ae8bddb44e4/rpds_py-2026.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:0a5ae4dbe43c1076983b72616496919872ae7bbe7a1e21cc48336bc3154d130b", size = 226361, upload-time = "2026-05-28T11:59:11.079Z" }, - { url = "https://files.pythonhosted.org/packages/d4/e7/a78582dc57caa592dcc7d4fb69b61390561e908eb3d2f5df5928a8e354c0/rpds_py-2026.5.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3abe24a66e57adcfa645d718063a5fa5103ecc71ddbf26d78af8f9368018ff1d", size = 353040, upload-time = "2026-05-28T11:59:12.531Z" }, - { url = "https://files.pythonhosted.org/packages/a3/43/35e3f136343aef451e545ce8c38d36c2f93c0ed88703db8b64ba2b205c68/rpds_py-2026.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b1d94308ddf0b1982f61f2eb54bf92997c9ece8a8093ef014250f4a517906c", size = 345775, upload-time = "2026-05-28T11:59:13.827Z" }, - { url = "https://files.pythonhosted.org/packages/20/e1/0f2160c5982d3157734d5cb3ed63d8b2d583a73c9864f77b666449f32cf8/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fa92420128dadce7f54bd73ba1825a273e9268fe9e35dbf7e6362890efa4e08", size = 376329, upload-time = "2026-05-28T11:59:15.271Z" }, - { url = "https://files.pythonhosted.org/packages/d0/11/ee0ba42aff83bf4effdbc576673c6be64c5e173978c3f6d537e94482f77d/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ca653c6546386227cd9800d1bef6a348099acf8db4250341da6d90f663d6dfcb", size = 383539, upload-time = "2026-05-28T11:59:16.665Z" }, - { url = "https://files.pythonhosted.org/packages/11/df/d94aa6a499d4ac40afe2d7620f2c597fd3c0f182e854ad7cf3f596a81cb6/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66c93681c4729e4e3ecba31b8179fae083ff3118841672835140338b4b9867c1", size = 494674, upload-time = "2026-05-28T11:59:17.991Z" }, - { url = "https://files.pythonhosted.org/packages/1f/75/33d30f43bb2f458de11979486a591b1bf6e5651765ed1704c6197c2dc773/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40ff257542e04796880e011e15cd4dc21c2599975df2aaa8f2c8495ca574e1a5", size = 389268, upload-time = "2026-05-28T11:59:19.434Z" }, - { url = "https://files.pythonhosted.org/packages/f4/1e/2c9096fc19d5fd084b0184ca2b651e659aa0a37e6fdbecf6ece47f147fe1/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6825cc329b290e93c5f6a9be2393118a763f6ccf6abd83704e0c102ca583644", size = 376280, upload-time = "2026-05-28T11:59:21Z" }, - { url = "https://files.pythonhosted.org/packages/b9/e5/61ec9f8be8211ea7f48448195549e4aaf02004083475493b0e137702ecb2/rpds_py-2026.5.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:de42116e69cb53b911cc34aee5ab98f36c597b822545045d49e938818b99e5e4", size = 387233, upload-time = "2026-05-28T11:59:22.454Z" }, - { url = "https://files.pythonhosted.org/packages/0d/ca/bcec1005c4f4a234f92a29078631fee49206c7265ccae966f18fd332e80e/rpds_py-2026.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0f920015df2a504bebaba6d4c31ccf3fcf942f92655c086da30b671aad19aa6", size = 405009, upload-time = "2026-05-28T11:59:23.845Z" }, - { url = "https://files.pythonhosted.org/packages/72/e6/4d5718c5cf26c522dc7c9999e238da1e77380b81d0c5d1df11e271ddfeb1/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0408a24e44feb919423dc6d9da677cb5cddb894d2ca9e763967d156d9c60fab4", size = 553113, upload-time = "2026-05-28T11:59:25.184Z" }, - { url = "https://files.pythonhosted.org/packages/d4/25/2ee807bdb3e1f0b7eddf7782acd5665a8b5205a331a7d7244a52c4812fd9/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cea68bcd53467561ae2f96a6bdad1544299ba97b5b0ddcd5ac3d376e5c781c24", size = 618838, upload-time = "2026-05-28T11:59:26.749Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c1/7d4c26f167f8c41501cc073d30ee22082b16ce358cf5b00ec97cbc7804ea/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4be8b1d2a705cc37d08256004e1d07de143fa0075c8e85a3df020b776f62b732", size = 582436, upload-time = "2026-05-28T11:59:28.11Z" }, - { url = "https://files.pythonhosted.org/packages/04/1d/9d12b0a337bab46f4769f8857f4007e3b2d639e14f9a44a0efe157696e64/rpds_py-2026.5.1-cp312-cp312-win32.whl", hash = "sha256:6736718bd4fc49cbcb538ba30516fdbef161522acefb739657d48b97bd864fed", size = 212734, upload-time = "2026-05-28T11:59:29.689Z" }, - { url = "https://files.pythonhosted.org/packages/c5/93/e4116f2de7f56bc7406a76033dc501811ddeb22b7f056b92d632871ebb0c/rpds_py-2026.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:0a7d1eec967df0e9b22614a5e177622e0c89611d03727fa0cb48e45028907870", size = 229045, upload-time = "2026-05-28T11:59:31.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/53/6c3419d85eb2ec5938a37627c585b42d76a63bb731d6e42ed4b079ebf486/rpds_py-2026.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:1841d067089e117142d79b98aa0df2f08b52f2ecc1819dd2700636c0db74a473", size = 223967, upload-time = "2026-05-28T11:59:32.318Z" }, - { url = "https://files.pythonhosted.org/packages/6c/32/14c961ad295f490eb0849ada8b79683e93a59b9de3afdd983eaf55fa6867/rpds_py-2026.5.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:efef4ac29c6ff495531eb17ee705b62841ecaa291b7c7077e848ea03e237164d", size = 352787, upload-time = "2026-05-28T11:59:33.655Z" }, - { url = "https://files.pythonhosted.org/packages/ca/bb/d1b85117967c11191441a7274ae616c65d93901d082c588f89a50a8da5ae/rpds_py-2026.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c39f5b67a8a2e67179ada2a954227d670fe65fa9098457f698f56ddf248709b3", size = 345179, upload-time = "2026-05-28T11:59:35Z" }, - { url = "https://files.pythonhosted.org/packages/7c/46/d84105f062e626a1b233f863907288a4708c2d833b8b4c6fb2764bc080c0/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5c30f3f04eef4fbd362226a6f31d7c8895ca4fbb6e0b790f6890a98d8da8559", size = 376173, upload-time = "2026-05-28T11:59:36.43Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ae/469d7959ce5b1201e1de135dc735b86db3b35dd0d1734f6a44246d5f061c/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:277f6c82f0580848796c7ecc8a7173aa3bfb928e4ff831261c2f60a81dc270db", size = 383162, upload-time = "2026-05-28T11:59:37.995Z" }, - { url = "https://files.pythonhosted.org/packages/dc/a2/57853d31a1116a561aa072794602ad3f6341e18d70a8523f1bd5b9fc1e5a/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63c2c4c213f1a4e3f3de28ecab029dbdee976324e729c0d7a55211be72576b02", size = 495093, upload-time = "2026-05-28T11:59:39.453Z" }, - { url = "https://files.pythonhosted.org/packages/99/63/3a8eabcad9314b7daf5c65f451d2c33d989235cd8a5762186cf2c3f5a4f8/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3350ec808fb538fe71a1f94dfaa0e29c598dfad805ce49f0caec5ae3183c652b", size = 389829, upload-time = "2026-05-28T11:59:40.896Z" }, - { url = "https://files.pythonhosted.org/packages/4b/25/05678d97fc25e2622df14dc530fb82023174ecfff6733991ed0d78f167bd/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1b964e3ab599e718dc46c018d104b1ebc007cbc6567d827c94a687fca56d77e", size = 374786, upload-time = "2026-05-28T11:59:42.626Z" }, - { url = "https://files.pythonhosted.org/packages/88/d1/8c90b6431e80a3b91b284a5c7c8c0c4f9c006444d90477a740d6e0f9c694/rpds_py-2026.5.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:19cb09fab7b7fc96b2a6e28f2e34b72a3705ff27b37edb77455316e5d3f3dc9b", size = 386920, upload-time = "2026-05-28T11:59:44.124Z" }, - { url = "https://files.pythonhosted.org/packages/ff/99/4638f672ab356682d633ee0da9255f5b67ce6efd0b85eb94ad3e255e65a5/rpds_py-2026.5.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abe76bcdba31e576cb83eeb8797aa0d882b738fef6dc65d0601fc753806a5b46", size = 405059, upload-time = "2026-05-28T11:59:47.177Z" }, - { url = "https://files.pythonhosted.org/packages/66/3f/3546524b6eb4cc2e1f363a3d638fa52f6c24faae3500c25fb488b02f1740/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8bff7073db3899158fff55ebf57b113a67030af26f80a18978f9f0aa60250ddf", size = 553030, upload-time = "2026-05-28T11:59:48.603Z" }, - { url = "https://files.pythonhosted.org/packages/c6/c3/7b3388c796fcf471bd17194242d4dc1a7608567c0fa422bcc1c5e79f9c1e/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8ba264fa49be666cd9cc56bf34ec7002fb3d27a4aee5bcb4d43d0d18feb1bb6f", size = 618975, upload-time = "2026-05-28T11:59:50.314Z" }, - { url = "https://files.pythonhosted.org/packages/61/1e/a3cb07f2795075d1d88efddae2f541359fde5f08c81ee114c29c2949c90a/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4860b603ddda0475a8885499b3729e90229d480105b42651962a5397d995fa89", size = 581178, upload-time = "2026-05-28T11:59:51.673Z" }, - { url = "https://files.pythonhosted.org/packages/a1/74/e758c03a5ef46f04c37f2651a2893db846d569ba8a7bca469d4b58939bcd/rpds_py-2026.5.1-cp313-cp313-win32.whl", hash = "sha256:7944270ae71383f6e2657dd7d5ce4eeb4ac2d0059a6738f0510583d462ab4842", size = 212481, upload-time = "2026-05-28T11:59:53.148Z" }, - { url = "https://files.pythonhosted.org/packages/70/ec/a2aca432db9c7359b40fa393eeeaa0d166c2f70175be956e75fa24197c44/rpds_py-2026.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:88647f43a73c4e01be19b04ceef0c8d3a1958153604d13c773becd8016f2a0cf", size = 228519, upload-time = "2026-05-28T11:59:54.505Z" }, - { url = "https://files.pythonhosted.org/packages/29/60/a73bfdd45b096574556acf303bbd9fa9eed36ca8a818b514e2a5d5fe2b9d/rpds_py-2026.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:453895624ecf7db7063b1004e44037522bbaef9ff6a945e59bc71662d7a03abd", size = 223446, upload-time = "2026-05-28T11:59:56.081Z" }, - { url = "https://files.pythonhosted.org/packages/18/e2/408105fd611823f00882aea810f3989a30d26b1bab8b6beb20f98c724e0e/rpds_py-2026.5.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:b4e4bc98639ec915f512fde3aa7a95e0041d95d9c3cc86eea841fa63cb1e8600", size = 355287, upload-time = "2026-05-28T11:59:57.448Z" }, - { url = "https://files.pythonhosted.org/packages/8d/58/5c4a43436843c90d0f6d19f82c200c80e3843ca9fa07b237623327f6d384/rpds_py-2026.5.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cacedb7a6e167680acba45ad5716e89067d225dc80da0d7040cae8c81d4572fa", size = 347033, upload-time = "2026-05-28T11:59:58.881Z" }, - { url = "https://files.pythonhosted.org/packages/fb/c2/1a71acdacaf4e259b10278fb87b039ded3cf80041bcd89dd8a3ea702ded6/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68700371c5d7ae1412862ddfa719090925c93ecf351c566d66f09d04b136ea00", size = 376891, upload-time = "2026-05-28T12:00:00.516Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c8/535f3d9b65addd8e28aa87b83c6e526799c3717a88273db8ea795beeef7a/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:296c799becfa849c779c8725494fe9ed94959ed886787df4364b058465bad7f0", size = 385646, upload-time = "2026-05-28T12:00:02.394Z" }, - { url = "https://files.pythonhosted.org/packages/1c/91/dc033f313345c354ade914dbe73cdb90b615a4409ea02430d5356794f3d8/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3858b908218ee108d0bbfb2095ccc237648053c9bf98affad7cb079acaf1d97", size = 498830, upload-time = "2026-05-28T12:00:04.189Z" }, - { url = "https://files.pythonhosted.org/packages/27/fc/90fcbea459dbb8ddc18a2e0fd1de9412b48bc84ffff2db771cf714bacfd6/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4fb8d2e7cb2f850b169806d61d1b991738acec96500a75c30f49caf064ce7cef", size = 392830, upload-time = "2026-05-28T12:00:05.797Z" }, - { url = "https://files.pythonhosted.org/packages/b2/1d/46cd11a228c9750684a798d98f878be6f614aa762438da7378f035e79e35/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27b74c10ed6a8f190f4287f53bcfea348b92a84a9c9f70d30183d1e6172d580d", size = 379613, upload-time = "2026-05-28T12:00:07.433Z" }, - { url = "https://files.pythonhosted.org/packages/24/4a/d9b0c6af3a1de03eb93741bbe8be2bdce84d8fda8224f3005451d86df389/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b9a6528956191c48c52294a592dbd4a8386d7048bdb25c0efcb6b966466c6d83", size = 388183, upload-time = "2026-05-28T12:00:09.227Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b4/db7aaabdda6d020afc87d981bcc2f57a434c7dec60ecfc2ab3dd50b20351/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:af03e34e860047bc7a352b842856fcf78798fbb81132cc98bd2f907ab4eb9cd2", size = 408578, upload-time = "2026-05-28T12:00:10.779Z" }, - { url = "https://files.pythonhosted.org/packages/08/d6/070f6a41cbb343e2ac4171859bf3f3623e0ab002f72619d6d505313ec2de/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fea6e836d10abbe191d557d33bd58bd5987725fe63aa1eefe557d230209855bd", size = 553573, upload-time = "2026-05-28T12:00:12.443Z" }, - { url = "https://files.pythonhosted.org/packages/75/ab/1a71ea3589c4345dac0a0518f0e6a031cb42689277851b683c46d27463a5/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:fc0c0f878ea770a0a8a462456c5ad36fc9fe6358e6b76fdadc7f17575e0b8bf1", size = 620861, upload-time = "2026-05-28T12:00:14.09Z" }, - { url = "https://files.pythonhosted.org/packages/8a/22/9bf80a56069c0c443fcfefac639a86a744550a2898817a6dfd3e26654924/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e0b360f316d966b048b085857630b3cc51f3db2f07b06f440eac8f695374d1e3", size = 585633, upload-time = "2026-05-28T12:00:15.66Z" }, - { url = "https://files.pythonhosted.org/packages/da/68/3b2c0a75c9e04125696f84ebdbbf304acf5a40b58ba4481cdb98a922c3ba/rpds_py-2026.5.1-cp313-cp313t-win32.whl", hash = "sha256:a2999883eedf72fdfb7520b92c7d4ec2572a71ff40239377aa604cc529eecafc", size = 210074, upload-time = "2026-05-28T12:00:17.291Z" }, - { url = "https://files.pythonhosted.org/packages/e7/8b/609157d5a25d37d4f29f92840ba531f416907c34ae5c5739dd21fc2bef98/rpds_py-2026.5.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e07be2a9d7122bd6e82dea89814ef8dc893feb1aae97fec1630f3263bbb30e55", size = 228635, upload-time = "2026-05-28T12:00:18.73Z" }, - { url = "https://files.pythonhosted.org/packages/d4/6f/19c1918a4b590d8de87e712e4abe4b3875771eff60216fb6153cf6665c68/rpds_py-2026.5.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1f2c391c3059798093b65df23aca2cac150460ae9c630d99dec83d703d9485b9", size = 349756, upload-time = "2026-05-28T12:00:20.217Z" }, - { url = "https://files.pythonhosted.org/packages/e5/60/a06fe7da34eca79dacbf958a2ba0c6eea85bc2b29de20080bf40f72f66fa/rpds_py-2026.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:413b424f7c4ee65ab5e5be91f5731be0f8b41a1ee2b12dfe810d716312e95a78", size = 343831, upload-time = "2026-05-28T12:00:21.711Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ec/b2333b97b90e2a6ef6ca8ad386ee284968e74bcfe113b3f1a8d9036429a9/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c595a1d9255dce0599e13130d1440ab2506654f2b50294226ee06402f8fef63", size = 375127, upload-time = "2026-05-28T12:00:23.326Z" }, - { url = "https://files.pythonhosted.org/packages/14/7f/e00aae54067f2b488c4637961d5f58204d470795fc791085fa3f15060d2e/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c27c5f6102eac8c03e7595a00827a53b271ba40a53b59ff8709170e0855ea4a", size = 379034, upload-time = "2026-05-28T12:00:24.89Z" }, - { url = "https://files.pythonhosted.org/packages/be/cc/423999bbb8ae8dc93c77fc1d5e984ade5eb89d237d3bb884ccfa72ae2890/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c7fcf61d44cacecaf3aea542b0e053db77972a4573e7ceda16fb2b399161195", size = 490823, upload-time = "2026-05-28T12:00:26.676Z" }, - { url = "https://files.pythonhosted.org/packages/0f/aa/c671bf660f12e68d3c52ff86c7066ed1372df5a0f4f2ff584e419b8207e7/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c817a189d4ee14290420e5ff051e4dd6baa13f3edf84685071dee07a6d538ee", size = 388144, upload-time = "2026-05-28T12:00:28.577Z" }, - { url = "https://files.pythonhosted.org/packages/19/c8/d63bb75b68afe77b229e3021c6031bcaf01da5db5b0e69d0d10f9ba679a7/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21846aac0ed2e0589f38c12dc44e77bb64e494b771eadbcf169cba00566ba7ba", size = 371959, upload-time = "2026-05-28T12:00:30.304Z" }, - { url = "https://files.pythonhosted.org/packages/82/35/c51122014d8274ff37dc606d60049c3db7d83da02b5b282511e5a906a9a6/rpds_py-2026.5.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b317c87a13f769a4e787819bd508aaa5d69aa09b0880de9af6d3a8a54571cdec", size = 383558, upload-time = "2026-05-28T12:00:31.764Z" }, - { url = "https://files.pythonhosted.org/packages/e3/f9/2790cb99c136a5363acdeacf5c27c56f3de0d4118a1f48fca83404c99c89/rpds_py-2026.5.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce87129d9f2c14fa6c4a8601fb80eb4488c80d38a20cd13758ef11123e14995d", size = 402789, upload-time = "2026-05-28T12:00:33.247Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1b/e4fb584f8c75d35c38150ff6a332cda949e6f97acba1f4fd123b14ab56fe/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9cdddb6c1207d284d94fd1530adf57fbd797fe7c4b8704ba85f49414f2557e7d", size = 551405, upload-time = "2026-05-28T12:00:34.819Z" }, - { url = "https://files.pythonhosted.org/packages/d8/f7/a6731b4216cb3793ea1af5391da240f5683dacc0d13e034fe5fc3503f240/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:4e237e139f94d3c036fd28eb9f564c99055476ff4ff05cd42be55ce349b5aa02", size = 616975, upload-time = "2026-05-28T12:00:36.268Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/2e051a81d95d8e63f4b35a1c463a87e8766bc3d083c067c5dfb6bf220747/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ed0954b524873214369184a9c82b0eaa45a3fbb9a798cd95b17e0d98499e7ea0", size = 578701, upload-time = "2026-05-28T12:00:37.82Z" }, - { url = "https://files.pythonhosted.org/packages/65/56/b5f6fdb2083e32bca8a8993d89e70db114b4756c9e2c38421328126689d2/rpds_py-2026.5.1-cp314-cp314-win32.whl", hash = "sha256:2d88621d6a7d4dfa633d21abe90f280bb205274e16b1d1e61c6ad4640b2453b7", size = 209806, upload-time = "2026-05-28T12:00:39.492Z" }, - { url = "https://files.pythonhosted.org/packages/fb/80/65a5aa96c155e611d1ed844e4e1f57f3e36b021f396d9f8585d756e6b90d/rpds_py-2026.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:cef8ac28d26f4dda3533060c20fbf80a325458fa9fd23ea72a73cdfa8e978838", size = 225985, upload-time = "2026-05-28T12:00:40.94Z" }, - { url = "https://files.pythonhosted.org/packages/27/7c/ad185212e87b05f196daef92bc5f3caf07298eb47c295b5585c3dd3093ac/rpds_py-2026.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:eaaea962c68cdc68d4a533ba985ab8e9484277910bbfaa2ab3ef7732667bfed8", size = 221219, upload-time = "2026-05-28T12:00:43.15Z" }, - { url = "https://files.pythonhosted.org/packages/23/58/e14ae18759020334646b031e708ab4158d653a938822bfb7b95ef2e93aa3/rpds_py-2026.5.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:21942f52dbbd5f8758bf021213d28bd45c39e873e65e2407faf5f1846f5761ad", size = 352148, upload-time = "2026-05-28T12:00:44.638Z" }, - { url = "https://files.pythonhosted.org/packages/31/9b/5f4a1e2f960bca3ac5d052b139dd31eed97b259f9d909173821760d542e8/rpds_py-2026.5.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f414556f6e3958300ff941e40c9f97e3dc9774ddd1b3434c475d73dd354bbed3", size = 345196, upload-time = "2026-05-28T12:00:46.14Z" }, - { url = "https://files.pythonhosted.org/packages/1a/71/1d9574d6a2fa20ab60eaa55c7467f5aa20cbc770f341a05f09c0876f59e2/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1013a8625c74043210190b246f5b1551e09757c1f356c6e4160ef96c5bc081", size = 374981, upload-time = "2026-05-28T12:00:47.531Z" }, - { url = "https://files.pythonhosted.org/packages/0c/9a/37e99f4915a80aa71670263c1267f7ae0af95f53a3f61e6c3bdc016d4515/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cc68e231a77a5f0d774ae278a1f8e55c0456501820847c1e4efb3829f3441df6", size = 379961, upload-time = "2026-05-28T12:00:49.216Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ff/6e73f74b89d2e0715e0fc86b7dde893f9a61ae2f9b256ff3bdfe41ac4e94/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9baffb505aff33acc69b422a19f77806680f3c8632227d79f48de8a810d1c2c5", size = 495965, upload-time = "2026-05-28T12:00:51.111Z" }, - { url = "https://files.pythonhosted.org/packages/ea/e0/425faba25f59d74d4638b267f7c7a80e8649d2ef4db10a19b0c4a71e6e6f/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8d2f912928d426e8cfa396f7f3f8d29a59e6689c86dcca3c420730c1096322b", size = 389526, upload-time = "2026-05-28T12:00:52.77Z" }, - { url = "https://files.pythonhosted.org/packages/c6/76/7a41960e3fddae47fab43a28684d5da981401dffd88253de0944148654cb/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90f628283be835db980c941767d41c9a27b5239e54ba0a9c1335247e82406964", size = 376190, upload-time = "2026-05-28T12:00:54.215Z" }, - { url = "https://files.pythonhosted.org/packages/27/60/5f38dc70824fc6951b51d35377e577a3a3a4c81a6769cc5a2de25ebe0ad1/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:1ebb2f0ab7e16132995a72de805170e0203df0c3dd22e1ef1cd1fdd90bd7a131", size = 383921, upload-time = "2026-05-28T12:00:55.673Z" }, - { url = "https://files.pythonhosted.org/packages/60/1a/d60a38caa1505f4b9483c3fbbde12c94e1079154f4f401a6da96f7e77621/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f3df3d16ded76f1f8c9cdebd0e1ea55fdf4c23b812de189814da7cf229c22a81", size = 404766, upload-time = "2026-05-28T12:00:57.518Z" }, - { url = "https://files.pythonhosted.org/packages/87/ff/602fd3f174d6425f0bce05ad0dfbec0e96b38d0f7d08a79af5aa20083885/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9af8905b8f854990e40d5206aa5ac58d9b0fe0b7f351ff2bb086c20f6c8c6a47", size = 551343, upload-time = "2026-05-28T12:00:58.978Z" }, - { url = "https://files.pythonhosted.org/packages/b8/c1/1be13327acdbead3eca1fde03b6a34dbb011f1e864e217f0d32cc1779a7f/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:036a36a87fb1cd3b214d11c4b3c4f7d2ddad933625dca1c900b56a057c07740a", size = 618502, upload-time = "2026-05-28T12:01:00.656Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d7/afb49b49d7f2be8b7ba1a9f0977fa5168003437b93086726f066544e8351/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:62ae3853454fe9ef283a03c96c2d835d39e84b14643a9d62c82ef0fb87d702ca", size = 581916, upload-time = "2026-05-28T12:01:02.22Z" }, - { url = "https://files.pythonhosted.org/packages/25/d1/dbef8c1f8a10f07beb62b5f054e20099fd9924b3ec001b8f0b6ac7813a85/rpds_py-2026.5.1-cp314-cp314t-win32.whl", hash = "sha256:6c3d771a46ec18b12af06ce36243a9a80b07a5d0515236332d90863ca8bb326a", size = 207855, upload-time = "2026-05-28T12:01:03.821Z" }, - { url = "https://files.pythonhosted.org/packages/2a/72/bfa4e61ab8e7dc1c8adf397e05e6cbdd4239357bd72b248d3de662f23915/rpds_py-2026.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c93c629be4636cf54337bd5f06c104d55e42ced54d681f6fe21ae510a65116f6", size = 225422, upload-time = "2026-05-28T12:01:05.194Z" }, - { url = "https://files.pythonhosted.org/packages/27/3a/7b5da92b640f67b6717ccafc83cdd06bfa7ff2395c3685c68922bb54d703/rpds_py-2026.5.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:3574b55c604b8f75dacb007136508bbc0db406e626301778096a133327e7f2fb", size = 349576, upload-time = "2026-05-28T12:01:06.722Z" }, - { url = "https://files.pythonhosted.org/packages/d7/8a/2aafd7ad355a1bd48ca76e2262b74b15e6432b5a1efe150efd4d779cd55d/rpds_py-2026.5.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:94068eb3ae6d43f5a786b7db96a406a34e6d5c24489feef32fd6e8946ea7b291", size = 343640, upload-time = "2026-05-28T12:01:08.441Z" }, - { url = "https://files.pythonhosted.org/packages/f7/7d/6c9523c1abbe840a1b7fba3c516d48e1d3487cc80fea4366c4071cf56784/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a5b10e8ce894825f380a8f1b6444cf73c294dfea62afbb2d13e3a9e630cec1", size = 375322, upload-time = "2026-05-28T12:01:09.934Z" }, - { url = "https://files.pythonhosted.org/packages/5a/5d/0b7b03fb1dc509321f01de3149784ab773e34c8573022029af8076afcb9c/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fc09f82e63d4bcd58149572f857a431bae851dc747e313c3b5bdf7abb907fda8", size = 379066, upload-time = "2026-05-28T12:01:11.48Z" }, - { url = "https://files.pythonhosted.org/packages/d7/e2/8ef6012999ebf1cb1c22f876d9ce5e63d960fd4631d2af3202d3f480aa25/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e10464d17df3b582745c25cec695cb9558bca2cb6ddb631aee1787fc72c767b2", size = 494586, upload-time = "2026-05-28T12:01:13.051Z" }, - { url = "https://files.pythonhosted.org/packages/80/af/1eeb029bec67582c226b7809172207cd005073af4ebd906e65ff494f4983/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ba05adbf15d994c38ec0b7ab32e858e5110c21e9009a00a86545fd220f84e038", size = 388415, upload-time = "2026-05-28T12:01:14.631Z" }, - { url = "https://files.pythonhosted.org/packages/18/23/ffbe10711c4d766c1cab0557d6906c074f795814863c67b351355d29354a/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77c004fdc7b891967106f78ddfd7b076bfe6813c6139c6fff6aed3bcaa960b26", size = 372427, upload-time = "2026-05-28T12:01:16.153Z" }, - { url = "https://files.pythonhosted.org/packages/bd/3a/30ba4a6ad457e5b070c18d742a33fb77d8d922b565cc881f8a5313d63bfe/rpds_py-2026.5.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:83bcf894486c9d78dd290d3c0124ff6dd8875d3025e2090a8ec49fcc37c55fdd", size = 383615, upload-time = "2026-05-28T12:01:17.809Z" }, - { url = "https://files.pythonhosted.org/packages/d3/69/62e242b53ce39c0814bd24e1a6e6eba6c92be716277745f317f9540a2e7b/rpds_py-2026.5.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3df104083952a0e0c6f10de33e440eabe98fb6317d23e1a58c68f6df08d01b9", size = 402786, upload-time = "2026-05-28T12:01:19.419Z" }, - { url = "https://files.pythonhosted.org/packages/38/c1/a770b9c186928a1ed0f7e6d7ae50e7f3950ed23e3f9e366dbc8e38cb55de/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:980450826cf22e133c57e0835070bdd0dd3f73b9b708c3ce223def2cb9469e14", size = 551583, upload-time = "2026-05-28T12:01:21.013Z" }, - { url = "https://files.pythonhosted.org/packages/21/7c/68e8579b95375b70d2a963103c42e705856cdb98569258bd807f4423891c/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:205dde846f24332ab0c1188699a043b8d165b79bb84529ce272c45048ff6be01", size = 616941, upload-time = "2026-05-28T12:01:22.548Z" }, - { url = "https://files.pythonhosted.org/packages/70/a1/a6135aed5730ff03ab957182259987ac11e55fb392a28dc6f0592048a280/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:3966b82dd563176396df030f3dd52a6e54cb69b718e95e78bd555ed3d1e0185d", size = 578349, upload-time = "2026-05-28T12:01:24.118Z" }, - { url = "https://files.pythonhosted.org/packages/09/6e/f24201a76a84e6c49d0bdfdfcb735210e21701e9b21c5bfc0ba497dd62f6/rpds_py-2026.5.1-cp315-cp315-win32.whl", hash = "sha256:7818f8d0a415be74d2be3590b0a1c1f463a642f4d0217e7d10602dceef5b79aa", size = 209922, upload-time = "2026-05-28T12:01:25.522Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e4/966bc240bb0485fc265278f6de44d05834bf0b3618886e0b22e33d54c49a/rpds_py-2026.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:b3cc20c0d800af78fd0fac68086e28c1856cec51ea528bb81ea851aa40d39325", size = 226003, upload-time = "2026-05-28T12:01:27.062Z" }, - { url = "https://files.pythonhosted.org/packages/5c/5c/a15a59269cd5e74472734516c73795c15eccfc841b3d4b0228c3f53f19d0/rpds_py-2026.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:3609e9939a8a76cd904cf98a3f1f13b5dc7e150adeaee89e0ea09652ea213e16", size = 221245, upload-time = "2026-05-28T12:01:28.51Z" }, - { url = "https://files.pythonhosted.org/packages/e0/22/135ce03804e179a71ceb13be095deda4a279bc88f7a6b8fa161c5ad44e12/rpds_py-2026.5.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:5d333a7127d4b307601ac37792bee01bb95c867cbfacf21b6375b804d6bbd723", size = 352015, upload-time = "2026-05-28T12:01:30.214Z" }, - { url = "https://files.pythonhosted.org/packages/3b/5f/f1f6d2652eb9d848f6eb369d8db83a2da6249bb49ad2c2a48f45d54538d3/rpds_py-2026.5.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:b5f077b44a4f7808520f66dae234988d867deb9aed9be5da057ce9ba831b2a41", size = 345016, upload-time = "2026-05-28T12:01:31.656Z" }, - { url = "https://files.pythonhosted.org/packages/88/66/b74182775691ea2290c99e52ac8d5db844e56fbec90ce421f107658c8314/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d8f9b7b78c9538fc9e04e82ec0e888ff0c3cffcfad152c77e57cd09351a98a", size = 374775, upload-time = "2026-05-28T12:01:33.136Z" }, - { url = "https://files.pythonhosted.org/packages/ff/8f/15e5a61d9f0a43902d36561d4f07cae6ae9f4716be825159fd72717f33af/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e3a8ae58895ac107ed934a6bf51e5846f95c53b9b940c2c6d310838fd5846358", size = 380270, upload-time = "2026-05-28T12:01:34.574Z" }, - { url = "https://files.pythonhosted.org/packages/02/c3/f859b12763a80540cdf2af0f15b19904cf756a71d7bdd3f82ff3e5b1bbf9/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0957cf3c2b8632ec7aaebffebea8005b353cc2a237b6e2ae3c2cac0820704cfb", size = 495285, upload-time = "2026-05-28T12:01:36.127Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c7/ff27c2ac8411d30b03b1829fd88cae8dad1a4d0da48dd25e57c4038042e6/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c396c1304de421050b3681ea70f371874b54d41b0151e96109758144c231e30b", size = 389581, upload-time = "2026-05-28T12:01:37.635Z" }, - { url = "https://files.pythonhosted.org/packages/6e/67/fe92ee32a6cc05c77228a2f8b1762e7124f386ec20ff83d0757b762d58d0/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad1bff7f666b9598e573815affd666aac6a13a585dde336f843e33350c7fadc", size = 376041, upload-time = "2026-05-28T12:01:39.307Z" }, - { url = "https://files.pythonhosted.org/packages/f8/91/b4d6685c27aba55bd82f25b278be8237038117d05f9659a6213ad3408130/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:656a042550878f12d45752452d47094b7cfe5ad1e9d7b87b5a22ad3ae5ff8015", size = 383946, upload-time = "2026-05-28T12:01:41.043Z" }, - { url = "https://files.pythonhosted.org/packages/bd/79/2c1d832a53c8e0f8e98fc970ec257b950fecd4f62be2ab7182b500a0cbc8/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c4bd4f70294737b5206a3e8e30ccadbf8a60301831c8ea23eec5dbeea1ecfa", size = 405526, upload-time = "2026-05-28T12:01:43.032Z" }, - { url = "https://files.pythonhosted.org/packages/78/c4/c98117b03c6a8581ab2c2dfccfe9a5ad82bd8128a3c28b46a6ad2d97c393/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:43bca78665423cabae77146f2fe7ce55272b6c8d55d82cca83effd42c7e13972", size = 551165, upload-time = "2026-05-28T12:01:44.648Z" }, - { url = "https://files.pythonhosted.org/packages/3b/c1/bc479ca069200af730881b1bd525e3114b2b391a351509fcb1b772f28086/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:42d0f20e85e549c870749d0e247f0c10d318a45b7e9676d575d2dcb04a1b2e66", size = 618778, upload-time = "2026-05-28T12:01:46.337Z" }, - { url = "https://files.pythonhosted.org/packages/77/65/38ab2f90df44c2febfb63cc10ced40763d9b4bc94d173e734528663fe7f5/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:b1be5c35683684d5331b93600c210e8367c254683d8a6df6bd21bd2da3a334fb", size = 581839, upload-time = "2026-05-28T12:01:48.109Z" }, - { url = "https://files.pythonhosted.org/packages/15/2d/ce1f605fe036aadd460e5822e578c6c7ec3a860936cca37d6e0f299daa77/rpds_py-2026.5.1-cp315-cp315t-win32.whl", hash = "sha256:75808f6c38ce7749bb68cc2770161aae5045e6c6f6781a9782e74b93304399df", size = 207866, upload-time = "2026-05-28T12:01:49.648Z" }, - { url = "https://files.pythonhosted.org/packages/79/cb/966040123eb102371559746908ef2c9471f4d43e17ec9a645a2258dab64b/rpds_py-2026.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:90bd6630002a1c7f09e7843dd79f0d24f3d2897cc25a753480917865d14f15b3", size = 225441, upload-time = "2026-05-28T12:01:51.408Z" }, - { url = "https://files.pythonhosted.org/packages/42/56/3fe0fb34820ff667be791b3a3c22b85e8bcba54e9c832f47438c191fa7be/rpds_py-2026.5.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:edf2765d84e42447f112ad877af8fe1db0089aaec5b28e88d6eab45e7fe99cea", size = 357151, upload-time = "2026-05-28T12:01:53.43Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f2/3eb9ccdb9f143b8c9b003978898cb497f942a324c077401e6b8834238e63/rpds_py-2026.5.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ad3773236e95f7f33991eb125224b7da66f206504d032a253a02da7e134519fb", size = 350195, upload-time = "2026-05-28T12:01:54.901Z" }, - { url = "https://files.pythonhosted.org/packages/a7/24/dbda232bc4f3ed732120692ab0d2c8402cb020516556d8bee622dcef2413/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a04df86b3f0fade39ec8fd0e0aab089b1da9fbd2b48df778a57ef96f5e7d38df", size = 381850, upload-time = "2026-05-28T12:01:56.601Z" }, - { url = "https://files.pythonhosted.org/packages/40/30/32e769839a358f78810c234f160f2cc21d1e4e47e1c0e0e0d535be5a0219/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6142dbd80c4df62a5d899f0d616d417f84e0bc8d32526c8e5589019d75d028a7", size = 387899, upload-time = "2026-05-28T12:01:58.212Z" }, - { url = "https://files.pythonhosted.org/packages/ab/86/ec84d243aadb3b34b71dd26a010d0930b2d284ff5fc9a69fec53810ee6fd/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b35217adefe87f2fe4db7e9766cabe84744bfe9616d9667be18988928c7f2dc", size = 501618, upload-time = "2026-05-28T12:01:59.888Z" }, - { url = "https://files.pythonhosted.org/packages/74/25/b60e52686bbff777a64f9e4f4d3dd57980dc846913777177a2c92e4937aa/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b95d5e11fc712b752081183a55a244c03cd00570489edd7014d8899f8ceb8162", size = 394003, upload-time = "2026-05-28T12:02:01.482Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c7/b3a6a588cc2219510ef3f42e207483a93950bedd1e3a0fd4015c95cff9e5/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141c9498daf2ace9eda35d2b0e376f9ea8b058d84f2aef4f96fccfd449a2f251", size = 379778, upload-time = "2026-05-28T12:02:03.197Z" }, - { url = "https://files.pythonhosted.org/packages/31/00/c7dba3fc8a3da8cb3f6db1eb3386be4d79c2e97c6890d20eb9ac66ae8c43/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:6f249f8b860a200ad35193af961183ebe9132710484e6f6ce0cf89fd83c63a9a", size = 392359, upload-time = "2026-05-28T12:02:04.817Z" }, - { url = "https://files.pythonhosted.org/packages/93/dd/472ba494c70753f93745992c99855bee0636daf74e6984e5e003f150316f/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e4abbf391a70be864920858bf360f4fb380577c9a0f732438a1996726e2c195b", size = 412820, upload-time = "2026-05-28T12:02:06.401Z" }, - { url = "https://files.pythonhosted.org/packages/1d/6f/93831a3bfe789542ed0c1d0d74b78b440f055d6dc3ea4640eba2d95e6e23/rpds_py-2026.5.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:c74005a7bb87752acf351c93897ec63ad77a07a0da7ecad9c050e32e7286ba34", size = 557243, upload-time = "2026-05-28T12:02:08.013Z" }, - { url = "https://files.pythonhosted.org/packages/1f/ff/0b3d604614ffc77522c6b288fdbce68957eb583da1002aa65ba38ac0ee40/rpds_py-2026.5.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:8213afbe8a3a906fb9acb2014423fe3359ee783d0bf90995f70623a3217bfa6c", size = 623541, upload-time = "2026-05-28T12:02:09.661Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ea/e7b0251441da9adfeaebcf29601d10f2a1455fcf0772fae9e7e19032bd96/rpds_py-2026.5.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8c43a8a973270fd173bf48cdf80bbe66312421cba68d40845034f174f2389049", size = 586326, upload-time = "2026-05-28T12:02:11.47Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/aa/2a/9618a122aeb2a169a28b03889a2995fe297588964333d4a7d67bdf46e147/rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4", size = 64051, upload-time = "2026-06-30T07:17:53.009Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/1f/a2dca5ffdbf1d475ffc4e80e4d5d720ff3a00f691795910116960ee12511/rpds_py-2026.6.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7b689145a1485c335569bd056464f3243a29af7ed3871c7be31ad624ba239bc7", size = 342174, upload-time = "2026-06-30T07:14:54.821Z" }, + { url = "https://files.pythonhosted.org/packages/4d/dc/323d08583c0832911768663d1944f0107fcd4088704858d84b5e06d105a0/rpds_py-2026.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:db08f45aecde626498fb3df07bcf6d2ec040af42e859a4f5040d79c200342911", size = 345513, upload-time = "2026-06-30T07:14:56.515Z" }, + { url = "https://files.pythonhosted.org/packages/0b/2a/e31989834d18d2f26ec1d2774c5b1eb3331df4ea8ada525175294c94b48a/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc992ab27b15f852c76755eb2ab7dce86585ddadba6fa5946e58556088845b4", size = 373783, upload-time = "2026-06-30T07:14:57.736Z" }, + { url = "https://files.pythonhosted.org/packages/87/fe/e80107ee3639585c9941c17d6a42cd65325022f656c023191fce78c324c8/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f88d653e7b3b779d71ae7454e20dcc9b6bae903f33c269db9f2be41bda3f261", size = 378316, upload-time = "2026-06-30T07:14:59.077Z" }, + { url = "https://files.pythonhosted.org/packages/22/6f/81e3adf81acfb6fa694de2a6e4e7d8863121e3e0799e0a7725e6cf5679c4/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e52655eaf81e32593abedaa4bfe33170c8cfedf3365ed9be6e11e07f148f0278", size = 499423, upload-time = "2026-06-30T07:15:00.488Z" }, + { url = "https://files.pythonhosted.org/packages/2d/9a/41263969df0ce3d9af2a96d5005a288200af1989aed3354bfceb5fc0b21f/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dfcc8b909769d19db55c7cc9541eb64b9b774b1057ffffb4f1048070475bb9f9", size = 386077, upload-time = "2026-06-30T07:15:01.911Z" }, + { url = "https://files.pythonhosted.org/packages/5e/19/7e98f468bd50346faff5b10e5297374b443bfdddacc8e9fbc65984539597/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c1255b302953c86a486b81d330d5ee1d5bd937691ce271b6be0ef0e299eaab7", size = 371315, upload-time = "2026-06-30T07:15:03.317Z" }, + { url = "https://files.pythonhosted.org/packages/99/3c/2b973b4d371906a134b03decfea7f5d9835a2c6d263454392e15b64b5b18/rpds_py-2026.6.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:8d2294a31386bfa251d8c8a39472beee17db67d4f1a6eabea665d35c9a4461c3", size = 383502, upload-time = "2026-06-30T07:15:04.627Z" }, + { url = "https://files.pythonhosted.org/packages/98/2a/12e2799500af0a307bca76b63361c51f9fe479223561489c29eea1f2ee41/rpds_py-2026.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8f23ead891a3b762f35ab3b04623da7056545b48aa60d59957e6789914545da", size = 402673, upload-time = "2026-06-30T07:15:05.856Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e3/21e5872d165fe08be4f229e3d5ee9d90019c0bf0e5538de60dbd54009450/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:421aba32367055614287a4292b6a17f1939c9452299f7a0209c117e990b646d4", size = 549964, upload-time = "2026-06-30T07:15:07.159Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d0/5ee0fe36844297de8123bee27bc12078c1a7416ad9f1b8a8ca18d6b0c0ac/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1e5822dfc2f0d4ab7e745eaa6d85945069329beeccef965af3f3bb26058fcab6", size = 615446, upload-time = "2026-06-30T07:15:08.531Z" }, + { url = "https://files.pythonhosted.org/packages/b1/80/1ea5873cb683f2fbe5f21b23ea1f6d179ead19f3c5b249b7eb5dca568ef2/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83e35b57523816c8613fd0776b40cd8bb9f596b37ddd2692eb4a6bb5ab2f8c93", size = 576975, upload-time = "2026-06-30T07:15:09.97Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e1/90ef639217a5ddb15b7f4f61b1c33911fd044ad03c311bafdd2bcab85582/rpds_py-2026.6.3-cp311-cp311-win32.whl", hash = "sha256:de3eceba0b683bcbb1ab93da016d0270df1f9ae7be716b40214c5dafac6ea45a", size = 204453, upload-time = "2026-06-30T07:15:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/f2/b7/b7a1695d7af36f521fb11e80d6d3adbd744f73b921859bd3c2a2c0dc706f/rpds_py-2026.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:2c54a076ca4d370980ab57bc0e31df57bbe8d41340436a90ef8b1219a3cbb127", size = 223219, upload-time = "2026-06-30T07:15:12.476Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a2/145afacf796e4506062825941176ad9445c2dcf2b3b6a1f13d3030a15e19/rpds_py-2026.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:168c733a7112e071bb7a66460e667edfcff06c017a3c523f7a8a8e08d0140804", size = 219137, upload-time = "2026-06-30T07:15:13.631Z" }, + { url = "https://files.pythonhosted.org/packages/5c/be/2e8974163072e7bab7df1a5acd54c4498e75e35d6d18b864d3a9d5dadc92/rpds_py-2026.6.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0", size = 343691, upload-time = "2026-06-30T07:15:14.96Z" }, + { url = "https://files.pythonhosted.org/packages/a4/73/319dfa745dd668efe89309141ded489126461fcecd2b8f3a3cda185129b6/rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf", size = 338542, upload-time = "2026-06-30T07:15:16.267Z" }, + { url = "https://files.pythonhosted.org/packages/21/63/4239893be1c4d09b709b1a8f6be4188f0870084ff547f46606b8a75f1b03/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24", size = 368180, upload-time = "2026-06-30T07:15:17.62Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ca/9c5de382225234ceb37b1844ebdb140db12b2a278bb9efe2fcd19f6c82ce/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e", size = 375067, upload-time = "2026-06-30T07:15:18.952Z" }, + { url = "https://files.pythonhosted.org/packages/87/dc/863f69d1bf04ade34b7fe0d59b9fdf6f0135fe2d7cbca74f1d665589559d/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975", size = 490509, upload-time = "2026-06-30T07:15:20.434Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ef/eac16a12048b45ec7c7fa94f2be3438a5f26bf9cc8580b18a1cfd609b7f6/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680", size = 382754, upload-time = "2026-06-30T07:15:21.831Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/d2f3f532616be4d06c316ef119683e832bd3d41e112bf3a88f4151c95b17/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6", size = 366189, upload-time = "2026-06-30T07:15:23.371Z" }, + { url = "https://files.pythonhosted.org/packages/e3/29/41a7b0e98a4b44cd676ab7598419623373eb43b20be68c084935c1a8cf88/rpds_py-2026.6.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a", size = 377750, upload-time = "2026-06-30T07:15:24.659Z" }, + { url = "https://files.pythonhosted.org/packages/2e/05/ecda0bec46f9a1565090bcdc941d023f6a25aff85fda28f89f8d19878152/rpds_py-2026.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4", size = 395576, upload-time = "2026-06-30T07:15:25.987Z" }, + { url = "https://files.pythonhosted.org/packages/68/a8/6ed52f03ee6cb854ce78785cc9a9a672eb880e83fd7224d471f667d151f1/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa", size = 543807, upload-time = "2026-06-30T07:15:27.356Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d6/156c0d3eea27ba09b92562ba2364ba124c0a061b199e17eac637cd25a5e2/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc", size = 611187, upload-time = "2026-06-30T07:15:28.931Z" }, + { url = "https://files.pythonhosted.org/packages/f1/31/774212ed989c62f7f310220089f9b0a3fb8f40f5443d1727abd5d9f52bc9/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822", size = 573030, upload-time = "2026-06-30T07:15:30.553Z" }, + { url = "https://files.pythonhosted.org/packages/c9/50/22f73127a41f1ce4f87fe39aadfb9a126345801c274aa93ae88456249327/rpds_py-2026.6.3-cp312-cp312-win32.whl", hash = "sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed", size = 202185, upload-time = "2026-06-30T07:15:32.027Z" }, + { url = "https://files.pythonhosted.org/packages/04/3a/f0ee4d4dde9d3b69dedf1b5f74e7a40017046d55052d173e418c6a94f960/rpds_py-2026.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f", size = 220394, upload-time = "2026-06-30T07:15:33.359Z" }, + { url = "https://files.pythonhosted.org/packages/f3/83/3382fe37f809b59f02aac04dbc4e765b480b46ee0227ed516e3bdc4d3dfc/rpds_py-2026.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96", size = 215753, upload-time = "2026-06-30T07:15:34.778Z" }, + { url = "https://files.pythonhosted.org/packages/a4/9e/b818ee580026ec578138e961027a68820c40afeb1ec8f6819b54fb99e196/rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223", size = 343012, upload-time = "2026-06-30T07:15:36.005Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6b/686d9dc4359a8f163cfbbf89ee0b4e586431de22fe8248edb63a8cf50d49/rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f", size = 338203, upload-time = "2026-06-30T07:15:37.462Z" }, + { url = "https://files.pythonhosted.org/packages/9e/9b/069aa329940f8207615e091f5eedbbd40e1e15eac68a0790fd05ccdf796c/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f", size = 367984, upload-time = "2026-06-30T07:15:39.008Z" }, + { url = "https://files.pythonhosted.org/packages/14/db/34c203e4becff3703e4d3bc121842c00b8689197f398161203a880052f4e/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7", size = 374815, upload-time = "2026-06-30T07:15:40.253Z" }, + { url = "https://files.pythonhosted.org/packages/ee/7d/8071067d2cc453d916ad836e828c943f575e8a44612537759002a1e07381/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6", size = 490545, upload-time = "2026-06-30T07:15:41.729Z" }, + { url = "https://files.pythonhosted.org/packages/a3/42/da06c5aa8f0484ff07f270787434204d9f4535e2f8c3b51ed402267e63c3/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af", size = 382828, upload-time = "2026-06-30T07:15:43.327Z" }, + { url = "https://files.pythonhosted.org/packages/57/d7/fe978efc2ae50abe48eb7464668ea99f53c010c60aeebb7b35ad27f23661/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf", size = 365678, upload-time = "2026-06-30T07:15:44.992Z" }, + { url = "https://files.pythonhosted.org/packages/69/9d/1d8922e1990b2a6eb532b6ff53d3e73d2b3bbffc84116c75826bee73dfc6/rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885", size = 377811, upload-time = "2026-06-30T07:15:46.523Z" }, + { url = "https://files.pythonhosted.org/packages/b1/3d/198dceafb4fb034a6a47347e1b0735d34e0bd4a50be4e898d408ee66cb14/rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4", size = 395382, upload-time = "2026-06-30T07:15:47.955Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f1/13968e49655d40b6b19d8b9140296bbc6f1d86b3f0f6c346cf9f1adddf4b/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7", size = 543832, upload-time = "2026-06-30T07:15:49.33Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ab/289bcb1b90bd3e40a2900c561fa0e2087345ecbb094f0b870f2345142b7c/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d", size = 611011, upload-time = "2026-06-30T07:15:50.847Z" }, + { url = "https://files.pythonhosted.org/packages/1e/16/5043105e679436ccfbc8e5e0dd2d663ed18a8b8113515fd06a5e5d77c83e/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97", size = 572431, upload-time = "2026-06-30T07:15:52.394Z" }, + { url = "https://files.pythonhosted.org/packages/85/ed/adab103321c0a6565d5ae1c2998349bc3ee175b82ccc5ae8fc04cc413075/rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0", size = 201710, upload-time = "2026-06-30T07:15:53.894Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ed/a03b09668e74e5dabbf2e211f6468e1820c0552f7b0500082da31841bf7b/rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80", size = 219454, upload-time = "2026-06-30T07:15:55.25Z" }, + { url = "https://files.pythonhosted.org/packages/27/17/b8642c12930b71bc2b25831f6708ccf0f75abcd11883932ec9ce54ba3a78/rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb", size = 215063, upload-time = "2026-06-30T07:15:56.573Z" }, + { url = "https://files.pythonhosted.org/packages/b6/36/7fbe9dcdaf857fb3f63c2a2284b62492d95f5e8334e947e5fb6e7f68c9be/rpds_py-2026.6.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:931908d9fc855d8f74783377822be318edb6dcb19e47169dc038f9a1bf60b06e", size = 344510, upload-time = "2026-06-30T07:15:57.921Z" }, + { url = "https://files.pythonhosted.org/packages/ba/54/f785cc3d3f60839ca57a5af4927a9f347b07b2799c373fc20f7949f87c7e/rpds_py-2026.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd", size = 339495, upload-time = "2026-06-30T07:15:59.238Z" }, + { url = "https://files.pythonhosted.org/packages/63/ef/d4cdaf309e6b095b43597103cf8c0b951d6cca2acce68c474f75ec12e0c7/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d", size = 369454, upload-time = "2026-06-30T07:16:01.021Z" }, + { url = "https://files.pythonhosted.org/packages/96/4a/9559a68b7ee15db09d7981212e8c2e219d2a1d6d4faa0391d813c3496a36/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847927daf4cffbd4e90e42bc890069897101edd015f956cb8721b3473372edda", size = 374583, upload-time = "2026-06-30T07:16:02.287Z" }, + { url = "https://files.pythonhosted.org/packages/ef/75/8964aa7d2c6e8ac43eba8eb6e6b0fdda1f46d39f2fc3e6aa9f2cb17f485d/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aca6c1ef08a82bfe327cc156da694660f599923e2e6665b6d81c9c2d0ac9ffc8", size = 492919, upload-time = "2026-06-30T07:16:03.723Z" }, + { url = "https://files.pythonhosted.org/packages/8f/97/6908094ac804115e65aedfd90f1b5fee4eebebd3f6c4cfc5419939267565/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae50181a047c871561212bb97f7932a2d45fb53e947bd9b57ebad85b529cbc53", size = 383725, upload-time = "2026-06-30T07:16:05.305Z" }, + { url = "https://files.pythonhosted.org/packages/d1/9c/0d1fdc2e7aba23e290d603bc494e97bd205bae262ce33c6b32a69768ed5e/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504", size = 367255, upload-time = "2026-06-30T07:16:07.086Z" }, + { url = "https://files.pythonhosted.org/packages/c4/fe/f0209ca4a9ed074bc8acb44dfd0e81c3122e94c9689f5645b7973a866719/rpds_py-2026.6.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e4316bf32babbed84e691e352faf967ce2f0f024174a8643c37c94a1080374fc", size = 379060, upload-time = "2026-06-30T07:16:08.525Z" }, + { url = "https://files.pythonhosted.org/packages/c6/8d/f1cc54c616b9d8897de8738aac148d20afca93f68187475fe194d09a71b9/rpds_py-2026.6.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8c6e5a2f750cc71c3e3b11d71661f21d6f9bc6cebc6564b1466417a1ec03ec77", size = 395960, upload-time = "2026-06-30T07:16:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/fb/04/aafff00f73aeca2945f734f1d483c64ab8f472d0864ab02377fd8e89c3b2/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4470ce197d4090875cf6affbf1f853338387428df97c4fb7b7106317b8214698", size = 545356, upload-time = "2026-06-30T07:16:11.816Z" }, + { url = "https://files.pythonhosted.org/packages/fd/cc/e229663b9e4ddac5a4acbe9085dd80a71af2a5d356b8b39d6bff233f24b0/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea964164cc9afa72d4d9b23cc28dafae93693c0a53e0b42acbff15b22c3f9ddd", size = 612319, upload-time = "2026-06-30T07:16:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7a/8a0e6d3e6cd066af108b71b43122c3fe158dd9eb86acac626593a2582eb1/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:639c8929aa0afe81be836b04de888460d6bed38b9c54cfc18da8f6bfabf5af5d", size = 573508, upload-time = "2026-06-30T07:16:15.23Z" }, + { url = "https://files.pythonhosted.org/packages/87/03/2a69ab618a789cf6cf85c86bb844c62d090e700ab1a2aa676b3741b6c516/rpds_py-2026.6.3-cp314-cp314-win32.whl", hash = "sha256:882076c00c0a608b131187055ddc5ae29f2e7eaf870d6168980420d58528a5c8", size = 202504, upload-time = "2026-06-30T07:16:16.893Z" }, + { url = "https://files.pythonhosted.org/packages/85/62/a3892ba945f4e24c78f352e5de3c7620d8479f73f211406a97263d13c7d2/rpds_py-2026.6.3-cp314-cp314-win_amd64.whl", hash = "sha256:0be972be84cfcaf46c8c6edf690ca0f154ac17babf1f6a955a51579b34ad2dc5", size = 220380, upload-time = "2026-06-30T07:16:18.108Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e7/c2bd44dc831931815ad11ebb5f430b5a0a4d3caa9de837107876c30c3432/rpds_py-2026.6.3-cp314-cp314-win_arm64.whl", hash = "sha256:2a9c6f195058cb45335e8cc3802745c603d716eb96bc9625950c1aac71c0c703", size = 215976, upload-time = "2026-06-30T07:16:19.654Z" }, + { url = "https://files.pythonhosted.org/packages/79/9c/fff7b74bce9a091ec9a012a03f9ff5f69364eaf9451060dfc4486da2ffdd/rpds_py-2026.6.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:f90938e92afda60266da758ee7d363447f7f0138c9559f9e1811629580582d90", size = 346840, upload-time = "2026-06-30T07:16:21.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/77bcb1168b33704908295533d27f10eb811e9e3e193e8993dc99572211d3/rpds_py-2026.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec829541c45bca16e61c7ae50c20501f213605beb75d1aba91a6ee37fbbb56a4", size = 340282, upload-time = "2026-06-30T07:16:22.875Z" }, + { url = "https://files.pythonhosted.org/packages/87/3c/7a9081c7c9e645b39efe19e4ffbeccd80add246327cd9b888aecffd72317/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd70d95892096cdb26f15a00c45907b17817577aa8d1c76b2dcc2788391f9e9", size = 370403, upload-time = "2026-06-30T07:16:24.415Z" }, + { url = "https://files.pythonhosted.org/packages/f7/69/af47021eb7dad6ff3396cb001c08f0f3c4d06c20253f75be6421a59fe6b7/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29dfa0533a5d4c94d4dfa1b694fcb56c9c63aad8330ffdd816fd225d0a7a162f", size = 376055, upload-time = "2026-06-30T07:16:26.111Z" }, + { url = "https://files.pythonhosted.org/packages/81/fc/a3bcf517084396a6dd258c592567a3c011ba4557f2fde23dceaf26e74f2e/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af05d726809bff6b141be124d4c7ce998f9c9c7f30edb1f46c07aa103d540b41", size = 494419, upload-time = "2026-06-30T07:16:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/c9/eb/13d529d1788135425c7bf207f8463458ca5d92e43f3f701365b83e9dffc1/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9826217f048f620d9a712672818bf231442c1b35d96b227a07eabd11b4bb6945", size = 384848, upload-time = "2026-06-30T07:16:29.183Z" }, + { url = "https://files.pythonhosted.org/packages/8e/f4/b7ac49f30013aba8f7b9566b1dd07e81de95e708c1374b7bacc5b9bc5c9c/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536bceea4fa4acf7e1c61da2b5786304367c816c8895be71b8f537c480b0ea1f", size = 371369, upload-time = "2026-06-30T07:16:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/31/86/6260bafa622f788b07ddec0e52d810305c8b9b0b8c27f58a2ab04bf62b4f/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:bc0011654b91cc4fb2ae701bec0a0ba1e552c0714247fa7af6c59e0ccfa3a4e1", size = 379673, upload-time = "2026-06-30T07:16:32.486Z" }, + { url = "https://files.pythonhosted.org/packages/19/c3/03f1ee79a047b48daeca157c89a18509cde22b6b951d642b9b0af1be660a/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:539d75de9e0d536c84ff18dfeb805398e58227001ce09231a26a08b9aed1ee0e", size = 397500, upload-time = "2026-06-30T07:16:34.471Z" }, + { url = "https://files.pythonhosted.org/packages/f0/95/8ed0cd8c377dca12aea498f119fe639fc474d1461545c39d2b5872eb1c0f/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:166cf54d9f44fc6ceb53c7860258dde44a81406646de79f8ed3234fca3b6e538", size = 545978, upload-time = "2026-06-30T07:16:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f2/0eb57f0eaa83f8fc152a7e03de968ab77e1f00732bebc892b190c6eebde7/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d34c20167764fbcf927194d532dd7e0c56772f0a5f943fa5ef9e9afbba8fb9db", size = 613350, upload-time = "2026-06-30T07:16:38.213Z" }, + { url = "https://files.pythonhosted.org/packages/5b/de/e0674bdbc3ef7634989b3f854c3f34bc1f587d36e5bfdc5c378d57034619/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea7bb13b7c9a29791f87a0387ba7d3ad3a6d783d827e4d3f27b40a0ff44495e2", size = 576486, upload-time = "2026-06-30T07:16:39.797Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f6/21101359743cd136ada781e8210a85769578422ba460672eea0e29739200/rpds_py-2026.6.3-cp314-cp314t-win32.whl", hash = "sha256:6de4744d05bd1aa1be4ed7ea1189e3979196808008113bbbf899a460966b925e", size = 201068, upload-time = "2026-06-30T07:16:41.316Z" }, + { url = "https://files.pythonhosted.org/packages/a6/b2/9574d4d44f7760c2aa32d92a0a4f41698e33f5b204a0bf5c9758f52c79d5/rpds_py-2026.6.3-cp314-cp314t-win_amd64.whl", hash = "sha256:c7b9a2f8f4d8e90af72571d3d495deebdd7e3c75451f5b41719aee166e940fc2", size = 220600, upload-time = "2026-06-30T07:16:43.091Z" }, + { url = "https://files.pythonhosted.org/packages/08/ae/f23a2697e6ee6340a578b0f136be6483657bef0c6f9497b752bb5c0964bb/rpds_py-2026.6.3-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:e059c5dde6452b44424bd1834557556c226b57781dee1227af23518459722b13", size = 344726, upload-time = "2026-06-30T07:16:44.5Z" }, + { url = "https://files.pythonhosted.org/packages/c3/63/e7b3a1a5358dd32c930a1062d8e15b67fd6e8922e81df9e91706d66ee5c8/rpds_py-2026.6.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2f7c26fbc5acd2522b95d4177fe4710ffd8e9b20529e703ffbf8db4d93903f05", size = 339587, upload-time = "2026-06-30T07:16:46.255Z" }, + { url = "https://files.pythonhosted.org/packages/ec/64/10a85681916ca55fffb91b0a211f84e34297c109243484dd6394660a8a7c/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3086b538543802f84c843911242db20447de00d8752dd0efc936dbcf02218ba", size = 369585, upload-time = "2026-06-30T07:16:48.101Z" }, + { url = "https://files.pythonhosted.org/packages/76/c2/baf95c7c38823e12ba34407c5f5767a89e5cf2233895e56f608167ae9493/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2e5c5ee828d42cb11760761c0af6507927bec42d0ad5458f97c9203b054617", size = 375479, upload-time = "2026-06-30T07:16:49.93Z" }, + { url = "https://files.pythonhosted.org/packages/6a/94/0aad06c72d65101e11d33528d438cda99a39ce0da99466e156158f2541d3/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed0c1e5d10cdc7135537988c74a0188da68e2f3c30813ba3744ab1e42e0480f9", size = 492418, upload-time = "2026-06-30T07:16:51.641Z" }, + { url = "https://files.pythonhosted.org/packages/b5/17/de3f5a479a1f056535d7489819639d8cd591ea6281d700390b43b1abd745/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c2642a7603ec0b16ed77da4555db3b4b472341904873788327c0b0d7b95f1bb", size = 384123, upload-time = "2026-06-30T07:16:53.622Z" }, + { url = "https://files.pythonhosted.org/packages/46/7d/bf09bd1b145bb2671c03e1e6d1ab8651858d90d8c7dfeadd85a37a934fd8/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4320744c1ffdd95a603def63344bfab2d33edeab301c5007e7de9f9f5b3885", size = 367351, upload-time = "2026-06-30T07:16:55.241Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ea/1bb734f314b8be319149ddee80b18bd41372bdcfbdf88d28131c0cd37719/rpds_py-2026.6.3-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:a9f4645593036b81bbdb36b9c8e0ea0d1c3fee968c4d59db0344c14087ef143a", size = 378827, upload-time = "2026-06-30T07:16:56.841Z" }, + { url = "https://files.pythonhosted.org/packages/4b/93/d9611e5b25e26df9a3649813ed66193ace9347a7c7fc4ab7cf70e94851c0/rpds_py-2026.6.3-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e55d236be29255554da47abe5c577637db7c24a02b8b46f0ca9524c855801868", size = 395966, upload-time = "2026-06-30T07:16:58.557Z" }, + { url = "https://files.pythonhosted.org/packages/c3/cb/99d77e16e5534ae1d90629bbe419ba6ee170833a6a85e3aa1cc41726fbbc/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:24e9c5386e16669b674a69c156c8eeefcb578f3b3397b713b08e6d60f3c7b187", size = 545680, upload-time = "2026-06-30T07:17:00.164Z" }, + { url = "https://files.pythonhosted.org/packages/59/15/11a29755f790cef7a2f755e8e14f4f0c33f39489e1893a632a2eee59672b/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c60924535c75f1566b6eb75b5c31a48a43fef04fa2d0d201acbad8a9969c6107", size = 611853, upload-time = "2026-06-30T07:17:01.962Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/0c27547e21644da938fb530f7e1a8148dd24d02db07e7a5f2567a17ce710/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:38a2fea2787428f811719ceb9114cb78964a3138838320c29ac39526c79c16ba", size = 573715, upload-time = "2026-06-30T07:17:03.693Z" }, + { url = "https://files.pythonhosted.org/packages/29/71/4d8fcf700931815594bce892255bbd973b94efaf0fc1932b0590df18d886/rpds_py-2026.6.3-cp315-cp315-win32.whl", hash = "sha256:d483fe17f01ad64b7bf7cc38fcefff1ca9fb83f8c2b2542b68f97ffe0611b369", size = 202864, upload-time = "2026-06-30T07:17:05.746Z" }, + { url = "https://files.pythonhosted.org/packages/eb/62/b577562de0edbb55b2be85ce5fd09c33e386b9b13eee09833af4240fd5c4/rpds_py-2026.6.3-cp315-cp315-win_amd64.whl", hash = "sha256:67e3a721ffc5d8d2210d3671872298c4a84e4b8035cfe42ffd7cde35d772b146", size = 220430, upload-time = "2026-06-30T07:17:07.471Z" }, + { url = "https://files.pythonhosted.org/packages/c8/95/d6d0b2509825141eef60669a5739eec88dbc6a48053d6c92993a5704defe/rpds_py-2026.6.3-cp315-cp315-win_arm64.whl", hash = "sha256:6e84adbcf4bf841aed8116a8264b9f50b4cb3e7bd89b516122e616ac56ca269e", size = 215877, upload-time = "2026-06-30T07:17:09.008Z" }, + { url = "https://files.pythonhosted.org/packages/b7/bf/f3ea278f0afd615c1d0f19cb69043a41526e2bb600c2b536eb192218eb27/rpds_py-2026.6.3-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ae6dd8f10bd17aad820876d24caec9efdafd80a318d16c0a48edb5e136902c6b", size = 346933, upload-time = "2026-06-30T07:17:10.762Z" }, + { url = "https://files.pythonhosted.org/packages/9d/29/9907bdf1c5346763cf10b7f6852aad86652168c259def904cbe0082c5864/rpds_py-2026.6.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:bdbd97738551fca3917c1bd7188bec1920bb520104f28e7e1007f9ceb17b7690", size = 340274, upload-time = "2026-06-30T07:17:12.266Z" }, + { url = "https://files.pythonhosted.org/packages/6f/2c/8e03767b5778ef25cebf74a7a91a2c3806f8eced4c92cb7406bbe060756d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b95977e7211527ab0ba576e286d023389fbeeb32a6b7b771665d333c60e5342", size = 370763, upload-time = "2026-06-30T07:17:14.107Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e1/df2a7e1ba2efd796af26194250b8d42c821b46592311595162af9ef0528d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15fde0e6fb0d88a60d221204873743e5d9f0b7d29165e62cd86d0413ad74ba6", size = 376467, upload-time = "2026-06-30T07:17:15.76Z" }, + { url = "https://files.pythonhosted.org/packages/6b/de/8a0814d1946af29cb068fb259aa8622f856df1d0bab58429448726b537f5/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a136d453475ac0fcbda502ef1e6504bd28d6d904700915d278deeab0d00fe140", size = 496689, upload-time = "2026-06-30T07:17:17.308Z" }, + { url = "https://files.pythonhosted.org/packages/df/f3/f19e0c852ba13694f5a79f3b719331051573cb5693feacf8a88ffffc3a71/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f826877d462181e5eb1c26a0026b8d0cab05d99844ecb6d8bf3627a2ca0c0442", size = 385340, upload-time = "2026-06-30T07:17:18.928Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ae/7ec3a9d2d4351f99e37bcb06b6b6f954512646bfdbf9742e1de727865daf/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79486287de1730dbaff3dbd124d0ca4d2ef7f9d29bf2544f1f93c09b5bcbbd12", size = 372179, upload-time = "2026-06-30T07:17:20.539Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ac/9cee911dff2aaa9a5a8354f6610bf2e6a616de9197c5fff4f54f82585f1e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:808345f53cb952433ca2816f1604ff3515608a81784954f38d4452acfe8e61d5", size = 379993, upload-time = "2026-06-30T07:17:22.212Z" }, + { url = "https://files.pythonhosted.org/packages/83/6b/7c2a07ba88d1e9a936612f7a5d067467ed03d971d5a06f7d309dff044a7e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1967debc37f64f2c4dc90a7f563aec558b471966e12adcac4e1c4240496b6ebf", size = 398909, upload-time = "2026-06-30T07:17:23.66Z" }, + { url = "https://files.pythonhosted.org/packages/97/0b/776ffcb66783637b0031f6d58d6fb55913c8b5abf00aeecd46bf933fb477/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:f0840b5b17057f7fd918b76183a4b5a0635f43e14eb2ce60dce1d4ee4707ea00", size = 546584, upload-time = "2026-06-30T07:17:25.264Z" }, + { url = "https://files.pythonhosted.org/packages/55/33/ba3bc04d7092bd553c9b2b195624992d2cc4f3de1f380b7b93cbee67bd79/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:faa679d19a6696fd54259ad321251ad77a13e70e03dd834daa762a44fb6196ef", size = 614357, upload-time = "2026-06-30T07:17:26.888Z" }, + { url = "https://files.pythonhosted.org/packages/8b/71/14edf065f04630b1a8472f7653cad03f6c478bcf95ea0e6aed55451e33ea/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:23a439f31ccbeff1574e24889128821d1f7917470e830cf6544dced1c662262a", size = 576533, upload-time = "2026-06-30T07:17:28.546Z" }, + { url = "https://files.pythonhosted.org/packages/ba/76/65002b08596c389105720a8c0d22298b8dc25a4baf89b2ce431343c8b1de/rpds_py-2026.6.3-cp315-cp315t-win32.whl", hash = "sha256:913ca42ccad3f8cc6e292b587ae8ae49c8c823e5dce51a736252fc7c7cdfa577", size = 201204, upload-time = "2026-06-30T07:17:30.193Z" }, + { url = "https://files.pythonhosted.org/packages/8c/97/d855d6b3c322d1f27e26f5241c42016b56cf01377ea8ed348285f54652f0/rpds_py-2026.6.3-cp315-cp315t-win_amd64.whl", hash = "sha256:ae3d4fe8c0b9213624fdce7279d70e3b148b682ca20719ebd193a23ebfa47324", size = 220719, upload-time = "2026-06-30T07:17:31.788Z" }, + { url = "https://files.pythonhosted.org/packages/b4/9c/f0d19ac587fd0e4ab6b72cda355e9c5a6166b01ef7e064e437aef8eb9fef/rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4cf2d36a2357e4d07bb5a4f98801265327b48256867816cfd2ceb001e9754a8f", size = 349791, upload-time = "2026-06-30T07:17:33.315Z" }, + { url = "https://files.pythonhosted.org/packages/38/c7/1d49d204c9fd2ee6c537601dc4c1ba921e03363ca576bfab94a00254ac9a/rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:30c6dc199b24a5e3e81d50da0f00858c5bbdb2617a750395687f4339c5818171", size = 352842, upload-time = "2026-06-30T07:17:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e5/c0b5dc93cd0d4c06ce1f438907649514e2ea077bcd911e3154a51e96c38e/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9891e594296ab9dada6551c8e7b387b2721f27a67eecd528412e8906247a7b90", size = 382094, upload-time = "2026-06-30T07:17:36.514Z" }, + { url = "https://files.pythonhosted.org/packages/0d/54/ec0e907b4ca8d541112db352409bd15f871c9b243e0c92c9b5a46ae96f01/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5c2dc92304aa48a4a60443b548bb12f12e119d4b72f314015e67b9e1be97fca", size = 388662, upload-time = "2026-06-30T07:17:38.235Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f4/921c22a4fd0f1c1ac13a3996ffbf0aa67951e2c8ad0d1d9574938a2932e8/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:127e08c0642d880cf32ca47ec2a4a77b901f7e2dd1ad9762adb13955d72ffcc9", size = 504896, upload-time = "2026-06-30T07:17:39.689Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1b/a114b972cefa1ab1cdb3c7bb177cd3844a12826c507c722d3a73516dbbaf/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bb68f03f395eb793220b45c097bd4d8c32944393da0fad8b999efac0868fc8c", size = 391545, upload-time = "2026-06-30T07:17:41.336Z" }, + { url = "https://files.pythonhosted.org/packages/4e/98/af9b3db77d47fcbe6c8c1f36e2c2147ec70292819e99c325f871584a1c11/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3450b693fde92133e9f51060568a4c31fcca76d5e53bbd611e689ca446517e9", size = 380059, upload-time = "2026-06-30T07:17:42.857Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ba/0efd8668b97c1d26a61566386c636a7a7a09829e474fdf807caa15a2c844/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:5e8d07bddee435a2ff6f1920e18feff28d0bc4533e42f4bf6927fbd073312c41", size = 393235, upload-time = "2026-06-30T07:17:44.637Z" }, + { url = "https://files.pythonhosted.org/packages/62/90/8c139ee9690f73b0829f32647de6f40d826f8f443af6fa72644f96351aac/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3a83ae6c67b7676b9878378547ca8e93ed77a580037bcbcd1d32f739e1e6089c", size = 413008, upload-time = "2026-06-30T07:17:46.225Z" }, + { url = "https://files.pythonhosted.org/packages/9c/97/0043896fdd7828ce09a1d9a8b06433714d0960fc4ff3fc4aa72b666b764e/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2bfd04c19ddbd6640de0b51894d764bd2758854d5b75bd102d2ef10cb9c293a9", size = 558118, upload-time = "2026-06-30T07:17:47.759Z" }, + { url = "https://files.pythonhosted.org/packages/f6/40/02355f0e134f783a8f9814c4680a1bd311d37671577a5964ea838573ff37/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:ca6546b66be9dc4738b1b043d5ebd5488c66c578c5ff0fd0e8065313fe3afb76", size = 623138, upload-time = "2026-06-30T07:17:49.355Z" }, + { url = "https://files.pythonhosted.org/packages/10/85/48f0abdcef5cce4e034c7a5b0ceeceba0b01bf0d942824f4bb720afe2dec/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8e65860d238379ed982fd9ba690579b5e95af2f4840f99c772816dbe573cb826", size = 586486, upload-time = "2026-06-30T07:17:51.141Z" }, ] [[package]] name = "ruff" -version = "0.15.17" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/a9/3abdf488f1bf3d24c699415e454ed554a6350d5d89ce183be1ee0a3361ac/ruff-0.15.17.tar.gz", hash = "sha256:2ec446937fd16c8c4de2674a209cc5af64d9c6f17d21fbf1151054fa0bcf5219", size = 4743346, upload-time = "2026-06-11T17:54:47.663Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/4d/e11259f5da07cb6afb2d074c31bf09da9671993f7329d4f15d2fdc458301/ruff-0.15.17-py3-none-linux_armv6l.whl", hash = "sha256:d9feddb927fc68bd295f5eebc587a7e42cfaf9b65f60ca4a2386febff575da8f", size = 10856677, upload-time = "2026-06-11T17:54:49.533Z" }, - { url = "https://files.pythonhosted.org/packages/29/3e/772d679e1a0dc058e58875bd2c0cb713a0530877b4a76fee3c7966df0d49/ruff-0.15.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:25805a226d741c47d274a35ad5c10a7dde175fcddfa511d7cf3da0a21eb3eab7", size = 11223443, upload-time = "2026-06-11T17:55:00.573Z" }, - { url = "https://files.pythonhosted.org/packages/68/58/bd41f7688b2fd5623012605130ed70e60aa7f2244baa3d5066bdd61530c8/ruff-0.15.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f6ad73b14c2d18a3bf8ad7cb6974294d7f613a7898604826058e6ac64918ef4d", size = 10566458, upload-time = "2026-06-11T17:55:07.52Z" }, - { url = "https://files.pythonhosted.org/packages/d8/5b/733371013fcf1ec339e477ece6ab42bfe10bdd9bba8ee88a9516aa56bfc0/ruff-0.15.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ba0c1e4f95bcb3869d0d30cbd5917071ef2e28665abfec970cdab0492c713ed", size = 10914483, upload-time = "2026-06-11T17:55:05.501Z" }, - { url = "https://files.pythonhosted.org/packages/bd/cc/6f24251cc0252f7239391ccb85833f320efad14ebe5b443943f37ced6332/ruff-0.15.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:81647960f10bff57d2e51cadd0c3950fe598400c852863a038720ef5b8cca91e", size = 10647497, upload-time = "2026-06-11T17:54:57.733Z" }, - { url = "https://files.pythonhosted.org/packages/68/dd/0d10c17ce1a1624d6fc3156309c3f834fdb5dfaad026ec90c85684f3990e/ruff-0.15.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e01a84ddbc8c16c23055ba3924476850f1bbc1917cebbb9376665a63e74260d", size = 11416967, upload-time = "2026-06-11T17:54:51.461Z" }, - { url = "https://files.pythonhosted.org/packages/2f/91/556bfb156f6144f355e831c23db00b2fc4120f86b3ce81cc5f7fd2df51f3/ruff-0.15.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fe9f653152f8f294f9f7e03bf3a453d8b4a27f7a59c78c8666167f2b17b96c", size = 12335770, upload-time = "2026-06-11T17:54:45.793Z" }, - { url = "https://files.pythonhosted.org/packages/88/82/8b5999aa13355e926f06d9f42a32dcca862f623bf0363785ff89d607dffd/ruff-0.15.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c0fe88a7676e7a05b73174d4d4a59cb2ac21ff8263583f87a81a6018475a978", size = 11575441, upload-time = "2026-06-11T17:54:32.661Z" }, - { url = "https://files.pythonhosted.org/packages/11/93/f10377bb04109ca0e8cbc483ff1982c54b6d418210041776f93e8cdc7fa9/ruff-0.15.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecfc3c7878fff94633ab0348524e093f9ce3243080416dd7d14f8ba400174719", size = 11557614, upload-time = "2026-06-11T17:54:34.698Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a6/eeeae7f7d5493df41649ab3db92f086b2d0a30199e4efdf8e3dd7a033f24/ruff-0.15.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:b8461180b22420b1bdc289909410930761629fddf2a5aaf60fae1ab26cedc4c4", size = 11544450, upload-time = "2026-06-11T17:54:39.042Z" }, - { url = "https://files.pythonhosted.org/packages/32/88/5991ce565129a24dd4a00db1254b3b5db2e53018cbe4018ea5a89738e727/ruff-0.15.17-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6eccbe50a038b503e7140b441aa9c7fc8c1f36edf23ebef9f4165c2f28f568b7", size = 10892524, upload-time = "2026-06-11T17:55:09.432Z" }, - { url = "https://files.pythonhosted.org/packages/f5/1d/0fdd248313425f55223968af04b0a42125466a8d88d21c1d99c6af0a51e8/ruff-0.15.17-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:382fc0521025f5a8ad447d8bdd523545d0d7646adb718eb1c2dac5065ec27c0f", size = 10659573, upload-time = "2026-06-11T17:54:36.824Z" }, - { url = "https://files.pythonhosted.org/packages/9e/0e/072e8260deb9461062ce9311ced27a8e541229a6ffd483013dd37661e43e/ruff-0.15.17-py3-none-musllinux_1_2_i686.whl", hash = "sha256:456d41fcd1b2777ad63f09a6e7121d43f7b688bbc76a800c10f7f8fb1f912c3f", size = 11127818, upload-time = "2026-06-11T17:55:03.124Z" }, - { url = "https://files.pythonhosted.org/packages/ab/b4/55060a34163121498014696b5f656db5b8c6963768f227dbf0d76b311073/ruff-0.15.17-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b1a04bcc94ae6194e9db05d16ad31f298a7194bfbcb08258bbe589cee1d587b8", size = 11655901, upload-time = "2026-06-11T17:54:53.562Z" }, - { url = "https://files.pythonhosted.org/packages/49/71/9b29d6b87cef468d697f43c6a91e3fae4a80185779d7d5a4ef27d173439f/ruff-0.15.17-py3-none-win32.whl", hash = "sha256:596065960ab1ff593f744220c9fe6580eda00a95003cffa9f4048bb5b1bf0392", size = 10925574, upload-time = "2026-06-11T17:54:55.723Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b2/8fc77f3723228836fa5d12497eb71c808f83782e10d058d2b15cfa14640b/ruff-0.15.17-py3-none-win_amd64.whl", hash = "sha256:6769e5fa1710b179b92e0bfa5a51735b35baea9013dadb06d5f44cbcf9547084", size = 12058788, upload-time = "2026-06-11T17:54:41.042Z" }, - { url = "https://files.pythonhosted.org/packages/2d/c7/c53e8dbff9c9dc4b7928773421ae294a5d28fcb8dcda1a089579d3a7e510/ruff-0.15.17-py3-none-win_arm64.whl", hash = "sha256:f3be1fbb34bcdfd146240d8fb92a709d4c2c8191348580a3c044ec60fa0b4456", size = 11355275, upload-time = "2026-06-11T17:54:43.635Z" }, +version = "0.15.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/06/ae069393fc66e8ff33036d4b368003833bf6e88ccf182e17e7a2f1c754fd/ruff-0.15.22.tar.gz", hash = "sha256:3f15175b1fb580126f58285a5dae6b2ea89000136d980c64499211f116b54809", size = 4785063, upload-time = "2026-07-16T15:14:13.244Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/18/ee54b7ae1e121be7a28ea6da4b67564ebb0530e183a54415ab7e3bcd2c4e/ruff-0.15.22-py3-none-linux_armv6l.whl", hash = "sha256:44423e73493737f5e7c5b41d475483898ff37afcdae38bc3da5085e29af1c2d8", size = 10781258, upload-time = "2026-07-16T15:13:19.452Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d2/2520cb14761ddbeaf57642a76942fc36adcbdbe53b4532241995f6fc485c/ruff-0.15.22-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b82c6482946e9eda7ff2e091d25b8bad3f718684e1916d41bd56873cee05b697", size = 10999477, upload-time = "2026-07-16T15:13:23.318Z" }, + { url = "https://files.pythonhosted.org/packages/c9/10/74e53572aa758dfaa678c2a2646b5c5515d884b7ca56be4d2ce03ca4b560/ruff-0.15.22-py3-none-macosx_11_0_arm64.whl", hash = "sha256:11c1c715af53a09f714e011106bffc419751ec8232fcb5da42173284ea3fec6f", size = 10466716, upload-time = "2026-07-16T15:13:26.162Z" }, + { url = "https://files.pythonhosted.org/packages/1e/cc/44eaaf0844e028182f2d0a8f2190d0f359159aed0a9e5ab861d892f1ae2a/ruff-0.15.22-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:742a29cf29bddb7c8327895d6a10e0e6c5b38a96dd407af9b5d0857f809c0576", size = 10892644, upload-time = "2026-07-16T15:13:29.229Z" }, + { url = "https://files.pythonhosted.org/packages/9f/21/8edf559014d2b0f82beea19cfb713993ad802ccda16868769979c6090a84/ruff-0.15.22-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72af58b951b0ae395935ae79763dc349bc0eb706319d28f7a33ad2cfb3cfc178", size = 10576719, upload-time = "2026-07-16T15:13:32.35Z" }, + { url = "https://files.pythonhosted.org/packages/bf/1e/3a13abd392a3b50b62e5938a831f9ab6e588358cacad5c18545b716d2182/ruff-0.15.22-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d425005c1835eb24e2ee4161cb90e8db263415f4a71c8c72c33abaa6c0c224", size = 11376494, upload-time = "2026-07-16T15:13:35.958Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3e/422d3d95bcf04dd78e1aeac22184d4f9a8fb2c01865d39d44618484a0317/ruff-0.15.22-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8b9b3f8779a4f08c969defc3c8c35abffaa757e601ed5ae66d6d1db6519969a", size = 12208370, upload-time = "2026-07-16T15:13:39.185Z" }, + { url = "https://files.pythonhosted.org/packages/1e/91/5d065a0e0a02bf4813f5119ad278462eed081d2b832eb7c021ade0ec9e65/ruff-0.15.22-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e0dd1b2e4d3d585f897a0d137cbf4eaf6223bef4e8ce34d6bb12556c5f9249e", size = 11581098, upload-time = "2026-07-16T15:13:42.132Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f9/a0d4871d12fae702eb1f41b686caf05f1f8b124dc6db6f784f53d74918fa/ruff-0.15.22-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365523eb91d9224e1bcb03b022fbf0facb8f9e23792a2c53d9d4b3924bdbdebb", size = 11399422, upload-time = "2026-07-16T15:13:45.2Z" }, + { url = "https://files.pythonhosted.org/packages/18/80/c843a5176cddbceb0b7e8dd41cf9993490796c1c469348d384f5a5c13c56/ruff-0.15.22-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:fabfd168afdf29fee5be98b831efa9683c94d7c5a3b58b9ce5a2e38444589a74", size = 11381683, upload-time = "2026-07-16T15:13:48.46Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/8485de0ae92239438a36cfc51350db9b9e85c9ebdfaea91b18e422706662/ruff-0.15.22-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:225dbf095a87f1d9f90f5fd7924d2613ee452a75a4308c63a8f50f761787aa7c", size = 10850295, upload-time = "2026-07-16T15:13:51.655Z" }, + { url = "https://files.pythonhosted.org/packages/fa/91/24977ec2ec72eaf15e4394ace2959fdff2dd1e14f03e005e838023407169/ruff-0.15.22-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1877d63b9d24ed278744f1523fd11b85540566d54641f97c566d7d9dc5ca5296", size = 10579640, upload-time = "2026-07-16T15:13:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/9c/47/9b51216951974df1f263ac19da550d34252e0ed7218c25f10c5ef9ed7517/ruff-0.15.22-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a1606c510bd7215680d32efab38965f7cdec3ef69f5170a3f4791404ffdd5262", size = 11105077, upload-time = "2026-07-16T15:13:57.915Z" }, + { url = "https://files.pythonhosted.org/packages/c2/47/20e9d4a3b8016778acea5fc32bb50d35d207500a17ddb529ffa6996feef8/ruff-0.15.22-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:630479b18625f5ffc373f77603a22a9f8ac0acd7ff0501178b5db28ec71e9c64", size = 11490980, upload-time = "2026-07-16T15:14:01.032Z" }, + { url = "https://files.pythonhosted.org/packages/4d/76/3f72d8fc38c1cb77b38c56a70da9d0c17700cc1cc50f9649c9d3c8f5ba71/ruff-0.15.22-py3-none-win32.whl", hash = "sha256:e5ba0e4a13fd14abbed2a77b517a3911290c6c6c59ef67784328d1668fab76cf", size = 10789165, upload-time = "2026-07-16T15:14:04.16Z" }, + { url = "https://files.pythonhosted.org/packages/cb/46/4965251734c2b6fcdca1b1b187d20bcac3af0ee5b083b89c910bb961ce3a/ruff-0.15.22-py3-none-win_amd64.whl", hash = "sha256:9be63ba1eb936acd2d1342fb8337c356353706fce233b2a15a09a97037e6acde", size = 11938297, upload-time = "2026-07-16T15:14:07.316Z" }, + { url = "https://files.pythonhosted.org/packages/57/c9/e69b1ff4c8b69093ef08b8919ab767af0569666865b39c30a8795d88d3c6/ruff-0.15.22-py3-none-win_arm64.whl", hash = "sha256:e1168075b72158510839f250027659cdd78476f40507dd517892304c41318661", size = 11298172, upload-time = "2026-07-16T15:14:10.51Z" }, ] [[package]] @@ -5398,15 +4548,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, ] -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - [[package]] name = "six" version = "1.17.0" @@ -5418,23 +4559,23 @@ wheels = [ [[package]] name = "slack-bolt" -version = "1.28.0" +version = "1.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "slack-sdk" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/97/a62dde97e84027b252807f2044bed2edcda2d063a5cb0c535fb2be8d9b5d/slack_bolt-1.28.0.tar.gz", hash = "sha256:bfe367d867e8fb157a057248ebd4ac2d7f43acac6d0700fa31381db1e10f3b0f", size = 130768, upload-time = "2026-04-06T23:24:59.936Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/4f/5ba15533d66da2e7174334cc0e2805142e5390c9f4c5f31633df78b17006/slack_bolt-1.30.0.tar.gz", hash = "sha256:af38258d41f801ad9c74503090e0f39accd66c49f667f7e55c97fcdb0e51b886", size = 131180, upload-time = "2026-07-15T20:47:33.679Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/a9/697b6a92c728f09d5ef6b8e83dc6c8a87bc6d59499b2933ed067f11b7e30/slack_bolt-1.28.0-py2.py3-none-any.whl", hash = "sha256:738d1ca5e7c7039b6e18103d29267ced6e18c2517053eff18991fdd593acce5c", size = 234819, upload-time = "2026-04-06T23:24:58.278Z" }, + { url = "https://files.pythonhosted.org/packages/2f/ee/1a7a286cf98fa3f4eeffaabc090e82f58f058ab4812aa1d7421d92c2637a/slack_bolt-1.30.0-py2.py3-none-any.whl", hash = "sha256:81f5bc46e79516d23d5e2a31dded6304dd1b8b6b72c0083f2f31d5d801e262c4", size = 235341, upload-time = "2026-07-15T20:47:32.113Z" }, ] [[package]] name = "slack-sdk" -version = "3.42.0" +version = "3.43.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0e/00/16258bfa547559b2c936b50c882b4f0a36ebf6b69639eb763d8fa5e8d6cb/slack_sdk-3.42.0.tar.gz", hash = "sha256:873db9e1f632ac650ffdbf9d8ba825f3e9e7e576a1e4f9604ccb2a15b3727e3d", size = 252136, upload-time = "2026-05-18T17:50:44.727Z" } +sdist = { url = "https://files.pythonhosted.org/packages/13/75/a4964eb771a0c74d79ee7a3bee6fb5d9718909dd1b675e80d62a6a0ad90a/slack_sdk-3.43.0.tar.gz", hash = "sha256:0553152e46c4259eb69f7464cdadc35ba4802ca10f9f5a849c92cf03d6c2ba07", size = 252769, upload-time = "2026-06-30T18:04:41.59Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/ef/8a1556bd4843443993fc116783790a7cc553601a37f7d965ec26eef95e76/slack_sdk-3.42.0-py2.py3-none-any.whl", hash = "sha256:eb39aff97e476e10cc5a8ac29bd2e79a9959e880d9fe0c03b4e8f05b2ac996ff", size = 315469, upload-time = "2026-05-18T17:50:41.972Z" }, + { url = "https://files.pythonhosted.org/packages/e4/55/42141b8338d46323d5b3c6095201b044c670c20f898643b322ea9b1543a1/slack_sdk-3.43.0-py2.py3-none-any.whl", hash = "sha256:4b6557c65577fc172f685af218b811f9f3b4909e24cddd839ada09565f10c585", size = 315866, upload-time = "2026-06-30T18:04:39.636Z" }, ] [[package]] @@ -5464,113 +4605,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304, upload-time = "2026-05-24T13:55:55.406Z" }, ] -[[package]] -name = "sqlalchemy" -version = "2.0.51" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/02/f1/a7a892f18d4d224e6b26f706531eafccc41e37594d37d304786969ee13cb/sqlalchemy-2.0.51.tar.gz", hash = "sha256:804dccd8a4a6242c4e30ad961e540e18a588f6527202f2d6791b01845d59fdc9", size = 9912201, upload-time = "2026-06-15T15:41:20.012Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/76/b3ea1d8842e7b62c718a88d302809003d65ed82011460ca48907dde658c4/sqlalchemy-2.0.51-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e8203d2fbd5c6254692ef0a72c740d75b2f3c7ca345404f4c1a4604813c77c0", size = 2162087, upload-time = "2026-06-15T16:05:15.795Z" }, - { url = "https://files.pythonhosted.org/packages/6c/22/f19552eb7876774d50cfd025337ef5d67acc10cd8f29adab7716cf47c352/sqlalchemy-2.0.51-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1af05726b3d0cdba1c55284bf408fd3b792e690fe2399bfb8304565551cda652", size = 3244579, upload-time = "2026-06-15T16:10:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/fc/97/e4a2eb5a8ec5cd3c2a0615a2f15f0afca89ac039229599b9ed0c0ed28e5e/sqlalchemy-2.0.51-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e54ff2dd657f2e3e0fbf2b097db1182f7bfea263eca4353f00065bae2a67c3d", size = 3243515, upload-time = "2026-06-15T16:12:22.627Z" }, - { url = "https://files.pythonhosted.org/packages/74/c6/5900ec624fab3360aa2ec59b99bb2046dd79799e310bb78a0514eaa4038e/sqlalchemy-2.0.51-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1e47b1199c2e832e325eacabc8d32d2487f58c9358f97e9a00f5eb93c5680d84", size = 3195492, upload-time = "2026-06-15T16:10:38.097Z" }, - { url = "https://files.pythonhosted.org/packages/8f/41/2ee3c4e1ac4fd22309349823fe13f33febeab1a71db1d7e9d60293a07dcb/sqlalchemy-2.0.51-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c68568f3facf8f66fa76c60e0ced69b67666ffa9941d1d0a3756fda196049080", size = 3215782, upload-time = "2026-06-15T16:12:24.051Z" }, - { url = "https://files.pythonhosted.org/packages/ce/1c/3bd72c341f1cb5faed5a7457ea840228a46be51cfbaf31a9db72fc963f11/sqlalchemy-2.0.51-cp310-cp310-win32.whl", hash = "sha256:0592bdadf86ddcabfd72d9ab66ea8a5d8d2cc6be1cc51fa7e66c03868ac5eac1", size = 2122119, upload-time = "2026-06-15T16:13:26.915Z" }, - { url = "https://files.pythonhosted.org/packages/2a/63/b6dfdd646abf91c3bedb13727226a5e765e5f8365e898d43818e6672fa46/sqlalchemy-2.0.51-cp310-cp310-win_amd64.whl", hash = "sha256:740cf6f35351b1ac3d82369152acf1d51d37e3dcf85d4dc0a22ca01410eabe2a", size = 2145158, upload-time = "2026-06-15T16:13:28.386Z" }, - { url = "https://files.pythonhosted.org/packages/3a/69/a67c69e5f28fc9c99d6f7bd60bd50e91f2fed2423e3b30fb228fa00e51f3/sqlalchemy-2.0.51-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1aa10c0daee6705294d181daadaa793221e1a59ed55000a3fab1d42b088ce4ba", size = 2161838, upload-time = "2026-06-15T16:05:17.144Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a4/c8c22b8438bddc0a030157c6ec0f6ef97b3c38effa444bdab2a27af04090/sqlalchemy-2.0.51-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5b2ed6d828f1f09bd812861f4f59ca3bc3803f9df871f4555187f0faf018604", size = 3319402, upload-time = "2026-06-15T16:10:40.002Z" }, - { url = "https://files.pythonhosted.org/packages/90/54/44012d32fd77d991256d2ff793ba3807c51d40cb27a85b4796224f6744df/sqlalchemy-2.0.51-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:436728ce18a80f6951a1e11cc6112c2ede9faf20766f1a26195a7c441ca12dbd", size = 3319675, upload-time = "2026-06-15T16:12:25.658Z" }, - { url = "https://files.pythonhosted.org/packages/29/a5/de0592acaf5906cd7430874392d6f7e8b4a7c8437610953ee2d1501c0b44/sqlalchemy-2.0.51-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dc261707bf5739aea8a541593f3cc1d463c2701fb05fbcbba0ce031b69a21260", size = 3270777, upload-time = "2026-06-15T16:10:42.125Z" }, - { url = "https://files.pythonhosted.org/packages/cb/14/a44c90739c780b362238e4ac3cb19dd0ca40d13e6ddc5daa112166ddab4f/sqlalchemy-2.0.51-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a6d26094615306d116dd5e4a51b0304c99dd2356fc569eed6922a80a6bd3b265", size = 3293940, upload-time = "2026-06-15T16:12:27.156Z" }, - { url = "https://files.pythonhosted.org/packages/65/eb/fbd0f206a330e66f8c602a99c37c4e731f107faed62954b41b01f16dd9d9/sqlalchemy-2.0.51-cp311-cp311-win32.whl", hash = "sha256:ca8435d13829b92f4a97362d91975154a4015db3a2634154e1754e9a915e6b86", size = 2121183, upload-time = "2026-06-15T16:13:29.905Z" }, - { url = "https://files.pythonhosted.org/packages/ad/fd/005bf80f3cf6e5c62b5dd68616280f51cd012c60840fa74781b3ed7b1623/sqlalchemy-2.0.51-cp311-cp311-win_amd64.whl", hash = "sha256:4a011ea4510683319ce4ed274b56ee05194b39b6da9d09ca7a39388f0fa84dcc", size = 2145796, upload-time = "2026-06-15T16:13:31.283Z" }, - { url = "https://files.pythonhosted.org/packages/d5/70/e868bc5412acd101a8280f25c95f10eeae0771c4eb806b02491142810ee8/sqlalchemy-2.0.51-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d78702b26ba1c18b2d0fb2ea940ba7f17a9581b42e8361ff93920ebbee1235a", size = 2160291, upload-time = "2026-06-15T16:08:48.918Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1c/71ee0f8a6b9d7316a1ccd30430b4c62b6c2e36adc96017a4e3a72dce49d6/sqlalchemy-2.0.51-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581921d849d6e6f994d560389192955e80e2950e18fcdfe2ccea863e01158e6e", size = 3343835, upload-time = "2026-06-15T16:19:42.613Z" }, - { url = "https://files.pythonhosted.org/packages/2b/7c/7ab9f9aadc5944fdd06612484ed7918fe376ad871a5f50404dc1536e0194/sqlalchemy-2.0.51-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d21ce524ab86c23046e992a5b81cb54c21079c6df6e78b8fc77d77cac70a6b9", size = 3358470, upload-time = "2026-06-15T16:26:38.011Z" }, - { url = "https://files.pythonhosted.org/packages/d0/7d/ff77169fee6186de145a7f2b87006c39638391130abbab2b1f63ac6ea583/sqlalchemy-2.0.51-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c5d98a2709840027f5a347c3af0a7c3d5f6c1ff93af2ca1c54494e23cba8f389", size = 3289874, upload-time = "2026-06-15T16:19:45.212Z" }, - { url = "https://files.pythonhosted.org/packages/6f/3b/6c505903710d781b55bc3141ee34a062bf9745a6b5bc7333305b9ed63b33/sqlalchemy-2.0.51-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1181256e0f16479691b5616d36375dc2620ad8332b25978763c3d206ad3f3f1d", size = 3321692, upload-time = "2026-06-15T16:26:39.747Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b7/c5ffe50aa2f4d947c9250e1519d939260329a07fe6272edfccd784b3d007/sqlalchemy-2.0.51-cp312-cp312-win32.whl", hash = "sha256:9f380393be5abeb6815f68fd39271b95127173511b6706b0a630a9995d53f8f5", size = 2119674, upload-time = "2026-06-15T16:23:09.543Z" }, - { url = "https://files.pythonhosted.org/packages/25/dc/46a65916af68a06ef6b972c6050ba4c8f97070fe3fb33097d34229d9bef6/sqlalchemy-2.0.51-cp312-cp312-win_amd64.whl", hash = "sha256:2cf39aabdf48e87c1c2c2ed6d20d33ffa0733b3071ce9c5f66357947dd009080", size = 2146670, upload-time = "2026-06-15T16:23:11.048Z" }, - { url = "https://files.pythonhosted.org/packages/54/fe/a210d52fd1a90ecfae8a78e9d8b27e18d733d60818a8bf250ff690b75120/sqlalchemy-2.0.51-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c2056838b6685b72fdb36c99996cf862753461a62f2e84f4196371d3b2d6a07", size = 2157184, upload-time = "2026-06-15T16:08:50.374Z" }, - { url = "https://files.pythonhosted.org/packages/17/6b/2dce8369b199cb855110e056032f94a9f66dacc2237d3d39c115a86eac56/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:483b11bd46bf35fc14c52faf338b04300c9e6ce554bce9b11be85bfec3bc3195", size = 3284735, upload-time = "2026-06-15T16:19:46.934Z" }, - { url = "https://files.pythonhosted.org/packages/53/ff/dbc495b8a14da840faffb353857a72d4190113cac33727906fb997047f0f/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1bed1ee8b01da6088210aa9412023326fb98a599ba502e6118308601dcbef77f", size = 3302756, upload-time = "2026-06-15T16:26:41.336Z" }, - { url = "https://files.pythonhosted.org/packages/cf/d5/fde8f4dddcf518ee15ab35a7c6a28acc32c8ba548d1d2aa451f96e6dbb0b/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:72ca54c952107ba5cd58854b67a5a6268631289d21651a1235396f3b98b47400", size = 3232055, upload-time = "2026-06-15T16:19:49.286Z" }, - { url = "https://files.pythonhosted.org/packages/67/d1/43d3a0ac955a58601c24fa23038b1c55ee3a1ec02c0f96ebb1eae2bcf614/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b3e693d15533a45cd5906f0589f9c35090bef6ef45bf1e8195c424aa0ae06a8d", size = 3269850, upload-time = "2026-06-15T16:26:43.017Z" }, - { url = "https://files.pythonhosted.org/packages/94/df/de669c7054cd47c4439ac34b1b2ee8b804a794791fbb10720e997a2c87c7/sqlalchemy-2.0.51-cp313-cp313-win32.whl", hash = "sha256:b93ab07b5292dbe7e6b8da89475275e7042744283921344b56105f3eeb0f828b", size = 2117721, upload-time = "2026-06-15T16:23:12.36Z" }, - { url = "https://files.pythonhosted.org/packages/d0/8a/403c51d064196bae20a0bc2476577f83a3f8dd299719a97417086b7f2ec5/sqlalchemy-2.0.51-cp313-cp313-win_amd64.whl", hash = "sha256:0f053118c30e53161857a953e4de667d90e274980dccbe5dd3829bbbeece72a5", size = 2143615, upload-time = "2026-06-15T16:23:13.906Z" }, - { url = "https://files.pythonhosted.org/packages/b1/49/a739be2e1d02a96a658eb71ab45d921c874249252358ad24a5bffdd02525/sqlalchemy-2.0.51-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6ea306caaae6bd5afd0a46050003c88f6bf33227377a49298c498c3cb88ff491", size = 2158999, upload-time = "2026-06-15T16:08:51.759Z" }, - { url = "https://files.pythonhosted.org/packages/23/6b/2e0e38cf75c8780eca78d9b2e78164f8bcfd70125e5caa588ff5cbb9c9f4/sqlalchemy-2.0.51-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c45a496d6bc05dec41dcd4c3a2b183723f47473255c159cd80b503c8f246424d", size = 3282539, upload-time = "2026-06-15T16:19:51.065Z" }, - { url = "https://files.pythonhosted.org/packages/dd/a1/e77854cb5336fd37dc3c6ae3b71de242c98caac5725120be0b526b31cbd0/sqlalchemy-2.0.51-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4004ada0aafe8ae1991b2cd1d99c6d9146126e123bd6f883c260d974aa012e54", size = 3287545, upload-time = "2026-06-15T16:26:44.735Z" }, - { url = "https://files.pythonhosted.org/packages/f6/ab/9e17272fd4dac8df3b83c4fbe52b998a1c9d89a843c8c35ff29b74ff7364/sqlalchemy-2.0.51-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f6bcad487aee1c638d707235682fc96f741de00663619881ab235400d03289e", size = 3230929, upload-time = "2026-06-15T16:19:52.625Z" }, - { url = "https://files.pythonhosted.org/packages/02/3c/52f408ea701781caee975606beccc48845f2aee8711ac29843d612c0306c/sqlalchemy-2.0.51-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:39a76529db6305693d8d4affa58ad5b5e2e18edd62daea628b29b97930b3513d", size = 3252888, upload-time = "2026-06-15T16:26:46.454Z" }, - { url = "https://files.pythonhosted.org/packages/24/16/3efd2ee6bc4ca4693a30a1dd17a91b606cae15d517d2a4746611d9b73ce8/sqlalchemy-2.0.51-cp314-cp314-win32.whl", hash = "sha256:08a204d8b5638717c26a24df18fcf40af45a6b22e35b70b1d62f0113c2e278e8", size = 2120551, upload-time = "2026-06-15T16:23:15.629Z" }, - { url = "https://files.pythonhosted.org/packages/7b/78/55b12e70f45bccc40d9e483925c065027b3b98ea4cbbdf6f8c2546feaf6c/sqlalchemy-2.0.51-cp314-cp314-win_amd64.whl", hash = "sha256:96747bfbadb055466e5b46d572618170046b45ce5a4879167f50d70a5319a499", size = 2146318, upload-time = "2026-06-15T16:23:17.108Z" }, - { url = "https://files.pythonhosted.org/packages/21/db/a9574ed40fed418924b1b1a3e54f47ee3963053b3d3d325a0d36b41f2c08/sqlalchemy-2.0.51-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1a213be1fcd5e49d9904c3b9939211ded90bc2a64e93f4c01963474285de", size = 2178920, upload-time = "2026-06-15T15:59:56.285Z" }, - { url = "https://files.pythonhosted.org/packages/bf/90/a1bb5c7cbba76b7bc1fbd586d0a5479a7bc9c27b4a8298f22ec9423b2bb3/sqlalchemy-2.0.51-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c6b36ed71f41942bdcd2ad2522be46bfce09d5705be5640ecf19bbc7660e4b7", size = 3566534, upload-time = "2026-06-15T15:58:35.024Z" }, - { url = "https://files.pythonhosted.org/packages/15/4b/481f1fed30e0e9e8dd24aecbb49f29eb57fe7657ece5cf06ee9b84bb97d8/sqlalchemy-2.0.51-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c2c62877097e1a0db401fba5cb4debee33265e5b2a55c4ccb489c02c53b4f72", size = 3535844, upload-time = "2026-06-15T16:02:43.973Z" }, - { url = "https://files.pythonhosted.org/packages/02/71/0aa64aeda645510af0a43f7d9ee70932f0d1dc4263aed34c50ee891d9df3/sqlalchemy-2.0.51-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0378d055e9e8cd6ce4d8dff683bdd3d7d413533c4ee51d67a2b1e0f9eacc0f23", size = 3475355, upload-time = "2026-06-15T15:58:36.592Z" }, - { url = "https://files.pythonhosted.org/packages/05/db/6061db32316446135a3abae5f308d144ab988a34234726042da3e58b1c63/sqlalchemy-2.0.51-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6e46fc36029eff666391e0531e5387b62ce6c4f1d8e50b3fb3099eaca1b42522", size = 3486591, upload-time = "2026-06-15T16:02:45.346Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c9/f14fdf71bb8957e0c7e39db69bbdf12b5c80f4ef775fdfa127bf4e0d6760/sqlalchemy-2.0.51-cp314-cp314t-win32.whl", hash = "sha256:9161cfc9efce70d1715f47d6ff40f79c6778c00d53be4fbc09d70301e4b83ba7", size = 2151313, upload-time = "2026-06-15T16:03:39.127Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c6/673e618e6f4f297e126d9b56ea2f6478708f6c1af4e3223835c22e2c3697/sqlalchemy-2.0.51-cp314-cp314t-win_amd64.whl", hash = "sha256:159bb6ba32059f57ad7375a8f50d844dd2f19d14954ecf820cd33e20debd46b2", size = 2186280, upload-time = "2026-06-15T16:03:40.569Z" }, - { url = "https://files.pythonhosted.org/packages/e2/22/dbf013a12ec759e54a34a119e9e217435b3f71b2dd5c61a7ade0a25dae87/sqlalchemy-2.0.51-py3-none-any.whl", hash = "sha256:bb024d8b621d0be75f4f44ecc7c950450026e76d66dc8f791bb5331d7fed59d5", size = 1944334, upload-time = "2026-06-15T16:09:22.418Z" }, -] - -[[package]] -name = "sqlalchemy-spanner" -version = "1.19.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "alembic" }, - { name = "google-cloud-spanner" }, - { name = "sqlalchemy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/af/b6/ce05f1b8a9c486bbac26d7348625c78ba6e751decc25009f28880504c29d/sqlalchemy_spanner-1.19.0.tar.gz", hash = "sha256:834cec66fb418e5085a44c68cee570c594c66dd8535b67dd5e8be3571d172136", size = 82914, upload-time = "2026-06-03T16:14:49.721Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/38/8150a0022174d02956b0f6b586777006af2fc794b1baa72748a11fde039f/sqlalchemy_spanner-1.19.0-py3-none-any.whl", hash = "sha256:3367a89388d9b7106111fc48c7fac441163602c414ad157f62e18b5705cc760e", size = 31919, upload-time = "2026-06-03T16:13:39.522Z" }, -] - -[[package]] -name = "sqlparse" -version = "0.5.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, -] - [[package]] name = "sse-starlette" -version = "3.4.4" +version = "3.4.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "starlette" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f7/2b/58abc2d1fd397e7dde08e947e05c884d8ef2f78d5e2588c17a12d42d6994/sse_starlette-3.4.4.tar.gz", hash = "sha256:07e0fa0460138baf25cdd5fb28683472c3995dc1642225191b3832d62526bcb0", size = 31819, upload-time = "2026-05-12T17:37:17.019Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d2/1b/bc9e3e7a72dcdad7dc7888758f5d00f56f8909ed5cfdff822bd72bb4c520/sse_starlette-3.4.5.tar.gz", hash = "sha256:83072538bc211a2f68b7b0422226c4af3e9b62e106e07034664b832ca019842a", size = 35249, upload-time = "2026-06-20T17:36:58.322Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/67/805710444ea8cc75fbf70b920ed431a560c4bf9c57f7d5a3117213189399/sse_starlette-3.4.4-py3-none-any.whl", hash = "sha256:3f4dd50d8aed2771a091f3a83000323fc3844541c16b4fe585ae2420cc6df973", size = 16514, upload-time = "2026-05-12T17:37:15.601Z" }, + { url = "https://files.pythonhosted.org/packages/78/75/c88d3f5dafd59c791da1ce27650d30bf5b70cbf1cbf01cd00e5f9e360915/sse_starlette-3.4.5-py3-none-any.whl", hash = "sha256:e71bad53323f65573c3864a6c3bd0c1eb6e5f092b2e48082b0c35927d19ca296", size = 16518, upload-time = "2026-06-20T17:36:56.729Z" }, ] [[package]] name = "starlette" -version = "0.52.1" +version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bb/2799cc2ede3ed41131f8975621e7213dfc7ef4acbbaadfa440f32500c370/starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6", size = 73632, upload-time = "2026-06-12T09:23:10.017Z" }, ] [[package]] name = "strands-agents" -version = "1.43.0" +version = "1.47.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "boto3" }, @@ -5586,14 +4649,14 @@ dependencies = [ { name = "typing-extensions" }, { name = "watchdog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/e7/ba9faab3ebaa63325ef03b61a74806bc5ccb6b626428541f898b4f33fb21/strands_agents-1.43.0.tar.gz", hash = "sha256:379ad28af36d9306c7ae3f43702b086082193e8eafa53de051c9ce91496178ac", size = 922114, upload-time = "2026-06-12T14:27:57.069Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/05/5eb8431ff340739b1c21b0da7d19ec9604b9c4953a1c4638df915321573c/strands_agents-1.47.0.tar.gz", hash = "sha256:97770cb6beb6e5fd1a58849f41201eb7edb43fd67ad3de6544ada2f342c2bcf0", size = 1157139, upload-time = "2026-07-10T14:45:05.809Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/35/29fc4c02293aef54dfa59a5516419153a9fb15a4857ecf8f2ce8638ae3dd/strands_agents-1.43.0-py3-none-any.whl", hash = "sha256:b934f74fe1b7103d438684b69ee044223a5bb407db2ccd2b55e5da6bf639d31b", size = 472542, upload-time = "2026-06-12T14:27:55.55Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cb/ceae892c5823bea9254160efc02a4553f2ced9d183676110c03c3a9ff0f3/strands_agents-1.47.0-py3-none-any.whl", hash = "sha256:1f6ce17404ff02079244ad7a4a180a2a7150546275e4b91187d71472ba8fe3f2", size = 600611, upload-time = "2026-07-10T14:45:03.942Z" }, ] [[package]] name = "strands-agents-tools" -version = "0.8.0" +version = "0.8.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -5614,9 +4677,9 @@ dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, { name = "watchdog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/74/aed74502a19a18ce1ddcd56e1dc4a0de6e9f2cb6babcc9f2d1969f253c0b/strands_agents_tools-0.8.0.tar.gz", hash = "sha256:fd93104d2d8dcff780505e8a2fca0cb2fa7a3da6bae01369073b60da0e08c5aa", size = 490638, upload-time = "2026-06-03T19:20:03.872Z" } +sdist = { url = "https://files.pythonhosted.org/packages/17/dd/aaa1f17ec10db4a9f49e2e48bd3cd5fb646c1c284e4fe6fa0a6a32380914/strands_agents_tools-0.8.3.tar.gz", hash = "sha256:19f6d3d617e9ef85b0a31c475f0a3f81d8ea60a2ed7dc6bd8f17fdd586c94d46", size = 511310, upload-time = "2026-07-09T19:55:59.262Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/37/9f611363451cf38f912c7551b73308339e6af54bf1b5e9a3e8efc7ae0972/strands_agents_tools-0.8.0-py3-none-any.whl", hash = "sha256:7446ae423794b6f886fb36e1a0f8a62fe0546978c18b805e6f50dcdddee1559e", size = 319602, upload-time = "2026-06-03T19:20:01.92Z" }, + { url = "https://files.pythonhosted.org/packages/b1/8b/1500ae455cc8434a54f013278320042d88ba9daffb5dafaed1fa8b48c3f3/strands_agents_tools-0.8.3-py3-none-any.whl", hash = "sha256:bc17fbad5ad7957bef33538fb5e0c386f7883dab92c557e92175e7ff16b42dac", size = 327194, upload-time = "2026-07-09T19:55:57.473Z" }, ] [[package]] @@ -5633,7 +4696,7 @@ wheels = [ [[package]] name = "temporalio" -version = "1.29.0" +version = "1.31.0" source = { virtual = "." } dependencies = [ { name = "nexus-rpc" }, @@ -5648,8 +4711,17 @@ aioboto3 = [ { name = "aioboto3" }, { name = "types-aioboto3", extra = ["s3"] }, ] +deepagents = [ + { name = "deepagents", marker = "python_full_version >= '3.11'" }, + { name = "langchain", marker = "python_full_version >= '3.11'" }, + { name = "langchain-core", marker = "python_full_version >= '3.11'" }, +] google-adk = [ { name = "google-adk" }, + { name = "mcp" }, +] +google-genai = [ + { name = "google-genai" }, ] grpc = [ { name = "grpcio" }, @@ -5687,13 +4759,19 @@ dev = [ { name = "async-timeout", marker = "python_full_version < '3.11'" }, { name = "basedpyright" }, { name = "cibuildwheel" }, + { name = "cryptography" }, + { name = "deepagents", marker = "python_full_version >= '3.11'" }, { name = "googleapis-common-protos" }, { name = "grpcio-tools" }, { name = "httpx" }, + { name = "langchain", marker = "python_full_version >= '3.11'" }, + { name = "langchain-anthropic", marker = "python_full_version >= '3.11'" }, + { name = "langchain-core", marker = "python_full_version >= '3.11'" }, { name = "langgraph" }, { name = "langsmith" }, { name = "litellm" }, { name = "maturin" }, + { name = "mcp" }, { name = "moto", extra = ["s3", "server"] }, { name = "mypy" }, { name = "mypy-protobuf" }, @@ -5727,10 +4805,15 @@ dev = [ [package.metadata] requires-dist = [ { name = "aioboto3", marker = "extra == 'aioboto3'", specifier = ">=10.4.0" }, - { name = "google-adk", marker = "extra == 'google-adk'", specifier = ">=1.27.0,<2" }, + { name = "deepagents", marker = "python_full_version >= '3.11' and extra == 'deepagents'", specifier = ">=0.6.12,<0.7" }, + { name = "google-adk", marker = "extra == 'google-adk'", specifier = ">=2.2.0,<3" }, + { name = "google-genai", marker = "extra == 'google-genai'", specifier = ">=2.10.0,<3.0.0" }, { name = "grpcio", marker = "extra == 'grpc'", specifier = ">=1.48.2,<2" }, + { name = "langchain", marker = "python_full_version >= '3.11' and extra == 'deepagents'", specifier = ">=1.3.11,<2" }, + { name = "langchain-core", marker = "python_full_version >= '3.11' and extra == 'deepagents'", specifier = ">=1.4.8,<2" }, { name = "langgraph", marker = "extra == 'langgraph'", specifier = ">=1.1.0" }, { name = "langsmith", marker = "extra == 'langsmith'", specifier = ">=0.7.34,<0.9" }, + { name = "mcp", marker = "extra == 'google-adk'", specifier = ">=1.24,<2" }, { name = "mcp", marker = "extra == 'openai-agents'", specifier = ">=1.9.4,<2" }, { name = "nexus-rpc", specifier = "==1.4.0" }, { name = "openai-agents", marker = "extra == 'openai-agents'", specifier = ">=0.17.5" }, @@ -5749,20 +4832,26 @@ requires-dist = [ { name = "types-protobuf", specifier = ">=3.20,<8.0.0" }, { name = "typing-extensions", specifier = ">=4.2.0,<5" }, ] -provides-extras = ["grpc", "opentelemetry", "pydantic", "openai-agents", "google-adk", "langgraph", "langsmith", "lambda-worker-otel", "aioboto3", "strands-agents"] +provides-extras = ["grpc", "opentelemetry", "pydantic", "openai-agents", "google-adk", "langgraph", "langsmith", "deepagents", "lambda-worker-otel", "aioboto3", "google-genai", "strands-agents"] [package.metadata.requires-dev] dev = [ { name = "async-timeout", marker = "python_full_version < '3.11'", specifier = ">=4.0,<6" }, { name = "basedpyright", specifier = "==1.34.0" }, { name = "cibuildwheel", specifier = ">=2.22.0,<3" }, + { name = "cryptography", specifier = ">=46" }, + { name = "deepagents", marker = "python_full_version >= '3.11'", specifier = ">=0.6.12,<0.7" }, { name = "googleapis-common-protos", specifier = ">=1.75.0,<2" }, { name = "grpcio-tools", specifier = ">=1.48.2,<2" }, { name = "httpx", specifier = ">=0.28.1" }, + { name = "langchain", marker = "python_full_version >= '3.11'", specifier = ">=1.3.11,<2" }, + { name = "langchain-anthropic", marker = "python_full_version >= '3.11'", specifier = ">=1.4.7" }, + { name = "langchain-core", marker = "python_full_version >= '3.11'", specifier = ">=1.4.8,<2" }, { name = "langgraph", specifier = ">=1.1.0" }, { name = "langsmith", specifier = ">=0.7.34,<0.9" }, { name = "litellm", specifier = ">=1.83.0" }, { name = "maturin", specifier = ">=1.8.2" }, + { name = "mcp", specifier = ">=1.9.4,<2" }, { name = "moto", extras = ["s3", "server"], specifier = ">=5" }, { name = "mypy", specifier = "==1.18.2" }, { name = "mypy-protobuf", specifier = ">=3.3.0,<4" }, @@ -5955,14 +5044,14 @@ wheels = [ [[package]] name = "tqdm" -version = "4.68.2" +version = "4.68.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/05/0d5260f1f1ca784f4a4a0def9cbe6affe587f5b4025328d446c3d67765f4/tqdm-4.68.2.tar.gz", hash = "sha256:89c230e8dbc67c7615c142487111222f878c77427ea09549960f62389e258add", size = 171923, upload-time = "2026-06-09T13:26:42.539Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/5f/57ff8b434839e70dab45601284ea413e947a63799891b7553e5960a793a8/tqdm-4.68.4.tar.gz", hash = "sha256:19829c9673638f2a0b8617da4cdcb927e831cd88bcfcb6e78d42a4d1af131520", size = 792418, upload-time = "2026-07-07T09:58:18.369Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/75/1a0392bcc21c44dcdf87b3cf2d137e7829be2c083a1e38d44efca3d57a16/tqdm-4.68.2-py3-none-any.whl", hash = "sha256:d4240441fb5353290b87d6a85968c9decc131a99b8c7faa28269d829de669ede", size = 78578, upload-time = "2026-06-09T13:26:40.731Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/5e5e750890ada51017d18d0d4c30da696e5b5bd3180947729927628fc3cb/tqdm-4.68.4-py3-none-any.whl", hash = "sha256:5168118b2368f48c561afda8020fd79195b1bdb0bdf8086b88442c267a315dc2", size = 676612, upload-time = "2026-07-07T09:58:16.256Z" }, ] [[package]] @@ -6003,21 +5092,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a6/57/bcf4e2370dd218c9aa68a9140a65d86729c73f1d529f7e94786c2766fc72/twisted-26.4.0-py3-none-any.whl", hash = "sha256:dc25ea0ebf6511c24f03232ee9f4afa54b291c5d897990e3a39cc4d14a1ef4c0", size = 3230362, upload-time = "2026-05-11T11:24:49.5Z" }, ] -[[package]] -name = "typer" -version = "0.25.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-doc" }, - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e4/51/9aed62104cea109b820bbd6c14245af756112017d309da813ef107d42e7e/typer-0.25.1.tar.gz", hash = "sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc", size = 122276, upload-time = "2026-04-30T19:32:16.964Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409, upload-time = "2026-04-30T19:32:18.271Z" }, -] - [[package]] name = "types-aioboto3" version = "15.5.0" @@ -6092,11 +5166,11 @@ wheels = [ [[package]] name = "typing-extensions" -version = "4.15.0" +version = "4.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, ] [[package]] @@ -6113,32 +5187,23 @@ wheels = [ [[package]] name = "tzdata" -version = "2026.2" +version = "2026.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ff/5a28bdfd8c3ebec42564ac7d0e54ca3db65044a9314a97f9564fa7a1e926/tzdata-2026.3.tar.gz", hash = "sha256:4a1518b8993086a7982523e071643f3c0e5f213e75b21318e78bcabfff9d1415", size = 198674, upload-time = "2026-07-10T08:50:37.887Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, + { url = "https://files.pythonhosted.org/packages/e5/6d/b53b99a9f2766d095985947a5782f1702cabb129a34f7a802d7197af832f/tzdata-2026.3-py2.py3-none-any.whl", hash = "sha256:dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931", size = 348168, upload-time = "2026-07-10T08:50:36.46Z" }, ] [[package]] name = "tzlocal" -version = "5.4" +version = "5.4.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d8/52/ee2e6d7031687c5bad28363148cb72f2bbf38201d2e220671bd9fb830bc2/tzlocal-5.4.tar.gz", hash = "sha256:41e1293f80d4b5ff38dff222601a8fbd06b4fdcaf25e224704047ad26a39af54", size = 30922, upload-time = "2026-06-15T12:06:56.594Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/5b/879b2f932adfa7a053c360d50bc896c977fa6426109185f7c12ebdd0cb9d/tzlocal-5.4.4.tar.gz", hash = "sha256:8dbb8660838688a7b6ba4fed31d18dedf842afb4d47ca050d6d891c2c15f3be4", size = 31170, upload-time = "2026-06-29T08:03:40.026Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/70/5771c9ecbdb7cc0c3f3bbded7e0fa7911ee8e872ce5b5dc48ce7dce21a11/tzlocal-5.4-py3-none-any.whl", hash = "sha256:024d11221ff83453eae1f608f09b145b9779e1345d08c15404ce8ff7917cf629", size = 28261, upload-time = "2026-06-15T12:06:54.914Z" }, -] - -[[package]] -name = "uritemplate" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/60/f174043244c5306c9988380d2cb10009f91563fc4b31293d27e17201af56/uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e", size = 33267, upload-time = "2025-06-02T15:12:06.318Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/99/3ae339466c9183ea5b8ae87b34c0b897eda475d2aec2307cae60e5cd4f29/uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686", size = 11488, upload-time = "2025-06-02T15:12:03.405Z" }, + { url = "https://files.pythonhosted.org/packages/9e/a4/017a7a6cbe387d961a688ec31364ae60a5c4e22c96ae9921b79a947c855d/tzlocal-5.4.4-py3-none-any.whl", hash = "sha256:aae09f0126a8a86fa736be266eb4a471380d26a0de3bc14844e7821fee3e2a15", size = 18115, upload-time = "2026-06-29T08:03:38.666Z" }, ] [[package]] @@ -6152,129 +5217,117 @@ wheels = [ [[package]] name = "uuid-utils" -version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/a1/822ceef22d1c139cffebe4b1b660cfaa10253d5c770aa2598dc8e9497593/uuid_utils-0.16.0.tar.gz", hash = "sha256:d6902d4375dfba4c9902c736bb82d3c040417b67f7d0fa48910ddfdb1ac95de7", size = 42596, upload-time = "2026-05-19T07:44:23.28Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/78/fc830a25597001586770f0436a4917aac21fcdaf7ac2824bbe168ccdc724/uuid_utils-0.16.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a632fead2a6505a8df3318d5e95503739b9aa1c518521cd93d83ce00699b78f8", size = 566691, upload-time = "2026-05-19T07:45:14.2Z" }, - { url = "https://files.pythonhosted.org/packages/10/39/3f1eee6d3c3c33d6dd75441bdb49ac246de57f97f67faa7ff04cdb5e4ffe/uuid_utils-0.16.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d716e5b35266400d2a2cd349697868179825f113c543e55c9d2ac304991f8d4f", size = 291039, upload-time = "2026-05-19T07:45:52.28Z" }, - { url = "https://files.pythonhosted.org/packages/c6/85/f7fb16eed216fd8085d62d4ce7179e2a81ac7649e043f34168e7700b6df4/uuid_utils-0.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:207c2a98ca8b065cc93378a3a59744efb88a68e9ecc2c3afefe43d59c864280a", size = 327880, upload-time = "2026-05-19T07:44:28.611Z" }, - { url = "https://files.pythonhosted.org/packages/06/ea/b2b629d29c8234677850e1ae47add9c8866dfb3864af257542989a13ba1b/uuid_utils-0.16.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:79824850330e450c7b2fa933572e32192240060937426052fa3fc05134ed3faa", size = 334090, upload-time = "2026-05-19T07:44:57.354Z" }, - { url = "https://files.pythonhosted.org/packages/1b/8e/a6871c6231244bb80be06a2babf3ca34396b29d893103d84ddfd3654e6e4/uuid_utils-0.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d89927c47e1a55509e90b7f2fd3e7ff89908c77b61f8f0deda97a89d8854e0f8", size = 448558, upload-time = "2026-05-19T07:45:03.986Z" }, - { url = "https://files.pythonhosted.org/packages/bf/d0/b606a2857f98c20c149044e80f276ff7966c9f679fc7b25f6d608bd8d48b/uuid_utils-0.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7ae4168e1ca0ae69d24207645a8b3cd2b641a0ad15058eda17d2c9898aa89d3", size = 327733, upload-time = "2026-05-19T07:43:40.129Z" }, - { url = "https://files.pythonhosted.org/packages/2d/e1/7951dd47b6717b6ebb340e673d31d539be928d280a697fab4dd233bcc7fa/uuid_utils-0.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d363017a3223de3a57eb6fca135df6ffcef7c534836bff2e71354dce7d10987c", size = 353659, upload-time = "2026-05-19T07:44:03.551Z" }, - { url = "https://files.pythonhosted.org/packages/a2/5d/f46e91fad5f049c7bd12701293c1ac31b4460ec83606c4bdd37c05abef52/uuid_utils-0.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4a87a7433b355eadaa200f150da6bb5b87bb6de0adf260883b26cb637aba0410", size = 504509, upload-time = "2026-05-19T07:44:34.147Z" }, - { url = "https://files.pythonhosted.org/packages/f4/94/ea4f559e5e87da5847ecf78ba68a78e8bb4e537e1169093ea543cab94886/uuid_utils-0.16.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6da070e75b0e2424728e6f8547647cce36c83f9a6101a08da4849a8ab2b58105", size = 609358, upload-time = "2026-05-19T07:44:39.711Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/60dbac2459426a925b77e08cb8ec492d4bc82caa0f124f498d2e24409cb8/uuid_utils-0.16.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1baab8966f9e0097cbaf9cc01ad448b38e616e7b4968ca5e49cb53a74ad91a2f", size = 569428, upload-time = "2026-05-19T07:44:46.025Z" }, - { url = "https://files.pythonhosted.org/packages/e8/90/ae39c1e1bff65dfe9c7c70cbd64b8d529a3d1cc836aeaa7accdc44e5c308/uuid_utils-0.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b42014536943c1a654ff107538c0f7dc39809d8d774ec8dafd19bec05006e568", size = 532465, upload-time = "2026-05-19T07:44:05.127Z" }, - { url = "https://files.pythonhosted.org/packages/03/5c/4dc93017a095c9c314525a9abc4f9983e520d88d7eff9bd52398d81c374e/uuid_utils-0.16.0-cp310-cp310-win32.whl", hash = "sha256:228701ab6f188b6def24f2add6db64f0794adb1f06d0abacdcec40b0cda13cdf", size = 171162, upload-time = "2026-05-19T07:44:58.518Z" }, - { url = "https://files.pythonhosted.org/packages/43/df/1398f5b117d5daa4d757b156728db7aa092a3eff1271c40ec39dbe945327/uuid_utils-0.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:10d3c5983f770b1b2847ad811c87a1c9e28f8155d1a27cc581abcd5abb386b64", size = 176927, upload-time = "2026-05-19T07:44:54.93Z" }, - { url = "https://files.pythonhosted.org/packages/24/24/0e18177e2fbb0b9f54f90fd48fe3302dfda731e22ad650d6e6f8f4b3d3d3/uuid_utils-0.16.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:04af9966ecd82b78eeba5725e29aa1e86fb8eb84b5443dd6a9935f9fadb6678e", size = 565929, upload-time = "2026-05-19T07:44:06.496Z" }, - { url = "https://files.pythonhosted.org/packages/5a/7e/bb91b04b2c8a081a4df2d50f1a50dd85502e2391c6eaed71b339ec9f2524/uuid_utils-0.16.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3d86ca394e0ea21bdb53784eb99276d263b93d1586f56678cab1414b7ae1d0f3", size = 290556, upload-time = "2026-05-19T07:43:44.973Z" }, - { url = "https://files.pythonhosted.org/packages/69/2a/47ee18b294af59754ef5acfa96eb027137c98cef7521199b6f70be705de4/uuid_utils-0.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f504efeb20ffd9571621658f7c8093c646d33150406d5742e49ff7cd861615", size = 328059, upload-time = "2026-05-19T07:45:30.533Z" }, - { url = "https://files.pythonhosted.org/packages/89/7c/ed6d8bb48eeecaed6722af1187d722c5243334be750419d10d5f05dffeb2/uuid_utils-0.16.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:57d85f48535dc541060f6b82f277cbcd12b78c04008ccc1039546cfcec027327", size = 334759, upload-time = "2026-05-19T07:45:07.715Z" }, - { url = "https://files.pythonhosted.org/packages/ff/33/371bddf9fd47e045c375df9668eea0d96ce9201ab6a03985b0155498e376/uuid_utils-0.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39453f1ebf4398fbeb71607f3437e2ac469c9e38b5921755c1e17ad0158a8907", size = 448927, upload-time = "2026-05-19T07:45:11.464Z" }, - { url = "https://files.pythonhosted.org/packages/dc/f1/b201d5ee005d4987fc072714fcb9f6e75303520cf19d4deec0b4df44bf40/uuid_utils-0.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50361aca5c2a770728a6343df85109fe57f89ac026827f34fe0153563cdc9ce7", size = 327178, upload-time = "2026-05-19T07:44:02.255Z" }, - { url = "https://files.pythonhosted.org/packages/b1/6a/04b4c02ce5c24a3602baa12e59bd3ec853ae73c3e9319b706c4620f47a05/uuid_utils-0.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:948485c47d8569a8bf6e86f522a2599fa9134674bee9f483898e601e68c3caca", size = 352981, upload-time = "2026-05-19T07:44:25.578Z" }, - { url = "https://files.pythonhosted.org/packages/2c/19/25db019727d14630c75c2a75a8ea66dd712bb468adcf410bac8d01ff19fd/uuid_utils-0.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ceef237cf8467fddbf6d8466cc1f6e2c04605ec919046ef5eba10a895b559fcf", size = 504686, upload-time = "2026-05-19T07:43:46.43Z" }, - { url = "https://files.pythonhosted.org/packages/5d/93/c000cd42ebfdd37cc74981ed31c979a1270156572bdebab8b5d61460e750/uuid_utils-0.16.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:24e6fa0d0ade7a9ad60a3c296022474983243df5b4e863babb4828a85ef2e52c", size = 610102, upload-time = "2026-05-19T07:45:53.765Z" }, - { url = "https://files.pythonhosted.org/packages/15/1d/7dd239909c82616722b9ee53fa1b4657c6244fb4fd026890300ebf6db22b/uuid_utils-0.16.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1c2df42314b014c9d23330f92887e21d2fc72fde0beb170c7833cd2d22d845a1", size = 569048, upload-time = "2026-05-19T07:45:41.596Z" }, - { url = "https://files.pythonhosted.org/packages/f1/49/b6a688648368a9cc0137e183657956853a91dc06ef73deda27290d586155/uuid_utils-0.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2e2f369dd734050fe96ae4905c58779b09276d47d5e9a0e5cd33ec7982784341", size = 532255, upload-time = "2026-05-19T07:45:16.936Z" }, - { url = "https://files.pythonhosted.org/packages/3f/fb/34f221ae93d5ea249a0d7056bdf45313b8d267d6aa9c5d0673ac1a4746c7/uuid_utils-0.16.0-cp311-cp311-win32.whl", hash = "sha256:733da81d51ea578862d8b9b754e8968b6da2be2b7840aee868917c23cae84015", size = 171081, upload-time = "2026-05-19T07:45:26.578Z" }, - { url = "https://files.pythonhosted.org/packages/a5/70/c2a608a813f655834ee6df4ce53ea46edad4d54f774eac1890be5c7e4e1c/uuid_utils-0.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:10d21fddb086e69245c4f0f77c7b442471f3a242aa85f62954bff157baa1c5f2", size = 176770, upload-time = "2026-05-19T07:43:49.102Z" }, - { url = "https://files.pythonhosted.org/packages/fd/c3/8ab4eff328a833c065f280b2e0d9ac873505b5e5282f2bc5133a9843d4dd/uuid_utils-0.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:98e2404713677070cee9a99a1f1e24afd496c18e833ee1b31a0587659452ff80", size = 175274, upload-time = "2026-05-19T07:44:27.216Z" }, - { url = "https://files.pythonhosted.org/packages/ff/4c/b4cf43a5d22bcdb91727acdf54be0d78e83e595b73c5a9a8a4291875f059/uuid_utils-0.16.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:727fae3f0682191ec9c8ce1cd0f71e81b471a2e26b7c5fd66712fc0f11640aa0", size = 562183, upload-time = "2026-05-19T07:45:02.683Z" }, - { url = "https://files.pythonhosted.org/packages/d6/fb/4b0d1c4b5e9f8679ca41b9cdbce5749e1d5db3d3d42a07060d6ce61ac583/uuid_utils-0.16.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:66a9c8cedf7695c28e700f6a66bde0809c3b2e0d8a70968be7bfd47c908952e5", size = 289018, upload-time = "2026-05-19T07:44:07.726Z" }, - { url = "https://files.pythonhosted.org/packages/de/43/2dc6c7401c8fab86e46b0b33ada6dcfde949b2fd48877ba6f880862be80e/uuid_utils-0.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9152bff801ec2ccf630df06d67389090a2c612dea87fbf9a887ab4b222929f6f", size = 326171, upload-time = "2026-05-19T07:45:25.186Z" }, - { url = "https://files.pythonhosted.org/packages/9b/f5/48f11fb91f36453611ca148bc441436f279870b1ec6b576dc5167fb6e680/uuid_utils-0.16.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:06fc7db470c37e5c1ab3fd2cd159697d6f8b279d7d23b5b96bd418b115f8caa9", size = 332222, upload-time = "2026-05-19T07:45:09.036Z" }, - { url = "https://files.pythonhosted.org/packages/30/cb/b2b49528521e4a097f129e8bf7850a26f00af46afba778832cf3458a5c00/uuid_utils-0.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e1a1f57fe3631e164dad27b24aa81267810e20575f705af3b0fa734f3a21247", size = 444801, upload-time = "2026-05-19T07:45:37.517Z" }, - { url = "https://files.pythonhosted.org/packages/a9/b3/a28d9c6f7c701dfe01c8020b30e33899a28eb9e4d056b07e7388f50ebf67/uuid_utils-0.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ee392fe59808a731b7b6bf4d453fb6e833774921331cceae5f254d1e9c5b97d", size = 325594, upload-time = "2026-05-19T07:44:44.682Z" }, - { url = "https://files.pythonhosted.org/packages/cf/65/e1ff41dc44966e396ead86e104ba21b35ddb07ff7a64bb55013074ee77fe/uuid_utils-0.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b2e981b1258db444df4cf4bf4c79673570d081d48d35f22d0f86471e0ad795c5", size = 349312, upload-time = "2026-05-19T07:45:15.582Z" }, - { url = "https://files.pythonhosted.org/packages/ed/57/fb19b7951f66a46e03bd1943a61ee9d59c83e994e56e8c97d79aff1f0e47/uuid_utils-0.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bbb92feb4db08cd76e27b4d3b1a82bfde708447317150c614eb9f761a43b387e", size = 502115, upload-time = "2026-05-19T07:43:38.756Z" }, - { url = "https://files.pythonhosted.org/packages/2f/8e/9a129c469b7b77afb62da5c6b7e92591073b845bd0c3108c0d0aa65389fb/uuid_utils-0.16.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c3c5afaaa68b1d6393d653e9fc93a2fde9da1681da01f74b4593f41d31fb5f1", size = 607433, upload-time = "2026-05-19T07:44:11.675Z" }, - { url = "https://files.pythonhosted.org/packages/4a/56/2ef71fad168cc3d894f7094fa458086c093635d7835381c91470b19c9ad3/uuid_utils-0.16.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:38126b353527c5f001e4b24db9e62351eb768d0367febcd68100a4b39a035109", size = 566076, upload-time = "2026-05-19T07:44:35.453Z" }, - { url = "https://files.pythonhosted.org/packages/95/bf/68e60ea053ca30f35df877b96001331398140d5c4983561affa1350331b1/uuid_utils-0.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41a67e546d9adf11c4e4cb5c8e81f000f8b1f000c17912ced089b499855719a5", size = 530645, upload-time = "2026-05-19T07:45:49.278Z" }, - { url = "https://files.pythonhosted.org/packages/42/19/b521f7d73094fca4c0c44002f4a42bfcbcf0b770fdc3c4b9a596dda25734/uuid_utils-0.16.0-cp312-cp312-win32.whl", hash = "sha256:52d2cc8c12a3466cd1727883e0746d8bad5dddd670369eb553ba17fdc3b565ca", size = 168887, upload-time = "2026-05-19T07:45:45.502Z" }, - { url = "https://files.pythonhosted.org/packages/87/1f/4126c3ccbc2d98a613664e55f6ab6d7bd4b98424a04486e4fcc76549af15/uuid_utils-0.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:c97625e5edfda8b118160ce1e88756f92b1635775f836c168be7bf10928d97fa", size = 174607, upload-time = "2026-05-19T07:43:52.938Z" }, - { url = "https://files.pythonhosted.org/packages/74/62/b83ccc8446ae39dcc0bda2cb3b525b6af6a2036383afe1d1d5fe7b234c2c/uuid_utils-0.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:baf79c8050eb784b252dd34807df73f61130fe8676b61231baccab62530f20ec", size = 173021, upload-time = "2026-05-19T07:45:10.204Z" }, - { url = "https://files.pythonhosted.org/packages/60/9b/74c1f47a9b4f138a254e51528e5ffaeba6bf99ecead9f0c4b6fccccfbfcb/uuid_utils-0.16.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d34cf9681e8892fad2a63e393068e544505408748cd8bf0c3517d753a01528d4", size = 563166, upload-time = "2026-05-19T07:44:10.494Z" }, - { url = "https://files.pythonhosted.org/packages/7c/1c/009e37b70f1f0ff17e7103a36bafde33d503d9ea7fe739761aa3e3c9fde6/uuid_utils-0.16.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0681d1bdb7956e0c6d581e7601dabcfb2b08c25d2a65189f4e9b102c94f5ff46", size = 289529, upload-time = "2026-05-19T07:43:54.466Z" }, - { url = "https://files.pythonhosted.org/packages/5e/5e/e0323d54321166639eb2be5e8a464f5cb0fc04d72d91f3e78944bb6a1da8/uuid_utils-0.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed45fb8732d216426227096b55accbb87cba57febc86a044d90780b090eb99d0", size = 326328, upload-time = "2026-05-19T07:45:31.901Z" }, - { url = "https://files.pythonhosted.org/packages/f0/a3/046f6cb958467c3bf4a163a8a53b178b64a62e21ed8ad5b2c1dacb3a2cfc/uuid_utils-0.16.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b617a334bb01ef2ff8c22900f5a14125eb9063f602131494cc9dc59519beaa5b", size = 332322, upload-time = "2026-05-19T07:43:41.284Z" }, - { url = "https://files.pythonhosted.org/packages/67/80/01914e3949744db7acd0006885e5542fbebb6e39114857d007d29b3265c2/uuid_utils-0.16.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a750d8aeb8ae880aa9a2529606bde0e994bcc7448730c953107f357a28e6102e", size = 445787, upload-time = "2026-05-19T07:45:36.102Z" }, - { url = "https://files.pythonhosted.org/packages/14/ef/f6908f41279f205d70c8a0d5dcb25dd6802741d7f88e3f0123453c3584d3/uuid_utils-0.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a250e111903c4368745fce5ac2aa607bd477c62d3307e45347338fdb64b38e0", size = 324678, upload-time = "2026-05-19T07:45:12.77Z" }, - { url = "https://files.pythonhosted.org/packages/11/4a/bf841ba90f829c7779d82155e0f4b88ef6726ccc25507d064d50ac2cd329/uuid_utils-0.16.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:95b7f480010ea98a29ee809857a98aa923008c68129af1b39244adccff7377fb", size = 349704, upload-time = "2026-05-19T07:44:47.172Z" }, - { url = "https://files.pythonhosted.org/packages/e6/31/3b5c60172b8c57bf4ca485484b8e4edef550ca324f9287f1183be97422e2/uuid_utils-0.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:420aa3ca403cedb73490b6ea3aeefeea7e0455f5ce60bbf856390ee872ae3306", size = 502456, upload-time = "2026-05-19T07:45:00.821Z" }, - { url = "https://files.pythonhosted.org/packages/88/bf/3da8d497af80fd51d8bf85551c77ede67f07825924ec5987bf9b6031014a/uuid_utils-0.16.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b8a9a7b1065a12d40f2cc25b7d705ab34954cc57095034367bca39ebcf4a876b", size = 607727, upload-time = "2026-05-19T07:44:30.058Z" }, - { url = "https://files.pythonhosted.org/packages/bd/4e/7c8cf03ec15cd6f40e4cbab81b2b4a625461327f68c7971e54723280ec3e/uuid_utils-0.16.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f235ac5827d74ac630cc87f29278cdaa5d2f273613a6e05bbd96df7aa4170776", size = 566204, upload-time = "2026-05-19T07:44:51.225Z" }, - { url = "https://files.pythonhosted.org/packages/f9/5f/af955feae69cce7fd2121ca3f790ff4b85ad2e17b2149546f50753e1a047/uuid_utils-0.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c8083284488b84ad178e74add64cfd1e74e8be5e30821e5acbc5019281c658b0", size = 529986, upload-time = "2026-05-19T07:45:57.85Z" }, - { url = "https://files.pythonhosted.org/packages/10/cf/3fec757e51bef10eb41ae8075f5442c60e85ff456b42d16a3063f5dc6c80/uuid_utils-0.16.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:27a071a899ba46a551d6524dbbc5a98b88be176d0f55ddf72cf71c005326ac10", size = 98683, upload-time = "2026-05-19T07:44:16.369Z" }, - { url = "https://files.pythonhosted.org/packages/40/a7/cd1adbea7ef882a70db064c00cd93b12e11027b4cdd7ffd79e95c35fc3e3/uuid_utils-0.16.0-cp313-cp313-win32.whl", hash = "sha256:924a8de04460e4cf65998ad0b6568084f7c51740ebd3254d07a0bcde35a84af6", size = 168822, upload-time = "2026-05-19T07:44:24.09Z" }, - { url = "https://files.pythonhosted.org/packages/74/99/617ceb9e3a95b23837012740979baf71afad723b70daf34862da3f7c17a1/uuid_utils-0.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:5279bc7ab3c6683f1c67314695bee14d869015acbbc677bdb0015190fe753d16", size = 174967, upload-time = "2026-05-19T07:44:56.022Z" }, - { url = "https://files.pythonhosted.org/packages/d9/d8/148ae707bfc36d482e39db679c86b81bdce264d4feb9df5d40a03b7687e3/uuid_utils-0.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:61a9c4c26ad12ac66fa4bfd0fdb8494724fe7a5b98a9fcd43e78e2b388663dbb", size = 173142, upload-time = "2026-05-19T07:43:50.171Z" }, - { url = "https://files.pythonhosted.org/packages/21/05/ca6d60705e71fdeaa3431dad94e279a8213c5573cb2925e1aabf3dc0330a/uuid_utils-0.16.0-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:73486b6aa3f755a6c97000f5ea67e7ac78d6df89bf22980789a1e943e24b74f0", size = 564408, upload-time = "2026-05-19T07:44:38.351Z" }, - { url = "https://files.pythonhosted.org/packages/eb/8c/b9a0462c38535c1662acb1025768e2d626bee5ce9e1790bad6b5381162ea/uuid_utils-0.16.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f1614572fd9345cdc3dde3f40c237345719fabca1aa87d2d87b321d523cfa34d", size = 289923, upload-time = "2026-05-19T07:45:19.611Z" }, - { url = "https://files.pythonhosted.org/packages/f2/33/a53afeef1a56051551a0f5a801e4bce411dd73c6a8c99bad16902651256d/uuid_utils-0.16.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9346ce6eb1fbd8b03a6b331d66016afcb4edcdff6eac708e21391600529a016a", size = 325762, upload-time = "2026-05-19T07:45:18.261Z" }, - { url = "https://files.pythonhosted.org/packages/72/ca/4462a4f36365d7ee72d41e05e6bcfe127e861b073ab37c25b2c8a518317c/uuid_utils-0.16.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a0fc6eb3fd821466fbab69cf356c6ec2b7327266bbbc740a2eb57c77c4bef965", size = 332359, upload-time = "2026-05-19T07:45:34.886Z" }, - { url = "https://files.pythonhosted.org/packages/c5/67/9d3373fa7c5a746fdecc64e30caf915c29eb632203508d87676f9243ed03/uuid_utils-0.16.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13a797e5e8f0dadc18351a5aa013815ddac25dce6864072a539d510910c95f71", size = 445483, upload-time = "2026-05-19T07:44:49.598Z" }, - { url = "https://files.pythonhosted.org/packages/57/08/ce01aa6d897fc7f875844fe58cad0a542c8ebf089d9242b654b56260ecb8/uuid_utils-0.16.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57c3583b1f1c00a94f59726a5e2b988fa209221143919a1af5c2fc24e318fc98", size = 326281, upload-time = "2026-05-19T07:44:59.677Z" }, - { url = "https://files.pythonhosted.org/packages/76/ef/2c719b2c26bb5b5e5061a1435c11ad2bd33ac3cd6d4cd0c7c3ac1d3396ed/uuid_utils-0.16.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:caac9c8b1d50e8fbddc76e93bfefbef472978eb45adbfdb6289d578816992953", size = 350809, upload-time = "2026-05-19T07:45:28.076Z" }, - { url = "https://files.pythonhosted.org/packages/e0/9b/c1ed447328b32229cca38ac4c62d309eab006e5e9c4020e2056a175bc607/uuid_utils-0.16.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:91db59bad97ed2b9d2c6ed25082fe9762b2c422e694fe06786b28cf4e776ac4c", size = 502088, upload-time = "2026-05-19T07:44:09.208Z" }, - { url = "https://files.pythonhosted.org/packages/c1/e0/8442f4efe7bde72f0b4ae5f675d0c7fbe209ad0b54718b8ddf43c46c6fae/uuid_utils-0.16.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:41985e342a30e76366a8becc60bbdb07d72cd1b86ec657b1f31654e9fb1baada", size = 607631, upload-time = "2026-05-19T07:44:19.384Z" }, - { url = "https://files.pythonhosted.org/packages/f1/1e/9a9fa261edf4c972f28ae83421377e3ab8dbd0bd7db58fd316e782d09a3b/uuid_utils-0.16.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1b0dcedf9266bf34a54d5cbe78648eaa627e02352f2a6923ed647530aea2f661", size = 567618, upload-time = "2026-05-19T07:43:58.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/f7/1bcfdb9d539bd42736dd6076470a42fbb5db23f79712c0a06aa0a3752f7b/uuid_utils-0.16.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:26fe23ab60f05de4ad70aaa5b6a4c2a7bbd43055e3dd6f6b31efba0532ac9c71", size = 530971, upload-time = "2026-05-19T07:45:06.348Z" }, - { url = "https://files.pythonhosted.org/packages/24/0c/18945f417d6bb4d0dd2b7652fe36c58c4e83bcf593b9b326b83aa40b853a/uuid_utils-0.16.0-cp313-cp313t-win32.whl", hash = "sha256:7f8cf49c05d58523a0f977cb7f11afc05791a0fa164d7303b8365a34750638e7", size = 169369, upload-time = "2026-05-19T07:44:32.581Z" }, - { url = "https://files.pythonhosted.org/packages/cc/cc/c0eb0c3fab2ed80d706369b750029143b53126809b77b36bcbb77da66bab/uuid_utils-0.16.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e99f9a8b2420b228faba23a637e96efaf5c6a678b2e225870f24431c82707f50", size = 175384, upload-time = "2026-05-19T07:45:56.623Z" }, - { url = "https://files.pythonhosted.org/packages/b7/77/50ac87b6e18b1c686f700aa38c9471a990683c6a955f71ac1a6677ed8145/uuid_utils-0.16.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6853b627983aa1b4fd95aa52d9e87136eb94a7b3b7de0fbb1db8a498d457eeec", size = 564108, upload-time = "2026-05-19T07:43:55.609Z" }, - { url = "https://files.pythonhosted.org/packages/83/16/65046676de246bb5334d9f58aa96d2feb9fc347fda3556aaff7da1c2fc7a/uuid_utils-0.16.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:f44b65ae0c329843817d9c90e36a7a3c677b413bf407c99e67db874dac49dad3", size = 289967, upload-time = "2026-05-19T07:45:38.886Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/54fa988606a15dfd2028e925d8eb9c3ee6edbf1eb7692a67b37282880b56/uuid_utils-0.16.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de8a365795a76f347f5622621c2bee543cffa0c70949f3ee093bdefc9d926dcc", size = 325835, upload-time = "2026-05-19T07:44:42.02Z" }, - { url = "https://files.pythonhosted.org/packages/d5/1b/50622f967ceacea1f89fd065d9bfd395b51acb02cfb0a4ddc8fa9ff0c983/uuid_utils-0.16.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:426a8c9af90242d879706ccf29da56f0b0712e7739fb0bbe16baacabc75596e2", size = 332607, upload-time = "2026-05-19T07:43:42.42Z" }, - { url = "https://files.pythonhosted.org/packages/12/f5/4059706be6617e2787e375ea52994ce3c3fa3920b7d4a9c8ebf7895681a5/uuid_utils-0.16.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:833bc4b3c3fc24be541f67b01b4a75b6b9942a9b7137395b4eb35435948bd6da", size = 444287, upload-time = "2026-05-19T07:43:37.106Z" }, - { url = "https://files.pythonhosted.org/packages/65/d5/f44b2710563da687a368f0ce4dcbd462dfb6708bcd46439d831991d595c7/uuid_utils-0.16.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efb5252d7c00d586077f10e169d6e6d0b0d0f806d8a085073f0d19b4737aef4e", size = 324949, upload-time = "2026-05-19T07:45:33.175Z" }, - { url = "https://files.pythonhosted.org/packages/3a/a7/a69e859e37d26c5603f0bc0ae481860f691224f140e5a832f325b804770d/uuid_utils-0.16.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b3377ce388fd7bf8d231ec9d1d4f58c8e87888ddea93581f60ed6f878a4f722", size = 349651, upload-time = "2026-05-19T07:43:59.998Z" }, - { url = "https://files.pythonhosted.org/packages/db/73/4139cd3ca7b81ea283c1c8769373e9b2008241c0744a8ffb25f0a1b31325/uuid_utils-0.16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:12b6310beb38adc173ec5dc89e98812fd7e3d98f87f3ef01d2ea6ecb5d87994f", size = 502326, upload-time = "2026-05-19T07:45:40.292Z" }, - { url = "https://files.pythonhosted.org/packages/cb/8c/858101583fbad1b3fa04da88b1f7170836aa0f00b4cb712063325c44466d/uuid_utils-0.16.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a49b5a75497643479c919e2e537a4a36224ac3aaa0fada61b75d87024021ac3e", size = 607689, upload-time = "2026-05-19T07:44:48.355Z" }, - { url = "https://files.pythonhosted.org/packages/5e/bd/8f3d54a4763dd91ebd0f3d7b0c2ec434e4e0b1fc667b03a44d611a465ec6/uuid_utils-0.16.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:63bfdf00be51b6b3b79275d6767d034ea5c7a0caa067a35d72861284100cb60a", size = 566214, upload-time = "2026-05-19T07:44:53.519Z" }, - { url = "https://files.pythonhosted.org/packages/54/76/4c9a8d9baaa243c7902d84dbba4d51b1ab51c379c66d3fd6368ff6933ecf/uuid_utils-0.16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7525bc59ac4579c32317d2493dd42cf134b9bb50cd0bc6a41dd9f77e4740dde6", size = 529989, upload-time = "2026-05-19T07:44:43.141Z" }, - { url = "https://files.pythonhosted.org/packages/6d/13/d32cea997f880cedde415730ce0e872ebfd7a040155ae0bbda70eccd208e/uuid_utils-0.16.0-cp314-cp314-win32.whl", hash = "sha256:fbcac6e6710aa2e4bfbb81762758e01470dc56d5048ba4253acc77c9833568ff", size = 169146, upload-time = "2026-05-19T07:45:46.655Z" }, - { url = "https://files.pythonhosted.org/packages/1c/19/9fc55172d8fe59e1f27a14d598b427fa508a7ebb35fa7b7b99c24fa0ef13/uuid_utils-0.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:d23fcaf37368a1647319187ef6f8b741bf079f033065899bc2d00a44b0a1214a", size = 175364, upload-time = "2026-05-19T07:45:55.335Z" }, - { url = "https://files.pythonhosted.org/packages/89/5d/fcd9226b715c5aa0638fcdd6deaf0de6c6c3c451c692cd76bfca810c6512/uuid_utils-0.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:ea3265f8e2b452a4870f3298cb1d183dc4e36a3682cbb264dbe46af31267e706", size = 173268, upload-time = "2026-05-19T07:44:31.19Z" }, - { url = "https://files.pythonhosted.org/packages/c1/64/97ec9af95e58b8187f2934008ffab26e1604d149e34fe01c388b0543a24f/uuid_utils-0.16.0-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:99f8420c3ed59f89a086782ac197e257f4b1debb4545dffa90cf5db23f96c892", size = 564464, upload-time = "2026-05-19T07:44:40.856Z" }, - { url = "https://files.pythonhosted.org/packages/3e/6d/e4082f407484ac28923c0bf8e861e71d277118d8b7542d0a350340e45350/uuid_utils-0.16.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:259bab73c241743d684dcc3507feb76f484d720545e4e4805582aeff8e19700b", size = 290087, upload-time = "2026-05-19T07:44:01.084Z" }, - { url = "https://files.pythonhosted.org/packages/8c/43/c5c5f273c0ff889f20f10344784f9197dd00eb81ccc294330d4b949fea7e/uuid_utils-0.16.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:897e8ef0dc5e4ac0b17cf9cae84bb41e560d806280ec5b93db7475b504022105", size = 325532, upload-time = "2026-05-19T07:43:47.508Z" }, - { url = "https://files.pythonhosted.org/packages/13/7f/669aa899ab5378374d28a28231e6978f739921a1af394c7ebd6cc86e2639/uuid_utils-0.16.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c5af79cde16a7600dfccb7d431aec0afd3088ff170b6a09887bf3f7ab3cc7c81", size = 332209, upload-time = "2026-05-19T07:43:51.528Z" }, - { url = "https://files.pythonhosted.org/packages/2b/57/a2a32406d79a222794ef98a19254fd9a81a029a0f32d7740fba9873bff1f/uuid_utils-0.16.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bece1a6f677ca36047442c465d8166643eed9818b9e43e0bf42d3cf73e92dcff", size = 445507, upload-time = "2026-05-19T07:44:20.541Z" }, - { url = "https://files.pythonhosted.org/packages/26/6b/85459a35bfa7d73e79acbc4eab1cf6aa6e4d9d022c3260ed9dea539c7f0b/uuid_utils-0.16.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb3444498e7b099499c8a607d7771377020fa55f7274e46f54106af19f752d7", size = 326154, upload-time = "2026-05-19T07:45:23.587Z" }, - { url = "https://files.pythonhosted.org/packages/84/9e/e965efdbb503ed14d6e57aec1a22b98326ed24cc2fb48e750c4d192267a0/uuid_utils-0.16.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:542098f6cb6874aebeff98715f3ab7646fbe0f2ffb24509ca372828c68c4ed0e", size = 350905, upload-time = "2026-05-19T07:44:36.957Z" }, - { url = "https://files.pythonhosted.org/packages/23/ae/4321867888a783d03b7c053c0b68ca45d03974d86fcebf44d4ec268db397/uuid_utils-0.16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7207b25fe534bcf4d57e0110f90670e61c1c38b6f4598ba855af69ab428fc118", size = 502098, upload-time = "2026-05-19T07:44:17.696Z" }, - { url = "https://files.pythonhosted.org/packages/9d/9a/914a47bf42479bff0ce3e1fa1cbe3585354708edc928e27687cf91de9c26/uuid_utils-0.16.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:16dc5c6e439f75b0456114e955983e2156c1f38887733e54d54205d3005223e4", size = 607032, upload-time = "2026-05-19T07:44:22.151Z" }, - { url = "https://files.pythonhosted.org/packages/85/4c/2abacd6badba61a047eaa39c8347656229d12843bd9bbe4906daa6dc752c/uuid_utils-0.16.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6d3ee32c57898d8415242b08d5dd086bc4f7bcbbb3fc102ef257f3d793eb294", size = 567664, upload-time = "2026-05-19T07:45:21.043Z" }, - { url = "https://files.pythonhosted.org/packages/53/1f/9d1a09521276424da19dc0d74456aed3311170fec181b28fa6acba45d963/uuid_utils-0.16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7555f120a2282d1901c9a632c2398a614101af4fe3f7c8114aa0f1d8c1978855", size = 530996, upload-time = "2026-05-19T07:45:44.229Z" }, - { url = "https://files.pythonhosted.org/packages/b4/22/14dbedb6b61f492d5524077fd10bbfb137583b0f0aafa6cd870ccb43f39a/uuid_utils-0.16.0-cp314-cp314t-win32.whl", hash = "sha256:756575d082ea4cb7d2f923d5b640c0efe7c82573aab49220c4e09b62d13737ff", size = 169358, upload-time = "2026-05-19T07:45:05.146Z" }, - { url = "https://files.pythonhosted.org/packages/25/f4/a636806c98401a1108f2456e9cc3fa39a618145bfb1d0860c57203159cfe/uuid_utils-0.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:aa50261a83991dbb570a00573741455bd8f3249444f7329e5bdcd494799d1504", size = 174813, upload-time = "2026-05-19T07:45:59.579Z" }, - { url = "https://files.pythonhosted.org/packages/75/12/3823742459d87a100deb24bb6b41692aa961b267abd130fa7739cdf7d409/uuid_utils-0.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:22a17e93a371d850ffce8fcdbacc2239f890efe73aa3262b6170c1febc08afe1", size = 171733, upload-time = "2026-05-19T07:45:29.283Z" }, - { url = "https://files.pythonhosted.org/packages/d3/89/655408a5485c56bf2c4561eb85f5bca119b1f4020370b4daaeb8d13e46fb/uuid_utils-0.16.0-pp311-pypy311_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4e35e9a986e86806a61288fac3afbb51317f2580929feefd1661891ffd7b8c24", size = 569295, upload-time = "2026-05-19T07:45:22.325Z" }, - { url = "https://files.pythonhosted.org/packages/24/1c/a7c5506a4e2cf95ac98fec0996c56daa14e41f2ab1858f569b3556a202f9/uuid_utils-0.16.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b35706350cf9bd4813f1811bebe03cac09795a5a379f90cb3616171f4e9ffc9e", size = 292316, upload-time = "2026-05-19T07:43:57.044Z" }, - { url = "https://files.pythonhosted.org/packages/dd/75/4267ab8baa1e6a8ad7c262e204484b44df0fde0920025ea9b43c2b869726/uuid_utils-0.16.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4fd5c7936a876ba2606ba124603b559a5c2cea458c59b9c31677e6acc3c53cc", size = 329619, upload-time = "2026-05-19T07:44:12.928Z" }, - { url = "https://files.pythonhosted.org/packages/15/77/c794102831e331564f651099cac55006694677938d70f1033b35da451a89/uuid_utils-0.16.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:130f7452c1b87b7c16d0bdc1f32a1de531ae4cc4220ed4e691402bbcfc39e0a9", size = 335121, upload-time = "2026-05-19T07:45:47.974Z" }, - { url = "https://files.pythonhosted.org/packages/8b/3e/458a0a2da75c596b151182a6c7550c6c3d30f479e14e40f69c0336579e59/uuid_utils-0.16.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5ee0bbbd4ca3968422cd8308f0072520bc73dc760cb26c6fa75ca1aca14d210", size = 449631, upload-time = "2026-05-19T07:45:50.645Z" }, - { url = "https://files.pythonhosted.org/packages/ed/15/dd1fab6f7fcd15f2c331d0c1f0f516bb1113a640216460f82be53db3dcf8/uuid_utils-0.16.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0824a31898ef46a9d84d748c3abe27cdb615ac3773c53cc1f84fc8e66dc7c4", size = 328418, upload-time = "2026-05-19T07:44:52.38Z" }, - { url = "https://files.pythonhosted.org/packages/96/56/62dcd551b140cbeb0f87522da2015b4b9e5818327b920506ad88d28562b0/uuid_utils-0.16.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abfbf5e0c47fb31b37164a99515104e449a0bee36a071dc8b105457a2b35a5e6", size = 356177, upload-time = "2026-05-19T07:45:42.856Z" }, - { url = "https://files.pythonhosted.org/packages/44/e7/3937b9a9d6745b94dbe7b86531e098db8c53b77c8d07df7daa9577a47b8e/uuid_utils-0.16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:680799a9ade01d69c53cb9d41392ced24919d4f600bfab5060b61fca37510097", size = 178508, upload-time = "2026-05-19T07:43:43.774Z" }, +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/91/63938e0e7e7876658e5e40178e7c0735b53527886fe11797a11699c55edd/uuid_utils-0.17.0.tar.gz", hash = "sha256:abb5667a36119019b3fa320c4d10c21ebccfcc87c8a739e6a0056cee7f48dde2", size = 43220, upload-time = "2026-07-09T13:49:58.433Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/60/659104207938f2ac62508b9aa595fc0515ac7452dd515c8e1d47d0b91169/uuid_utils-0.17.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d2d9a63a9e6f2416ace8c109043a9280d6b34f34bb2e5421903e149403db40a6", size = 564038, upload-time = "2026-07-09T13:47:51.731Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e7/e0d048a268b4163058bdd2f07a45bbe13c29e3cc6b7b88f8f00b001617ce/uuid_utils-0.17.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b776c7fc8755c7de06dd5a22b47c40ae84f67d13277ebb233cc84933ba4dcbcd", size = 286680, upload-time = "2026-07-09T13:47:53.141Z" }, + { url = "https://files.pythonhosted.org/packages/84/83/e3606dc9b4224d0c9a6675d9347e7e0da7e67fa30e061bfdb686138844d0/uuid_utils-0.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1edf2f8732e4ed95bd7b65f2658f4aa072efaaff321144f4e0d4bf6a22709263", size = 323533, upload-time = "2026-07-09T13:47:54.433Z" }, + { url = "https://files.pythonhosted.org/packages/22/f8/aec5c34fa80c9fef09a506a098015e728080076494b72b9e8e5cfc9669c4/uuid_utils-0.17.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:84ed3a2d5cd3ae6db87af20bfed3331116195ba4757ad7177fc8f12c1bbce2a9", size = 330691, upload-time = "2026-07-09T13:47:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/08/73/85776566863514f37b0a761648368e96b07d64981a9b6c391220aa2563a9/uuid_utils-0.17.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bf4d9cd1e80e73922073b9b27c143bedeb109d65f94cd12712e2c87118f2b7d", size = 444094, upload-time = "2026-07-09T13:47:56.936Z" }, + { url = "https://files.pythonhosted.org/packages/68/06/e0424b4268c0932e0ff8257303d70de4053f05958843268fac4cb0f79b57/uuid_utils-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52db0e471d3d2632d35445af352591f40a8f32959a412981d9f51e068bb9514b", size = 324548, upload-time = "2026-07-09T13:47:58.217Z" }, + { url = "https://files.pythonhosted.org/packages/db/d2/a0cb3a69ef6d9becc30a6a0594ddf6f798f6204953dfa85073cbec875b94/uuid_utils-0.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:344f7c755e280ea0ba6aeb08022190d867a80000b1715cacded54fc4b5633607", size = 350307, upload-time = "2026-07-09T13:47:59.418Z" }, + { url = "https://files.pythonhosted.org/packages/82/81/d82766af7db541e4a78b920bc1c4303d44995f841805d1498934088cd12c/uuid_utils-0.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:589d9da7de8fa7f739bb970ac4632c9a268213117d634e1c4a58c1c1e821ca05", size = 500661, upload-time = "2026-07-09T13:48:00.726Z" }, + { url = "https://files.pythonhosted.org/packages/10/71/b261cd0d38497ed8c2cce0263c5607ec9cd2bbace0f73cb19a6fc2060b6e/uuid_utils-0.17.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cee808b405e9095506f4e4e89924bec7ea77eac3129b6fe36eda04364b3b343b", size = 606577, upload-time = "2026-07-09T13:48:02.539Z" }, + { url = "https://files.pythonhosted.org/packages/3b/63/9e48512bb235e9533adbb25c30fd0c9cef09f6ecefe131ba392b98572b40/uuid_utils-0.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:53ce348ef4c6e98c02c19c522af01334fe94476ce9af0db8c4482f9f142ae9c1", size = 567054, upload-time = "2026-07-09T13:48:03.833Z" }, + { url = "https://files.pythonhosted.org/packages/b2/cc/d7bad8799a37ec33fc21b29fcb459d63d9f88aa09056d0c3e58903ba2fb0/uuid_utils-0.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9e753e81457241e2200c56a898e268e8fa25796271af0489c608f24d8e631eed", size = 529682, upload-time = "2026-07-09T13:48:05.097Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3b/59b1e07ada8aadd3c046c97fe9814d85e770abb7e8cf68d5d86538bf62e9/uuid_utils-0.17.0-cp310-cp310-win32.whl", hash = "sha256:c589f5023d471ce75dd2cce61acb25ed6347e562041588a1a366808f22d7176c", size = 170595, upload-time = "2026-07-09T13:48:06.411Z" }, + { url = "https://files.pythonhosted.org/packages/8b/5c/23a2d0253ada2ee8c497d541d4ef0dd5576c3d2454ec2f9d0b8a06af9304/uuid_utils-0.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:981cc10163988defea96e8d6c507df151eab8f483e7df9ae543d5a41a4be073b", size = 177225, upload-time = "2026-07-09T13:48:07.561Z" }, + { url = "https://files.pythonhosted.org/packages/d7/b2/8f03b61f0aa4afc687855c4f00db35f4d3e58c480cd885abc46f6e41308f/uuid_utils-0.17.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:f9b093cb3b6c9d6233ef45a05cab064d2aa0a8cb3c5777084c9e20fcb77c2371", size = 563901, upload-time = "2026-07-09T13:48:08.961Z" }, + { url = "https://files.pythonhosted.org/packages/e3/cb/88b909ffb9ac11f88d2e6ceabc592ccc660b5830b06dbcbd290ab8981f1f/uuid_utils-0.17.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0bc4c431ccd59c764080ceb43b126043325fe17861b87759d026a0cdd8423bb2", size = 286383, upload-time = "2026-07-09T13:48:10.2Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b8/bc5b64e9898867227c535cd0366c571c580a736748e81329437c1773e442/uuid_utils-0.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c00d182e31034250690f417b9068b78eab423c10d76766664e82d9860c340479", size = 323244, upload-time = "2026-07-09T13:48:11.477Z" }, + { url = "https://files.pythonhosted.org/packages/13/d9/8a17462ce066fbf89670fb737a3f0c93a77816736d2a4d134787e759d8ea/uuid_utils-0.17.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:570db214f6d8507587a8faa968a3fe65e957daeb7bc48b27dc7f69bc3ecdd6f1", size = 330466, upload-time = "2026-07-09T13:48:13.092Z" }, + { url = "https://files.pythonhosted.org/packages/43/37/0c65d0db3bae45183419756d938f1791a82c835fd92bf234eb4f008d2e02/uuid_utils-0.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:351462debd866f1f25e4d4f5c7fac89525b52151f0102a1bdfe94a999b046f5f", size = 443806, upload-time = "2026-07-09T13:48:14.372Z" }, + { url = "https://files.pythonhosted.org/packages/32/d5/7e698466d1f5254620b5ee0d711fdd20a0e9c2acd7040740c37193a8f673/uuid_utils-0.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:622cdde768300591ac79bfcd7bb3468e4b191b1105d5dbfe8d87c39d8f63dd46", size = 324261, upload-time = "2026-07-09T13:48:15.642Z" }, + { url = "https://files.pythonhosted.org/packages/5d/48/3a5b242d7f0b8e3ca77dcd7177f3cf73e0280cee32e2349d9796ca27f183/uuid_utils-0.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:75d7411e8eb9259764dd60310738540649057cda4509b4af14b36b7f663bfeb0", size = 350657, upload-time = "2026-07-09T13:48:17.273Z" }, + { url = "https://files.pythonhosted.org/packages/95/f4/f32ea82a89efed2eafee2f1d925d64687a81e550a9951933fb1b75c95ca6/uuid_utils-0.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1019476b6bdc047216ef7414be5babe0fa5ccfde977c0cac4fd6c75ddec66ff7", size = 500613, upload-time = "2026-07-09T13:48:18.459Z" }, + { url = "https://files.pythonhosted.org/packages/f4/5c/c7b73ec4bbe28db162a4841d352c6eda582801e0dd9fe72f6ad5cc584ee4/uuid_utils-0.17.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:04452640d8b6920c480c16e5afe91ff896d236e0c972830f9247e0898d38c803", size = 606306, upload-time = "2026-07-09T13:48:19.726Z" }, + { url = "https://files.pythonhosted.org/packages/63/95/8a2777204e8691b4961e6aa619001c3e5175aa430ab43da3079142e8d310/uuid_utils-0.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:793229621e1ad6cac55f015cfa9f4eff102accbc3da25d607b91c6b0bec167fb", size = 567231, upload-time = "2026-07-09T13:48:21.024Z" }, + { url = "https://files.pythonhosted.org/packages/1a/6f/1d778ca3ed6d2cf35f22088e2de714675416747ab41be510f22c141043a7/uuid_utils-0.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03815cea572c8a693cab5475b9d750cc161470961c7defa27e9286cad62f38f5", size = 529373, upload-time = "2026-07-09T13:48:22.312Z" }, + { url = "https://files.pythonhosted.org/packages/6e/d3/9ad1ab64b3bed0a0237d1db89dc6f5001d6116a82766753da4ac4496f979/uuid_utils-0.17.0-cp311-cp311-win32.whl", hash = "sha256:c4f845166b09acc65c5213a35551a7f81c17fa010ab467229b5813f79d17fe13", size = 169930, upload-time = "2026-07-09T13:48:23.504Z" }, + { url = "https://files.pythonhosted.org/packages/c2/1a/e01417f52eae6e2cb412260bb332b4ee4b37af2982d9c38cff4b68b2e899/uuid_utils-0.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:14dc2f46abb1091260c0d203fcbdf4e045042cc07e49183fd3b255904b95eb70", size = 177242, upload-time = "2026-07-09T13:48:24.723Z" }, + { url = "https://files.pythonhosted.org/packages/35/20/396c27f996add19f8ac31e49cc4570824e51a97719087dabf94694d25bc4/uuid_utils-0.17.0-cp311-cp311-win_arm64.whl", hash = "sha256:29179ffb7b317239b6d6afb100d14c439c728770460718280b9c0a42d2561ec2", size = 177023, upload-time = "2026-07-09T13:48:25.834Z" }, + { url = "https://files.pythonhosted.org/packages/20/80/a7e685968e3cec99d6fe2fb25d0f5726310e1bba356da68c13dfd8b7d140/uuid_utils-0.17.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:9205068badf453d2f0821fd5d340389b4679992d7ff79d4f3e5608996dd1b287", size = 556403, upload-time = "2026-07-09T13:48:27.022Z" }, + { url = "https://files.pythonhosted.org/packages/56/47/3102d93bcb7b0bfe6bede63ff8f221a7f91348e10a37f682773be27c56d9/uuid_utils-0.17.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0fcca4e838af9ac9243b3358d7c14afa4dca286a87781124c272d6c4cad9c968", size = 285608, upload-time = "2026-07-09T13:48:28.769Z" }, + { url = "https://files.pythonhosted.org/packages/55/fb/d59695f0f8db065b93c63316eaafa05a22d75a0486978a33736c52c646d5/uuid_utils-0.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f3729e839209f3457d0d8b6a35a376fdf65577a5aecaf4cc3587d3305759ba6", size = 319926, upload-time = "2026-07-09T13:48:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/5a/03/62fabcd1e990e07a0e220e8d552af45bc16f107fa8e55c2014a706bb1a1e/uuid_utils-0.17.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3dac0ad0cd9a2818d1775215365a4e8c2f8ada215529dd26f3f8cceeb67a6988", size = 327172, upload-time = "2026-07-09T13:48:31.187Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/a5081391338b459e2f8d8b12581f00f8caa6317fab510e0e85c18c59e938/uuid_utils-0.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e671b2322ef09106ecb1ca0f4c398b134d5e2c1f80d7a4f3336847a3072c0e94", size = 439075, upload-time = "2026-07-09T13:48:32.295Z" }, + { url = "https://files.pythonhosted.org/packages/59/30/91795bd01e17a13661280d4899fbf38fb05e3f38e873f9aaec106ec30aa0/uuid_utils-0.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8eb3e5caca8d3a6f72ea4cce024583f989f6f2e9186f98800213fff0176e8bcc", size = 320247, upload-time = "2026-07-09T13:48:33.64Z" }, + { url = "https://files.pythonhosted.org/packages/e5/11/09102b78303e4eb62069d6d88ef9fd661dc523e8f429e1fd67eaa78a6f44/uuid_utils-0.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b72c2002202038666bf647f9a790906214c7c11cd0d6efef77b7d07bef3034a", size = 344738, upload-time = "2026-07-09T13:48:34.786Z" }, + { url = "https://files.pythonhosted.org/packages/74/f9/be95bad6954b60328878c3800258f01a6accd24fd75112d13f023462d53f/uuid_utils-0.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4e2ac1c0b56f2c91b6f158e29ed96b1503223fe8aa6e79b1be1dc55bd8a5131c", size = 496845, upload-time = "2026-07-09T13:48:36.057Z" }, + { url = "https://files.pythonhosted.org/packages/2d/02/8a19a34e0530d987488a068a71576a236f5c8c746630b870b57f71eb24ef/uuid_utils-0.17.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6c142bd0cb4dba31c10babe00d59f7ef6460f0ef55eaa9c1a9da270684af996a", size = 603233, upload-time = "2026-07-09T13:48:37.512Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a8/b1abab36ff73b0248d82179816467f6d39a2e80fd64329a895ca94f3508e/uuid_utils-0.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e252db239eb41c32248e096e0d170bce5896a4fd3405556362bc3dd83d912206", size = 561401, upload-time = "2026-07-09T13:48:38.977Z" }, + { url = "https://files.pythonhosted.org/packages/61/91/70e7b528b351cc03a9ca43e6116371cdde31bb12bcead7ca2ca1367366cc/uuid_utils-0.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:237722b6581bb5b4eb4cefbcbe5c6e2980a440aabe781fbe50ebf1cb71eee4cc", size = 525314, upload-time = "2026-07-09T13:48:40.599Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f6/9167e90cf9937d6558f92d022ff3024a69d938a514d9c8faa4080f73b001/uuid_utils-0.17.0-cp312-cp312-win32.whl", hash = "sha256:46a73cacdf512f473a81f65dbf84186e08cfe6e9118fa582b6c6b33a8288a30d", size = 166831, upload-time = "2026-07-09T13:48:41.862Z" }, + { url = "https://files.pythonhosted.org/packages/5c/7d/0b889654d9ee3413f810cf4685e241285f650d98a4103ac9f3c6bcc95f29/uuid_utils-0.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:e59b60a0a4cb7541480e02090d37dc2df3b72df4c2e776fff64ce3a4e3dd4637", size = 172944, upload-time = "2026-07-09T13:48:42.992Z" }, + { url = "https://files.pythonhosted.org/packages/be/35/8c6e1bf65e4d400352885dadc656ad6d0af96e89231e3f04686bc2197128/uuid_utils-0.17.0-cp312-cp312-win_arm64.whl", hash = "sha256:d561a4c5747a1e6c7fa7c49a0292e78b4e8c456332caa084fc7abad8de828652", size = 172459, upload-time = "2026-07-09T13:48:44.271Z" }, + { url = "https://files.pythonhosted.org/packages/d2/dd/614fb9912157ac0128e6050859ccf06d9f13df9a944a803e8f80f6157e38/uuid_utils-0.17.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d11a7bc1e02da8984d32e6de9e0826c6edac00eac17de270f372bf32f9a0af63", size = 557259, upload-time = "2026-07-09T13:48:45.664Z" }, + { url = "https://files.pythonhosted.org/packages/3e/11/d072711704de3d21bec08b6c2f36a215200ca1d5e01a390ea1ac434080a0/uuid_utils-0.17.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7a49f47ac26df3e431c56b825c1bae8e6d3d591fdbb7438c227cc9845a7e3d73", size = 286271, upload-time = "2026-07-09T13:48:47.018Z" }, + { url = "https://files.pythonhosted.org/packages/18/6d/8a63e5eb2d5a6ba69a6c2036e305075bd6f5a022e7ea25fc6ce0eb7c51d2/uuid_utils-0.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32df1944808877702ceea398c103881c09a679bb672a215e01c2a84231266bf9", size = 320025, upload-time = "2026-07-09T13:48:48.208Z" }, + { url = "https://files.pythonhosted.org/packages/f7/2d/bdc2caf9719d9090d7c46043242ae6136cba4f7a7ee384992ab905ad9aa1/uuid_utils-0.17.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:98c88d3edd08e7245562e9815996dbc6f0bd4745e1c76462f24af5ae4e187dd1", size = 327931, upload-time = "2026-07-09T13:48:49.673Z" }, + { url = "https://files.pythonhosted.org/packages/b6/33/9219d09d51ead282b578b2a4e0a515c2cce3ec52076cada8bfb7e35727d5/uuid_utils-0.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a4370089c8b2e42f1db51d76408c7fa8eaa2934bf854d17983d16179c07c098", size = 438537, upload-time = "2026-07-09T13:48:50.842Z" }, + { url = "https://files.pythonhosted.org/packages/d8/79/e8e0f8b3955f2081c116157119d87659937893242eb834aa170da04d660b/uuid_utils-0.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09a55b7a5ae764985cb46467496a1787678d0a1400356157a080ad95b1a36869", size = 320656, upload-time = "2026-07-09T13:48:52.164Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5e/d1ceddc430ff04b6e21704b2030d4438074a2f478b265dab43da957791c1/uuid_utils-0.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56aa6488b931246fae11924e4bd0e2b32677e63945eecb71c29e3c2ca0dc3131", size = 345310, upload-time = "2026-07-09T13:48:54.076Z" }, + { url = "https://files.pythonhosted.org/packages/d5/62/89438e12f389a843e626b7e37691319a057b3d6b80914609106891faadda/uuid_utils-0.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:309a35f12d99dde19032bc2259cda6431c85eeac0879134dc777cc3087d7e1cb", size = 496771, upload-time = "2026-07-09T13:48:55.365Z" }, + { url = "https://files.pythonhosted.org/packages/87/d2/eedcd99f522d60e238ead03844f0d51743ba84d33044959e230b756bf212/uuid_utils-0.17.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:21c79b61ff750abcf057163dd764ccb6196cde7a26cda1b31b45cd97769e03b3", size = 603631, upload-time = "2026-07-09T13:48:56.746Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a8/bb1b38aaddd7243b6e562c6694f499bf094800918316192fd8cb2cdc2620/uuid_utils-0.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4134353bfe3026ddab8e886002dc52bc5a0ab04611aabb0eaae23c32e6e57f64", size = 562008, upload-time = "2026-07-09T13:48:58.241Z" }, + { url = "https://files.pythonhosted.org/packages/b4/77/5f7ed930dc105e293845c09e4d5bd84076318a12f45a46783e1af64906d7/uuid_utils-0.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7c89359affecebe2e39e6a116d069b363c936511a9572b308402489a26957d89", size = 525527, upload-time = "2026-07-09T13:48:59.784Z" }, + { url = "https://files.pythonhosted.org/packages/fd/25/1b55697adf6811a6f92cff6340e6b03e31fd6bc51066a5c10698c29b3679/uuid_utils-0.17.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:6a019a31bc4db89a0903a3e4f6b218571f3a6ff0ad4b3d3fe1c8f91a05ff6e3e", size = 97965, upload-time = "2026-07-09T13:49:01.217Z" }, + { url = "https://files.pythonhosted.org/packages/26/bf/cd729343de4684230be8a966bad7bfc2cf10ce3e643b1189a8b5370dbe35/uuid_utils-0.17.0-cp313-cp313-win32.whl", hash = "sha256:b3131a82d0c7611f0aa480a6d36929e001a3f54ba0fc029a8118a5863cce513c", size = 167316, upload-time = "2026-07-09T13:49:02.354Z" }, + { url = "https://files.pythonhosted.org/packages/76/f0/e602ae0a1b139a7826e5189b93d91902564def06d5006324fd2faf82c8fc/uuid_utils-0.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:9e311f908d2f842fca4c7dcebc4f10306b8089b204ef04cf6704b4332c9ff6ff", size = 173630, upload-time = "2026-07-09T13:49:03.529Z" }, + { url = "https://files.pythonhosted.org/packages/1a/52/024ebece265b387154115dc4f1d9727174ef82623069f4bec8b7ed7e73f7/uuid_utils-0.17.0-cp313-cp313-win_arm64.whl", hash = "sha256:c351737e2e65497c7200ab4ffb8af97e9f48be6488309abdd265fe08d66ee92f", size = 173214, upload-time = "2026-07-09T13:49:04.836Z" }, + { url = "https://files.pythonhosted.org/packages/56/44/e2fd3fdf356e1b55d2acf1b956b4f3f29ffb215a99c387eba04b1c5fba66/uuid_utils-0.17.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:673d89cc434cc9b97a0b4cf61272f6fca70a81f64eb0afbface2a0d9f77f06cd", size = 562232, upload-time = "2026-07-09T13:49:06.201Z" }, + { url = "https://files.pythonhosted.org/packages/19/28/65e0980d668a6d44e699f59d1acf43d6b5d4893592c115ce7c680bb4dfa1/uuid_utils-0.17.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:387cf7437c94ddec08651a0f1081381299c7075bc48a6251d8922bf39973378a", size = 287858, upload-time = "2026-07-09T13:49:07.45Z" }, + { url = "https://files.pythonhosted.org/packages/8f/8d/5e97bcebc90fb6a10f98af3dc1ba552e04183aba59e2edc0b9cf486dd998/uuid_utils-0.17.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:220b52746d99e11964badac3c0869016e0c24bafb70a7dd5c2c072a6be3da9cc", size = 321587, upload-time = "2026-07-09T13:49:09.489Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d7/88b2a2370cc3d455ba0515fb6f5c8f7ac0c0f55a86801b6e56a432f22c17/uuid_utils-0.17.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0ab4a66e7a035ad6625cfc1fbdb34f5c2d25a80ae1ef4bfee458ea2036333c6d", size = 328964, upload-time = "2026-07-09T13:49:11.292Z" }, + { url = "https://files.pythonhosted.org/packages/bd/0f/181c5da673953dfc0958cb4fb3a4984a9098673ddb05cac68e994bc8511b/uuid_utils-0.17.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5641071337eb11d61a001ea08793bf72216f3241f0a433ed2764804b2a3e3cc7", size = 442909, upload-time = "2026-07-09T13:49:12.644Z" }, + { url = "https://files.pythonhosted.org/packages/ec/38/5c5e665af542884a8fd3c61725c38453239e13940326b5b70f3ef8881a97/uuid_utils-0.17.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9082e709014946b1f6e96ae6ecd93652efca2d2a6a3ab67dbe151c8b4bf193a4", size = 323076, upload-time = "2026-07-09T13:49:13.897Z" }, + { url = "https://files.pythonhosted.org/packages/f5/35/7de97de18cbf226c2a4f2104ad15e56ca4491717c81c0b71795c0c585b4e/uuid_utils-0.17.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1fd6f0e8a162dc0e9255b6aebe3cd175e76c33202f1bf39da9e6294b93db0099", size = 347360, upload-time = "2026-07-09T13:49:15.237Z" }, + { url = "https://files.pythonhosted.org/packages/26/a1/9915d5dd59fdd1957ded5d188c0ea0b9db5a1d84d42c8d8828a7b83b366e/uuid_utils-0.17.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d63010803d7c368963bbe6f7ec379593e76dd581d7db0f29118d88713c9e0354", size = 499267, upload-time = "2026-07-09T13:49:16.774Z" }, + { url = "https://files.pythonhosted.org/packages/c0/05/88108405262ec850cea0f95733445d6873e5772af3292baabd9ef8457740/uuid_utils-0.17.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a46bedc273b6f58f11dee816ff74999625ef8d007890f411b7a4975bf1c89330", size = 604940, upload-time = "2026-07-09T13:49:18.147Z" }, + { url = "https://files.pythonhosted.org/packages/89/d5/6dbcd300de47cc443cff2656cd5327a385751213dcb2101cfee7388170b2/uuid_utils-0.17.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:405233a5f625b3d995648f4647fa6befa4567cf3f74e1f6b9837e16f7310f0e0", size = 564172, upload-time = "2026-07-09T13:49:19.593Z" }, + { url = "https://files.pythonhosted.org/packages/ab/94/e8057f2288a415fba8a978bca4b589f5cb6b91a028a5dc07a1775938b33f/uuid_utils-0.17.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b6c5d2d71e1f17329150ad9427d27f4a3f29a01792e7ecdc64a98ac5368fc4d5", size = 528533, upload-time = "2026-07-09T13:49:21.075Z" }, + { url = "https://files.pythonhosted.org/packages/f0/6b/31713148c77e48e62f51aa042a98a54a8be0396912ea5130f83f52ae722d/uuid_utils-0.17.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:f7e9b8728ba07a3cb2f29d5aa1a266c2664eb8ef0fd43afa34627c92f7fac8f0", size = 99197, upload-time = "2026-07-09T13:49:22.351Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f3/ca6f6ac5428312df8ed632f6dd9f9e6aba23090471fcdeae53eab027e8b3/uuid_utils-0.17.0-cp314-cp314-win32.whl", hash = "sha256:58838921e377791ef22c64cc92141bfae030f43651ff9272f0f28a208a9e6a5a", size = 169540, upload-time = "2026-07-09T13:49:23.563Z" }, + { url = "https://files.pythonhosted.org/packages/c6/cd/7ede0db66411fa09817d79b680f7454ea9bee2d374e1922e4efd065760a3/uuid_utils-0.17.0-cp314-cp314-win_amd64.whl", hash = "sha256:42275ebd0e8e74e32cdbfb8bd88fc99576567d51d54a508020611fd8f4f463a0", size = 175984, upload-time = "2026-07-09T13:49:24.703Z" }, + { url = "https://files.pythonhosted.org/packages/f0/81/533b5f80cd4918c0693f4e1b7b90ceb1caa45f4266ae8b528135d7ecca5d/uuid_utils-0.17.0-cp314-cp314-win_arm64.whl", hash = "sha256:b5d11cccba076a32321ef1380dea956821f0b51794ef59df64e58fb1cd543aae", size = 174749, upload-time = "2026-07-09T13:49:25.886Z" }, + { url = "https://files.pythonhosted.org/packages/a0/13/f400ac39d06fd8be5b099c09e41bb975205926722a3e8d53348817cb7ff9/uuid_utils-0.17.0-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:fae8b282f0cb22a5de222999f7723f4e5ec04f6fcdf4aaef879b5b36625ae2b0", size = 562610, upload-time = "2026-07-09T13:49:27.374Z" }, + { url = "https://files.pythonhosted.org/packages/03/8c/c71c8312304c56f6d0bcba87cd402fa79bec35d18ffc8c41954196ca68e5/uuid_utils-0.17.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:967955620df45e6cffe2e9950cb9903cb455649396f896b26b04363a91a5054b", size = 289473, upload-time = "2026-07-09T13:49:28.989Z" }, + { url = "https://files.pythonhosted.org/packages/bb/cd/522117e2e5184ca1d4f0f85ee833e9e21bd8c6b99eff8a4d1a8e5a194e33/uuid_utils-0.17.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:375cde148430d60a4a07c03abaa0774c4fddfdd90de99b4ba02f24088bc9d750", size = 321600, upload-time = "2026-07-09T13:49:30.4Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f4/0d81f9bd346fc717bc561c08fa6457e0328966eb76e536b938fe77d56459/uuid_utils-0.17.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:975c17da26c5b9d46c336b03c52a057ac28378d6f9d98b58d32a038589bb3912", size = 329569, upload-time = "2026-07-09T13:49:31.732Z" }, + { url = "https://files.pythonhosted.org/packages/5e/41/26e1363f36a94c9e8ec2dd21d5f63088d3e7c723adbb12dcc8fdc77be417/uuid_utils-0.17.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3150d836290c88f1d26eb59c4db280d87417dd3bfaadd2889c77416c8f0ff6fa", size = 442051, upload-time = "2026-07-09T13:49:33.024Z" }, + { url = "https://files.pythonhosted.org/packages/2b/a7/2c1ed1b34d7df7fdcc11c28fd26d94d44843b37d9af2435ff9fd8abdbc08/uuid_utils-0.17.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9472a8de37faf8bd216c628e0e68c8f6bef730d3ba0a5060f3b0fa460c992ac2", size = 324372, upload-time = "2026-07-09T13:49:34.554Z" }, + { url = "https://files.pythonhosted.org/packages/78/bf/328d3c6bb22c496944a1b3b732207d71aa6964eb604e5e3b9dcb91ed0a00/uuid_utils-0.17.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d27c531edb8d1f38ca2eddaa1fa24913a460aeb721f2efd4ef42a124ce94e354", size = 348548, upload-time = "2026-07-09T13:49:35.898Z" }, + { url = "https://files.pythonhosted.org/packages/3e/76/a07de5cb7b90582fdbbc830fd19be129cbbb9897cfe239fef469d7bd2d09/uuid_utils-0.17.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5670c52a438e21483ce715776144914a4e2a2a5c62d9dee15f8a3e90cf128ae6", size = 498985, upload-time = "2026-07-09T13:49:37.142Z" }, + { url = "https://files.pythonhosted.org/packages/f4/62/9966e46ae34fcec6b06119631fb3c09705ea78835035ce3a82d3348eb61a/uuid_utils-0.17.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:6f29689a76fe7a49cbd629a794d0ec1eab48814e323a00a146a741b0195bde68", size = 605183, upload-time = "2026-07-09T13:49:38.648Z" }, + { url = "https://files.pythonhosted.org/packages/d7/4e/bb962ba0fe31e903b199f22cf4c1a6cba35a8987aef526d287277ab8ca8b/uuid_utils-0.17.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:4441600447d340ae103a353f01dbcd22ff680e5ee1a22988efe8d7b791d8fdb3", size = 565412, upload-time = "2026-07-09T13:49:40.115Z" }, + { url = "https://files.pythonhosted.org/packages/ce/9e/122adfeeeae8a84ccfd43bce627b104d12a2180a93bffd2c0e1b54dad7a6/uuid_utils-0.17.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7b04935a79c03c41ad08d0a5f390aac968bfb561f1268897bc5b0f077971efd", size = 529885, upload-time = "2026-07-09T13:49:41.513Z" }, + { url = "https://files.pythonhosted.org/packages/b3/4f/257304dded339dc35fc9bf35722ac68fd4fdb930f255b8f7bccdf74ebba9/uuid_utils-0.17.0-cp314-cp314t-win32.whl", hash = "sha256:239d8a281fe10bae33205b5d43185834d556b18434e0a113b5dc1dfb2fd97e91", size = 169472, upload-time = "2026-07-09T13:49:42.871Z" }, + { url = "https://files.pythonhosted.org/packages/35/c8/e78c06db7e9ce317ce7b8759ff2058333eac75caa8c22b75f0059589c9be/uuid_utils-0.17.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e288a06cbbbcd01b44386e767985c9e21d2ad9bf59829aa7058d9a2a494804ab", size = 176271, upload-time = "2026-07-09T13:49:44.105Z" }, + { url = "https://files.pythonhosted.org/packages/a7/11/bd1c70e1ad3301163cebe66c8d26de26e6814d52f642a849448bd2833626/uuid_utils-0.17.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1776a80d16369999b21627028cc5dbce819be83e1e079fdd7a51b587d2916db9", size = 175004, upload-time = "2026-07-09T13:49:45.591Z" }, + { url = "https://files.pythonhosted.org/packages/ee/14/4ae708968b15cac7b68d5b854bfce724b21faa1c7a5147fb96d87f468a45/uuid_utils-0.17.0-pp311-pypy311_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7b9044ce4acbf392d4b3a503fe377641f4deff82e6c341c36ef27af0dea76cdf", size = 567823, upload-time = "2026-07-09T13:49:46.902Z" }, + { url = "https://files.pythonhosted.org/packages/4c/e2/d3af9c3d1dc6efb9ee1cffab30f3f2aacacc3892b21b495d78d34c6696bc/uuid_utils-0.17.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9a91c4814c7150a4d798da691b7804eacd78c4b84fb392a60fa0de21341861eb", size = 288763, upload-time = "2026-07-09T13:49:48.491Z" }, + { url = "https://files.pythonhosted.org/packages/bc/c2/f1b183e412387529893015a94a8447633c665f6d0392de20e245680e636a/uuid_utils-0.17.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dd4a21baaac9a88486f0dd166c5793feb101a0bb9f006f2c401657fff5a1343", size = 324919, upload-time = "2026-07-09T13:49:49.972Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3c/d32c799bdd51f3b08b6ee95f9de921b59c69075a96767f937fab55014813/uuid_utils-0.17.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32abaafc8e91928b3d9f4d82e42d2094041e38ad6bb964066faadff28e4162f1", size = 332689, upload-time = "2026-07-09T13:49:51.402Z" }, + { url = "https://files.pythonhosted.org/packages/6f/90/b4cd455619ff276dc3c3262a7420ead63aa1e531362f00df4cdb07d90e0a/uuid_utils-0.17.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd741c73440b328f937dc53b344ecadc46bc4f0cec0333a8f42b55f3468ce7ec", size = 445726, upload-time = "2026-07-09T13:49:52.757Z" }, + { url = "https://files.pythonhosted.org/packages/e2/f1/5cc042a37932aa9a66eb8ab4a9a5b31d80261ae4565ff0193d8cc1fb9392/uuid_utils-0.17.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89a0980d49683c00539c59cd9f46b1908c538e6b5b0a48ad12187bb856d0f391", size = 325610, upload-time = "2026-07-09T13:49:54.191Z" }, + { url = "https://files.pythonhosted.org/packages/5e/72/9e800c41d766484484e97845a7a7f677ba94462df86c97183e0290229d16/uuid_utils-0.17.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:de1064663aa7c839286488a319d2b3b478ca5ab5b2091ade888ed0eeca11a98a", size = 352672, upload-time = "2026-07-09T13:49:55.748Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8e/86ce2c03a1d9674530f6649e49067f7c69929600127077731de590d12132/uuid_utils-0.17.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2db386941cfdecdd0b5a8ceeed5cf7479c83d1730dcf64a48d43cfa018cc3310", size = 178681, upload-time = "2026-07-09T13:49:57.096Z" }, ] [[package]] name = "uvicorn" -version = "0.49.0" +version = "0.51.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/1f/fa18009dea8469069cca78a4e877a008ab78f08b064bfc9ab891579077ff/uvicorn-0.49.0.tar.gz", hash = "sha256:ebf4271aa580d9de97f93192d4595176df6e91f9aae919ca73e4fc07df1e66a3", size = 91284, upload-time = "2026-06-03T22:01:30.448Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/65/b7c6c443ccc58678c91e1e973bbe2a878591538655d6e1d47f24ba1c51f3/uvicorn-0.51.0.tar.gz", hash = "sha256:f6f4b69b657c312f516dd2d268ab9ae6f254b11e4bac504f37b2ab58b24dd0b0", size = 94412, upload-time = "2026-07-08T10:59:05.962Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/fa/e1388bbcf24ef3274f45c0c1c7b501fd14971037c1b6ee23610553307497/uvicorn-0.49.0-py3-none-any.whl", hash = "sha256:ba3d14c3ee7e41c6c654c46c9eb489d33213cdd30aa1696eab1374337c13f68f", size = 71376, upload-time = "2026-06-03T22:01:29.037Z" }, + { url = "https://files.pythonhosted.org/packages/45/ec/dbb7e5a6b91f86bfb9eb7d2988a2730907b6a729875b949c7f022e8b88fa/uvicorn-0.51.0-py3-none-any.whl", hash = "sha256:5d38af6cd620f2ae3849fb44fd4879e0890aa1febe8d47eb355fb45d93fe6a5b", size = 73219, upload-time = "2026-07-08T10:59:04.44Z" }, ] [[package]] @@ -6309,13 +5362,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, ] +[[package]] +name = "wcmatch" +version = "11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bracex" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/25/1da725838132221e33568973da484ff43813662ccc06ebf7f6e3abddfcd5/wcmatch-11.0.tar.gz", hash = "sha256:55d95c2447789712774b198ceec72939e88b5618f1f8f0a9b605bf7740b63b96", size = 141360, upload-time = "2026-07-10T05:50:24.183Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/12/f38b6fee116274d7221743caab07d765032e1370bb54cad8714f87aeb0e8/wcmatch-11.0-py3-none-any.whl", hash = "sha256:3a5977ace27e075eef67eb03d539563f1a19018b62881949a42932cf66926934", size = 42914, upload-time = "2026-07-10T05:50:22.995Z" }, +] + [[package]] name = "wcwidth" -version = "0.8.1" +version = "0.8.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/b4/51fe890511f0f242d07cb1ebe6a5b6db417262b9d2568b460347c57d95cc/wcwidth-0.8.1.tar.gz", hash = "sha256:faf5b4a5366a72dc49cad48cdf21f52bdf63bdda995178e483ba247ff79089b9", size = 1466072, upload-time = "2026-06-08T05:57:23.146Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/74/c6428f875774288bec1396f5bfcbc2d925700a4dad61727fd5f2b12f249d/wcwidth-0.8.2.tar.gz", hash = "sha256:91fbef97204b96a3d4d421609b80340b760cf33e26da123ff243d76b1fda8dda", size = 1466253, upload-time = "2026-06-29T18:11:11.601Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/6e/95b0e537de1f4d4301f76f944642c6da50d1511cc7b3d64dc418a66c7509/wcwidth-0.8.1-py3-none-any.whl", hash = "sha256:f453740b1e4a4f3291faa37944c555d71056c4da08d59809b307ef4feba695c8", size = 323092, upload-time = "2026-06-08T05:57:21.413Z" }, + { url = "https://files.pythonhosted.org/packages/96/42/3e5985a0a7e57de470b320c6d6a1a67c844f6737a587f3d44dd13d1819e7/wcwidth-0.8.2-py3-none-any.whl", hash = "sha256:d63947694a0539a1d51e01eda7caf800c291020e6cdd7e28ad7b14dd33ad4f85", size = 323166, upload-time = "2026-06-29T18:11:09.888Z" }, ] [[package]] @@ -6469,159 +5534,159 @@ wheels = [ [[package]] name = "xxhash" -version = "3.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/2f/e183a1b407002f5af81822bee18b61cdb94b8670208ef34734d8d2b8ebe9/xxhash-3.7.0.tar.gz", hash = "sha256:6cc4eefbb542a5d6ffd6d70ea9c502957c925e800f998c5630ecc809d6702bae", size = 82022, upload-time = "2026-04-25T11:10:32.553Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/49/e4b575b4ed170a7f640c8bd69cfadfa81c7b700191fde5e72228762b9f73/xxhash-3.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cd8ab85c916a58d5c8656ea15e3ce9df836fe2f120a74c296e01d69fab2614b4", size = 33426, upload-time = "2026-04-25T11:05:15.702Z" }, - { url = "https://files.pythonhosted.org/packages/07/61/40f0155b0b09988eb6cdbfc52652f2f371810b0c58163208cb05667757bd/xxhash-3.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:85f5c0e26d945b5bb475e0a3d95193117498130baa7619357bdc7869c2391b5a", size = 30859, upload-time = "2026-04-25T11:05:17.708Z" }, - { url = "https://files.pythonhosted.org/packages/12/bd/2902b7aad574e43cd85fd84849cfbce48c52cb02c7d6902b8a2b3f6e668e/xxhash-3.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b7ffeaada9f8699be63d639536b0b60dff73b7d3325b7475c5bc8fdbf4eed47f", size = 193839, upload-time = "2026-04-25T11:05:19.364Z" }, - { url = "https://files.pythonhosted.org/packages/48/df/343ce8fd09e47ba8fba43b3bad3283ddf0deca799d5a27b084c3aa2ce502/xxhash-3.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cee88dfaa6b1b2bfadd3c031fa5f05584870e62fb05dc500942e9900c44fcfda", size = 212896, upload-time = "2026-04-25T11:05:21.131Z" }, - { url = "https://files.pythonhosted.org/packages/79/cf/703e8422a8b52407864281fb4eb52c605e9f33180413b4458f05de110eba/xxhash-3.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7426ff0dfa76eb47efc2cc59d4a717bfa9dc9938bff5e49e748bca749f6aa616", size = 235896, upload-time = "2026-04-25T11:05:22.988Z" }, - { url = "https://files.pythonhosted.org/packages/ed/bc/d4b039edbd426575add5f217abeeb2bf870e2c510d35445df81b4f457901/xxhash-3.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e8ff6ec73110f610425caef3ea875afbfc34caa542f01df3a80f45aadeb9f906", size = 211665, upload-time = "2026-04-25T11:05:24.799Z" }, - { url = "https://files.pythonhosted.org/packages/42/24/c6f81361796814b92399a88bf079d3b65e617f531819128fcf1bd6ef0571/xxhash-3.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0d23fd49fdc5c8af61fb7104f1ad247954499140f6cb6045b3aa5c99dadbbf28", size = 444929, upload-time = "2026-04-25T11:05:26.245Z" }, - { url = "https://files.pythonhosted.org/packages/a4/db/268012153eb7f6bf2c8a0491fdcde11e093f166990821a2ab754fe95537d/xxhash-3.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12c249621af6d50a05d9f10af894b404157b15819878e18f75fcbb0213a77d07", size = 193271, upload-time = "2026-04-25T11:05:28.282Z" }, - { url = "https://files.pythonhosted.org/packages/0a/86/1d0d905d659850dad7f59c807c130249fdb204dc6f71f1fb36268f3f3e61/xxhash-3.7.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6741564a923f082f3c2941c8bb920462ed5b25eaebdd1e161f162233c9a10bc5", size = 284580, upload-time = "2026-04-25T11:05:30.116Z" }, - { url = "https://files.pythonhosted.org/packages/1f/52/fc01ca7ff425a9bdb38d9e3a17f2630447ce3b45d45a929a6cd94d469334/xxhash-3.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4fd8acc6e32596350619896feb372033c0920975992d29837c32853bb1feacd", size = 210193, upload-time = "2026-04-25T11:05:31.969Z" }, - { url = "https://files.pythonhosted.org/packages/ec/96/122e0c6a3537a54b30752031dca557182576bae1a4171c0be8c532c84496/xxhash-3.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:646a69b56d8145d85f7fd2289d14fba07880c8a5bda406aa256b407481a61f35", size = 241094, upload-time = "2026-04-25T11:05:33.651Z" }, - { url = "https://files.pythonhosted.org/packages/d8/17/92e33338db8c18add33a46b56c2b7d5dcc6cc2ac076c45389f6017b1bf37/xxhash-3.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:11dd69b1a34b7b9af29012f390825b0cdb0617c0966560e227ca74daa7478ba9", size = 197721, upload-time = "2026-04-25T11:05:35.387Z" }, - { url = "https://files.pythonhosted.org/packages/c7/04/fd4114a0820913f336bef5c82ef851bde8d06270982ebd7b2a859961bbf2/xxhash-3.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:01cf5c5333aed26cc8d5eea33b8d6398e085e365a704b7372fabdf7ab06441a9", size = 210073, upload-time = "2026-04-25T11:05:37.405Z" }, - { url = "https://files.pythonhosted.org/packages/dd/eb/a2472b8b81cd576a9af3a4889ad8ba5784e8c5a04592587056cdaededd6c/xxhash-3.7.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f1e65d52c2d526734abecb98372c256b7eacce8fdc42e0df8570417fb39e2772", size = 274960, upload-time = "2026-04-25T11:05:39.224Z" }, - { url = "https://files.pythonhosted.org/packages/3d/d3/493afc544aae50b5fb2844ceaeb3697283bb59695db1a7cb40448636de05/xxhash-3.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8ff00fcc3eb436617ed8556cf15daf76c2b501248361a065625a588af78a0a02", size = 413113, upload-time = "2026-04-25T11:05:40.669Z" }, - { url = "https://files.pythonhosted.org/packages/50/6a/002800845a22bff32bcf5fd09caceb4d3f5c3da6b754c46edb9743ce908b/xxhash-3.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b5cd29840505631c6f7dbb8a5d34b742b5e6bbda38fe0b9f54e825f3ea6b61dc", size = 190677, upload-time = "2026-04-25T11:05:42.403Z" }, - { url = "https://files.pythonhosted.org/packages/f4/0f/86ee514622a381c0dc49167c8d431a22aa93518a4063559c3e36e4b82bc8/xxhash-3.7.0-cp310-cp310-win32.whl", hash = "sha256:5bf2f1940499839b39fef1561b5ecb6ede9ac34ef4457474e1337fc7ef07c2f3", size = 30627, upload-time = "2026-04-25T11:05:44.022Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/2ef2310803efb4a2d07844e8098d797e25702024793aa2e85858623a43b5/xxhash-3.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:d41fcda2fa8ca682ebca134a2f2dc02575ba549267585597e73061565795f475", size = 31463, upload-time = "2026-04-25T11:05:45.218Z" }, - { url = "https://files.pythonhosted.org/packages/9e/75/40dbf8f142baf8993c38cd988c8d8f51fe0c51e6c84c5769a3c0280a651d/xxhash-3.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:a845a59664d5c531525a467470220f8edc37959e0a6f8e734ffb6654da5c4bee", size = 27747, upload-time = "2026-04-25T11:05:46.422Z" }, - { url = "https://files.pythonhosted.org/packages/3b/f4/7bd35089ff1f8e2c96baa2dce05775a122aacd2e3830a73165e27a4d0848/xxhash-3.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fdc7d06929ae28dda98297a18eef7b0fd38991a3b405d8d7b55c9ef24c296958", size = 33423, upload-time = "2026-04-25T11:05:47.628Z" }, - { url = "https://files.pythonhosted.org/packages/a3/26/4e00c88a6a2c8a759cfb77d2a9a405f901e8aa66e60ef1fd0aeb35edda48/xxhash-3.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea6daa712f4e094a30830cf01e9b47d03b24d05cc9dab8609f0d9a9db8454712", size = 30857, upload-time = "2026-04-25T11:05:49.189Z" }, - { url = "https://files.pythonhosted.org/packages/82/2f/eeb942c17a5a761a8f01cb9180a0b76bfb62a2c39e6f46b1f9001899027a/xxhash-3.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9e6c0d843f1daf85ea23aeb053579135552bde575b7b98af20bfc667b6e4548d", size = 194702, upload-time = "2026-04-25T11:05:50.457Z" }, - { url = "https://files.pythonhosted.org/packages/0e/fd/96f132c08b1e5951c68691d3b9ec351ec2edc028f6a01fcd294f46b9d9f0/xxhash-3.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:363c139bf15e1ac5f136b981d3c077eb551299b1effede7f12faa010b8590a60", size = 213613, upload-time = "2026-04-25T11:05:52.571Z" }, - { url = "https://files.pythonhosted.org/packages/82/89/d4e92b796c5ed052d29ed324dbfc1dc1188e0c4bf64bebbf0f8fc20698df/xxhash-3.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a778b25874cb0f862eaab5986bff4ca49ffb0def7c0a34c237b948b3c6c775b2", size = 236726, upload-time = "2026-04-25T11:05:54.395Z" }, - { url = "https://files.pythonhosted.org/packages/40/f1/81fc4361921dc6e557a9c60cb3712f36d244d06eeeb71cd2f4252ac42678/xxhash-3.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e1860f1e43d40e9d904cf22d93e587ea42e010ebce4160877e46bcab4bc232a", size = 212443, upload-time = "2026-04-25T11:05:56.334Z" }, - { url = "https://files.pythonhosted.org/packages/6a/d0/afeddd4cff50a332f50d4b8a2e8857673153ab0564ef472fcdeb0b5430df/xxhash-3.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9122ad6f867c4a0f5e655f5c3bdf89103852009dbb442a3d23e688b9e699e800", size = 445793, upload-time = "2026-04-25T11:05:58.953Z" }, - { url = "https://files.pythonhosted.org/packages/f7/d0/3c91e4e6a05ca4d7df8e39ec3a75b713609258ec84705ab34be6430826a1/xxhash-3.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d9110d0c3fb02679972837a033251fd186c529aa62f19c132fc909c74052b8", size = 193937, upload-time = "2026-04-25T11:06:00.546Z" }, - { url = "https://files.pythonhosted.org/packages/4e/3a/a6b0772d9801dd4bea4ca4fd34734d6e9b51a711c8a611a24a79de26a878/xxhash-3.7.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:347a93f2b4ce67ce61959665e32a7447c380f8347e55e100daa23766baacf0e5", size = 285188, upload-time = "2026-04-25T11:06:01.96Z" }, - { url = "https://files.pythonhosted.org/packages/6c/f8/cf8e31fd7282230fe7367cd501a2e75b4b67b222bfc7eacccfc20d2652cb/xxhash-3.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:acbb48679ddf3852c45280c10ff10d52ca2cd1da2e552fb81db1ff786c75d0e4", size = 210966, upload-time = "2026-04-25T11:06:03.453Z" }, - { url = "https://files.pythonhosted.org/packages/cc/f0/fd36cc4a81bf52ee5633275daae2b93dd958aace67fd4f5d466ec83b5f35/xxhash-3.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:fe14c356f8b23ad811dc026077a6d4abccdaa7bce5ca98579605550657b6fcfb", size = 241994, upload-time = "2026-04-25T11:06:05.264Z" }, - { url = "https://files.pythonhosted.org/packages/08/e1/67f5d9c9369be42eaf99ba02c01bf14c5ecd67087b02567960bfcee43b63/xxhash-3.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f420ad3d41e38194353a498bbc9561fd5a9973a27b536ce46d8583479cf44335", size = 198707, upload-time = "2026-04-25T11:06:07.044Z" }, - { url = "https://files.pythonhosted.org/packages/50/17/a4c865ca22d2da6b1bc7d739bf88cab209533cf52ba06ca9da27c3039bee/xxhash-3.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:693d02c6dc7d1aa0a45921d54cd8c1ff629e09dfdc2238471507af1f7a1c6f04", size = 210917, upload-time = "2026-04-25T11:06:08.853Z" }, - { url = "https://files.pythonhosted.org/packages/49/8b/453b35810d697abac3c96bde3528bece685869227da274eb80a4a4d4a119/xxhash-3.7.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:14bf7a54e43825ec131ee7fe3c60e142e7c2c1e676ad0f93fc893432d15414af", size = 275772, upload-time = "2026-04-25T11:06:10.645Z" }, - { url = "https://files.pythonhosted.org/packages/b5/ad/4eed7eab07fd3ee6678f416190f0413d097ab5d7c1278906bf1e9549d789/xxhash-3.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ae3a39a4d96bdb6f8d154fd7f490c4ad06f0532fcd2bb656052a9a7762cf5d31", size = 414068, upload-time = "2026-04-25T11:06:12.511Z" }, - { url = "https://files.pythonhosted.org/packages/d3/4e/fd6f8a680ba248fdb83054fa71a8bfa3891225200de1708b888ef2c49829/xxhash-3.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1cc07c639e3a77ef1d32987464d3e408565b8a3be57b545d3542b191054d9923", size = 191459, upload-time = "2026-04-25T11:06:14.07Z" }, - { url = "https://files.pythonhosted.org/packages/50/7c/8cb34b3bed4f44ca6827a534d50833f9bc6c006e83b0eb410ac9fa0793bd/xxhash-3.7.0-cp311-cp311-win32.whl", hash = "sha256:3281ba1d1e60ee7a382a7b958513ba03c2c0d5fcbd9a6f7517c0a81251a23422", size = 30628, upload-time = "2026-04-25T11:06:15.802Z" }, - { url = "https://files.pythonhosted.org/packages/0b/47/a49767bd7b40782bedae9ff0721bfe1d7e4dd9dc1585dea684e57ba67c20/xxhash-3.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:a7f25baec4c5d851d40718d6fae52285b31683093d4ff5207e63ab306ccf14a5", size = 31461, upload-time = "2026-04-25T11:06:17.104Z" }, - { url = "https://files.pythonhosted.org/packages/7c/c6/3957bfacfb706bd687be246dfa8dd60f8df97c44186d229f7fd6e26c4b7e/xxhash-3.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:4c2454448ce847c72635827bb75c15c5a3434b03ee1afd28cb6dc6fb2597d830", size = 27746, upload-time = "2026-04-25T11:06:18.716Z" }, - { url = "https://files.pythonhosted.org/packages/f2/8a/51a14cdef4728c6c2337db8a7d8704422cc65676d9199d77215464c880af/xxhash-3.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:082c87bfdd2b9f457606c7a4a53457f4c4b48b0cdc48de0277f4349d79bb3d7a", size = 33357, upload-time = "2026-04-25T11:06:20.44Z" }, - { url = "https://files.pythonhosted.org/packages/b9/1b/0c2c933809421ffd9bf42b59315552c143c755db5d9a816b2f1ae273e884/xxhash-3.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5e7ce913b61f35b0c1c839a49ac9c8e75dd8d860150688aed353b0ce1bf409d8", size = 30869, upload-time = "2026-04-25T11:06:21.989Z" }, - { url = "https://files.pythonhosted.org/packages/03/a8/89d5fdd6ee12d70ba99451de46dd0e8010167468dcd913ec855653f4dd50/xxhash-3.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3beb1de3b1e9694fcdd853e570ee64c631c7062435d2f8c69c1adf809bc086f0", size = 194100, upload-time = "2026-04-25T11:06:23.586Z" }, - { url = "https://files.pythonhosted.org/packages/87/ee/2f9f2ed993e77206d1e66991290a1ebe22e843351ca3ebec8e49e01ba186/xxhash-3.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3e7b689c3bce16699efcf736066f5c6cc4472c3840fe4b22bd8279daf4abdac", size = 212977, upload-time = "2026-04-25T11:06:25.019Z" }, - { url = "https://files.pythonhosted.org/packages/de/60/5a91644615a9e9d4e42c2e9925f1908e3a24e4e691d9de7340d565bea024/xxhash-3.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a6545e6b409e3d5cbafc850fb84c55a1ca26ed15a6b11e3bf07a0e0cd84517c8", size = 236373, upload-time = "2026-04-25T11:06:26.482Z" }, - { url = "https://files.pythonhosted.org/packages/22/c0/f3a9384eaaed9d14d4d062a5d953aa0da489bfe9747877aa994caa87cd0b/xxhash-3.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:31ab1461c77a11461d703c88eb949e132a1c6515933cf675d97ec680f4bd18de", size = 212229, upload-time = "2026-04-25T11:06:28.065Z" }, - { url = "https://files.pythonhosted.org/packages/2e/67/02f07a9fd79726804190f2172c4894c3ed9a4ebccaca05653c84beb58025/xxhash-3.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c4d596b7676f811172687ec567cbafb9e4dea2f9be1bbb4f622410cb7f40f40", size = 445462, upload-time = "2026-04-25T11:06:30.048Z" }, - { url = "https://files.pythonhosted.org/packages/40/37/558f5a90c0672fc9b4402dc25d87ac5b7406616e8969430c9ca4e52ee74d/xxhash-3.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13805f0461cba0a857924e70ff91ae6d52d2598f79a884e788db80532614a4a1", size = 193932, upload-time = "2026-04-25T11:06:31.857Z" }, - { url = "https://files.pythonhosted.org/packages/d5/90/aaa09cd58661d32044dbbad7df55bbe22a623032b810e7ed3b8c569a2a6f/xxhash-3.7.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d398f372496152f1c6933a33566373f8d1b37b98b8c9d608fa6edc0976f23b2", size = 284807, upload-time = "2026-04-25T11:06:33.697Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f3/53df3719ab127a02c174f0c1c74924fcd110866e89c966bc7909cfa8fa84/xxhash-3.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d610aa62cdb7d4d497740741772a24a794903bf3e79eaa51d2e800082abe11e5", size = 210445, upload-time = "2026-04-25T11:06:35.488Z" }, - { url = "https://files.pythonhosted.org/packages/72/33/d219975c0e8b6fa2eb9ccd486fe47e21bf1847985b878dd2fbc3126e0d5c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:073c23900a9fbf3d26616c17c830db28af9803677cd5b33aea3224d824111514", size = 241273, upload-time = "2026-04-25T11:06:37.24Z" }, - { url = "https://files.pythonhosted.org/packages/3e/50/49b1afe610eb3964cedcb90a4d4c3d46a261ee8669cbd4f060652619ae3c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:418a463c3e6a590c0cdc890f8be19adb44a8c8acd175ca5b2a6de77e61d0b386", size = 197950, upload-time = "2026-04-25T11:06:39.148Z" }, - { url = "https://files.pythonhosted.org/packages/c6/75/5f42a1a4c78717d906a4b6a140c6dbf837ab1f547a54d23c4e2903310936/xxhash-3.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:03f8ff4474ee61c845758ce00711d7087a770d77efb36f7e74a6e867301000b8", size = 210709, upload-time = "2026-04-25T11:06:40.958Z" }, - { url = "https://files.pythonhosted.org/packages/8a/85/237e446c25abced71e9c53d269f2cef5bab8a82b3f88a12e00c5368e7368/xxhash-3.7.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:44fba4a5f1d179b7ddc7b3dc40f56f9209046421679b57025d4d8821b376fd8d", size = 275345, upload-time = "2026-04-25T11:06:42.525Z" }, - { url = "https://files.pythonhosted.org/packages/62/34/c2c26c0a6a9cc739bc2a5f0ae03ba8b87deb12b8bce35f7ac495e790dc6d/xxhash-3.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31e3516a0f829d06ded4a2c0f3c7c5561993256bfa1c493975fb9dc7bfa828a1", size = 414056, upload-time = "2026-04-25T11:06:44.343Z" }, - { url = "https://files.pythonhosted.org/packages/a0/aa/5c58e9bc8071b8afd8dcf297ff362f723c4892168faba149f19904132bf4/xxhash-3.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b59ee2ac81de57771a09ecad09191e840a1d2fae1ef684208320591055768f83", size = 191485, upload-time = "2026-04-25T11:06:46.262Z" }, - { url = "https://files.pythonhosted.org/packages/d4/69/a929cf9d1e2e65a48b818cdce72cb6b69eab2e6877f21436d0a1942aff43/xxhash-3.7.0-cp312-cp312-win32.whl", hash = "sha256:74bbd92f8c7fcc397ba0a11bfdc106bc72ad7f11e3a60277753f87e7532b4d81", size = 30671, upload-time = "2026-04-25T11:06:48.039Z" }, - { url = "https://files.pythonhosted.org/packages/b9/1b/104b41a8947f4e1d4a66ce1e628eea752f37d1890bfd7453559ca7a3d950/xxhash-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:7bd7bc82dd4f185f28f35193c2e968ef46131628e3cac62f639dadf321cba4d1", size = 31514, upload-time = "2026-04-25T11:06:49.279Z" }, - { url = "https://files.pythonhosted.org/packages/98/a0/1fd0ea1f1b886d9e7c73f0397571e22333a7d79e31da6d7127c2a4a71d75/xxhash-3.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:7d7148180ec99ba36585b42c8c5de25e9b40191613bc4be68909b4d25a77a852", size = 27761, upload-time = "2026-04-25T11:06:50.448Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ca/d5174b4c36d10f64d4ca7050563138c5a599efb01a765858ddefc9c1202a/xxhash-3.7.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:4b6d6b33f141158692bd4eafbb96edbc5aa0dabdb593a962db01a91983d4f8fa", size = 36813, upload-time = "2026-04-25T11:06:51.73Z" }, - { url = "https://files.pythonhosted.org/packages/41/d0/abc6c9d347ba1f1e1e1d98125d0881a0452c7f9a76a9dd03a7b5d2197f23/xxhash-3.7.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:845d347df254d6c619f616afa921331bada8614b8d373d58725c663ba97c3605", size = 35121, upload-time = "2026-04-25T11:06:53.048Z" }, - { url = "https://files.pythonhosted.org/packages/bf/11/4cc834eb3d79f2f2b3a6ef7324195208bcdfbdcf7534d2b17267aa5f3a8f/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:fddbbb69a6fff4f421e7a0d1fa28f894b20112e9e3fab306af451e2dfd0e459b", size = 29624, upload-time = "2026-04-25T11:06:54.311Z" }, - { url = "https://files.pythonhosted.org/packages/23/83/e97d3e7b635fe73a1dfb1e91f805324dd6d930bb42041cbf18f183bc0b6d/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:54876a4e45101cec2bf8f31a973cda073a23e2e108538dad224ba07f85f22487", size = 30638, upload-time = "2026-04-25T11:06:55.864Z" }, - { url = "https://files.pythonhosted.org/packages/f4/40/d84951d80c35db1f4c40a29a64a8520eea5d56e764c603906b4fe763580f/xxhash-3.7.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:0c72fe9c7e3d6dfd7f1e21e224a877917fa09c465694ba4e06464b9511b65544", size = 33323, upload-time = "2026-04-25T11:06:57.336Z" }, - { url = "https://files.pythonhosted.org/packages/89/cc/c7dc6558d97e9ab023f663d69ab28b340ed9bf4d2d94f2c259cf896bb354/xxhash-3.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a6d73a830b17ef49bc04e00182bd839164c1b3c59c127cd7c54fcb10c7ed8ee8", size = 33362, upload-time = "2026-04-25T11:06:58.656Z" }, - { url = "https://files.pythonhosted.org/packages/2a/6e/46b84017b1301d54091430353d4ad5901654a3e0871649877a416f7f1644/xxhash-3.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91c3b07cf3362086d8f126c6aecd8e5e9396ad8b2f2219ea7e49a8250c318acd", size = 30874, upload-time = "2026-04-25T11:06:59.834Z" }, - { url = "https://files.pythonhosted.org/packages/df/5e/8f9158e3ab906ad3fec51e09b5ea0093e769f12207bfa42a368ca204e7ab/xxhash-3.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:50e879ebbac351c81565ca108db766d7832f5b8b6a5b14b8c0151f7190028e3d", size = 194185, upload-time = "2026-04-25T11:07:01.658Z" }, - { url = "https://files.pythonhosted.org/packages/f3/29/a804ded9f5d3d3758292678d23e7528b08fda7b7e750688d08b052322475/xxhash-3.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:921c14e93817842dd0dd9f372890a0f0c72e534650b6ab13c5be5cd0db11d47e", size = 213033, upload-time = "2026-04-25T11:07:03.606Z" }, - { url = "https://files.pythonhosted.org/packages/8b/91/1ce5a7d2fdc975267320e2c78fc1cecfe7ab735ccbcf6993ec5dd541cb2c/xxhash-3.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e64a7c9d7dfca3e0fafcbc5e455519090706a3e36e95d655cec3e04e79f95aaa", size = 236140, upload-time = "2026-04-25T11:07:05.396Z" }, - { url = "https://files.pythonhosted.org/packages/34/04/fd595a4fd8617b05fa27bd9b684ecb4985bfed27917848eea85d54036d06/xxhash-3.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2220af08163baf5fa36c2b8af079dc2cbe6e66ae061385267f9472362dfd53c6", size = 212291, upload-time = "2026-04-25T11:07:06.966Z" }, - { url = "https://files.pythonhosted.org/packages/03/fb/f1a379cbc372ae5b9f4ab36154c48a849ca6ebe3ac477067a57865bf3bc6/xxhash-3.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f14bb8b22a4a91325813e3d553b8963c10cf8c756cff65ee50c194431296c655", size = 445532, upload-time = "2026-04-25T11:07:08.525Z" }, - { url = "https://files.pythonhosted.org/packages/65/59/172424b79f8cfd4b6d8a122b2193e6b8ad4b11f7159bb3b6f9b3191329bb/xxhash-3.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:496736f86a9bedaf64b0dc70e3539d0766df01c71ea22032698e88f3f04a1ce9", size = 193990, upload-time = "2026-04-25T11:07:10.315Z" }, - { url = "https://files.pythonhosted.org/packages/b9/19/aeac22161d953f139f07ba5586cb4a17c5b7b6dff985122803bb12933500/xxhash-3.7.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0ff71596bd79816975b3de7130ab1ff4541410285a3c084584eeb1c8239996fd", size = 284876, upload-time = "2026-04-25T11:07:12.15Z" }, - { url = "https://files.pythonhosted.org/packages/77/d5/4fd0b59e7a02242953da05ff679fbb961b0a4368eac97a217e11dae110c1/xxhash-3.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1ad86695c19b1d46fe106925db3c7a37f16be37669dcf58dcc70a9dd6e324676", size = 210495, upload-time = "2026-04-25T11:07:13.952Z" }, - { url = "https://files.pythonhosted.org/packages/aa/fb/976a3165c728c7faf74aa1b5ab3cf6a85e6d731612894741840524c7d28c/xxhash-3.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:970f9f8c50961d639cbd0d988c96f80ddf66006de93641719282c4fe7a87c5e6", size = 241331, upload-time = "2026-04-25T11:07:15.557Z" }, - { url = "https://files.pythonhosted.org/packages/4a/2c/6763d5901d53ac9e6ba296e5717ae599025c9d268396e8faa8b4b0a8e0ac/xxhash-3.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5886ad85e9e347911783760a1d16cb6b393e8f9e3b52c982568226cb56927bdc", size = 198037, upload-time = "2026-04-25T11:07:17.563Z" }, - { url = "https://files.pythonhosted.org/packages/61/2b/876e722d533833f5f9a83473e6ba993e48745701096944e77bbecf29b2c3/xxhash-3.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6e934bbae1e0ec74e27d5f0d7f37ef547ce5ff9f0a7e63fb39e559fc99526734", size = 210744, upload-time = "2026-04-25T11:07:19.055Z" }, - { url = "https://files.pythonhosted.org/packages/21/e6/d7e7baef7ce24166b4668d3c48557bb35a23b92ecadcac7e7718d099ab69/xxhash-3.7.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3b6b3d28228af044ebcded71c4a3dd86e1dbd7e2f4645bf40f7b5da65bb5fb5a", size = 275406, upload-time = "2026-04-25T11:07:20.908Z" }, - { url = "https://files.pythonhosted.org/packages/92/fe/198b3763b2e01ca908f2154969a2352ec99bda892b574a11a9a151c5ede4/xxhash-3.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:6be4d70d9ab76c9f324ead9c01af6ff52c324745ea0c3731682a0cf99720f1fe", size = 414125, upload-time = "2026-04-25T11:07:23.037Z" }, - { url = "https://files.pythonhosted.org/packages/3a/6d/019a11affd5a5499137cacca53808659964785439855b5aa40dfd3412916/xxhash-3.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:151d7520838d4465461a0b7f4ae488b3b00de16183dd3214c1a6b14bf89d7fb6", size = 191555, upload-time = "2026-04-25T11:07:24.991Z" }, - { url = "https://files.pythonhosted.org/packages/76/21/b96d58568df2d01533244c3e0e5cbdd0c8b2b25c4bec4d72f19259a292d7/xxhash-3.7.0-cp313-cp313-win32.whl", hash = "sha256:d798c1e291bffb8e37b5bbe0dda77fc767cd19e89cadaf66e6ed5d0ff88c9fe6", size = 30668, upload-time = "2026-04-25T11:07:26.665Z" }, - { url = "https://files.pythonhosted.org/packages/99/57/d849a8d3afa1f8f4bc6a831cd89f49f9706fbbad94d2975d6140a171988c/xxhash-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:875811ba23c543b1a1c3143c926e43996eb27ebb8f52d3500744aa608c275aed", size = 31524, upload-time = "2026-04-25T11:07:27.92Z" }, - { url = "https://files.pythonhosted.org/packages/81/52/bacc753e92dee78b058af8dcef0a50815f5f860986c664a92d75f965b6a5/xxhash-3.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:54a675cb300dda83d71daae2a599389d22db8021a0f8db0dd659e14626eb3ecc", size = 27768, upload-time = "2026-04-25T11:07:29.113Z" }, - { url = "https://files.pythonhosted.org/packages/1c/47/ddbd683b7fc7e592c1a8d9d65f73ce9ab513f082b3967eee2baf549b8fc6/xxhash-3.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a3b19a42111c4057c1547a4a1396a53961dca576a0f6b82bfa88a2d1561764b2", size = 33576, upload-time = "2026-04-25T11:07:30.469Z" }, - { url = "https://files.pythonhosted.org/packages/07/f2/36d3310161db7f72efb4562aadde0ed429f1d0531782dd6345b12d2da527/xxhash-3.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8f4608a06e4d61b7a3425665a46d00e0579122e1a2fae97a0c52953a3aad9aa3", size = 31123, upload-time = "2026-04-25T11:07:31.989Z" }, - { url = "https://files.pythonhosted.org/packages/0d/3f/75937a5c69556ed213021e43cbedd84c8e0279d0d74e7d41a255d84ba4b1/xxhash-3.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad37c7792479e49cf96c1ab25517d7003fe0d93687a772ba19a097d235bbe41e", size = 196491, upload-time = "2026-04-25T11:07:33.358Z" }, - { url = "https://files.pythonhosted.org/packages/22/29/f10d7ff8c7a733d4403a43b9de18c8fabc005f98cec054644f04418659ee/xxhash-3.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc026e3b89d98e30a8288c95cb696e77d150b3f0fb7a51f73dcd49ee6b5577fa", size = 215793, upload-time = "2026-04-25T11:07:34.919Z" }, - { url = "https://files.pythonhosted.org/packages/8b/fd/778f60aa295f58907938f030a8b514611f391405614a525cccd2ffc00eb5/xxhash-3.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9b31ab1f28b078a6a1ac1a54eb35e7d5390deddd56870d0be3a0a733d1c321c", size = 237993, upload-time = "2026-04-25T11:07:36.638Z" }, - { url = "https://files.pythonhosted.org/packages/70/f5/736db5de387b4a540e37a05b84b40dc58a1ce974bfd2b4e5754ce29b68c3/xxhash-3.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3bb5fd680c038fd5229e44e9c493782f90df9bef632fd0499d442374688ff70b", size = 214887, upload-time = "2026-04-25T11:07:38.564Z" }, - { url = "https://files.pythonhosted.org/packages/4d/aa/09a095f22fdb9a27fbb716841fbff52119721f9ca4261952d07a912f7839/xxhash-3.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:030c0fd688fce3569fbb49a2feefd4110cbb0b650186fb4610759ecfac677548", size = 448407, upload-time = "2026-04-25T11:07:40.552Z" }, - { url = "https://files.pythonhosted.org/packages/74/8a/b745efeeca9e34a91c26fdc97ad8514c43d5a81ac78565cba80a1353870a/xxhash-3.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b1bde10324f4c31812ae0d0502e92d916ae8917cad7209353f122b8b8f610c3", size = 196119, upload-time = "2026-04-25T11:07:42.101Z" }, - { url = "https://files.pythonhosted.org/packages/8a/5c/0cfceb024af90c191f665c7933b1f318ee234f4797858383bebd1881d52f/xxhash-3.7.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:503722d52a615f2604f5e7611de7d43878df010dc0053094ef91cb9a9ac3d987", size = 286751, upload-time = "2026-04-25T11:07:43.568Z" }, - { url = "https://files.pythonhosted.org/packages/0b/0a/0793e405dc3cf8f4ebe2c1acec1e4e4608cd9e7e50ea691dabbc2a95ccbb/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c72500a3b6d6c30ebfc135035bcace9eb5884f2dc220804efcaaba43e9f611dd", size = 212961, upload-time = "2026-04-25T11:07:45.388Z" }, - { url = "https://files.pythonhosted.org/packages/0c/7e/721118ffc63bfff94aa565bcf2555a820f9f4bdb0f001e0d609bdfad70de/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:43475925a766d01ca8cd9a857fd87f3d50406983c8506a4c07c4df12adcc867f", size = 243703, upload-time = "2026-04-25T11:07:47.053Z" }, - { url = "https://files.pythonhosted.org/packages/6e/18/16f6267160488b8276fd3d449d425712512add292ba545c1b6946bfdb7dd/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8d09dfd2ab135b985daf868b594315ebe11ad86cd9fea46e6c69f19b28f7d25a", size = 200894, upload-time = "2026-04-25T11:07:48.657Z" }, - { url = "https://files.pythonhosted.org/packages/2d/94/80ba841287fd97e3e9cac1d228788c8ef623746f570404961eec748ecb5c/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c50269d0055ac1faecfd559886d2cbe4b730de236585aba0e873f9d9dadbe585", size = 213357, upload-time = "2026-04-25T11:07:50.257Z" }, - { url = "https://files.pythonhosted.org/packages/a1/7e/106d4067130c59f1e18a55ffadcd876d8c68534883a1e02685b29d3d8153/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1910df4756a5ab58cfad8744fc2d0f23926e3efcc346ee76e87b974abab922f4", size = 277600, upload-time = "2026-04-25T11:07:51.745Z" }, - { url = "https://files.pythonhosted.org/packages/c5/86/a081dd30da71d720b2612a792bfd55e45fa9a07ac76a0507f60487473c25/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d006faf3b491957efcb433489be3c149efe4787b7063d5cddb8ddaefdc60e0c1", size = 416980, upload-time = "2026-04-25T11:07:53.504Z" }, - { url = "https://files.pythonhosted.org/packages/35/29/1a95221a029a3c1293773869e1ab47b07cbbdd82444a42809e8c60156626/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:abb65b4e947e958f7b3b0d71db3ce447d1bc5f37f5eab871ce7223bda8768a04", size = 193840, upload-time = "2026-04-25T11:07:55.103Z" }, - { url = "https://files.pythonhosted.org/packages/c5/e0/db909dd0823285de2286f67e10ee4d81e96ad35d7d8e964ecb07fccd8af9/xxhash-3.7.0-cp313-cp313t-win32.whl", hash = "sha256:178959906cb1716a1ce08e0d69c82886c70a15a6f2790fc084fdd146ca30cd49", size = 30966, upload-time = "2026-04-25T11:07:56.524Z" }, - { url = "https://files.pythonhosted.org/packages/7b/ff/d705b15b22f21ee106adce239cb65d35067a158c630b240270f09b17c2e6/xxhash-3.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2524a1e20d4c231d13b50f7cf39e44265b055669a64a7a4b9a2a44faa03f19b6", size = 31784, upload-time = "2026-04-25T11:07:57.758Z" }, - { url = "https://files.pythonhosted.org/packages/a2/1f/b2cf83c3638fd0588e0b17f22e5a9400bdfb1a3e3755324ac0aee2250b88/xxhash-3.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:37d994d0ffe81ef087bb330d392caa809bb5853c77e22ea3f71db024a0543dba", size = 27932, upload-time = "2026-04-25T11:07:59.109Z" }, - { url = "https://files.pythonhosted.org/packages/0e/cc/431db584f6fbb9312e40a173af027644e5580d39df1f73603cbb9dca4d6b/xxhash-3.7.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:8c5fcfd806c335bfa2adf1cd0b3110a44fc7b6995c3a648c27489bae85801465", size = 36644, upload-time = "2026-04-25T11:08:00.658Z" }, - { url = "https://files.pythonhosted.org/packages/bc/01/255ec513e0a705d1f9a61413e78dfce4e3235203f0ed525a24c2b4b56345/xxhash-3.7.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:506a0b488f190f0a06769575e30caf71615c898ed93ab18b0dbcb6dec5c3713c", size = 35003, upload-time = "2026-04-25T11:08:02.338Z" }, - { url = "https://files.pythonhosted.org/packages/68/70/c55fc33c93445b44d8fc5a17b41ed99e3cebe92bcf8396809e63fc9a1165/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:ec68dbba21532c0173a9872298e65c89749f7c9d21538c3a78b5bb6105871568", size = 29655, upload-time = "2026-04-25T11:08:03.701Z" }, - { url = "https://files.pythonhosted.org/packages/c2/72/ff8de73df000d74467d12a59ce6d6e2b2a368b978d41ab7b1fba5ed442be/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:fa77e7ec1450d415d20129961814787c9abd9a07f98872f070b1fe96c5084611", size = 30664, upload-time = "2026-04-25T11:08:05.011Z" }, - { url = "https://files.pythonhosted.org/packages/b6/91/08416d9bd9bc3bf39d831abe8a5631ac2db5141dfd6fe81c3fe59a1f9264/xxhash-3.7.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fe32736295ea38e43e7d9424053c8c47c9f64fecfc7c895fb3da9b30b131c9ee", size = 33317, upload-time = "2026-04-25T11:08:06.413Z" }, - { url = "https://files.pythonhosted.org/packages/0e/3b/86b1caa4dee10a99f4bf9521e623359341c5e50d05158fa10c275b2bd079/xxhash-3.7.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ab9dd2c83c4bbd63e422181a76f13502d049d3ddcac9a1bdc29196263d692bb8", size = 33457, upload-time = "2026-04-25T11:08:08.099Z" }, - { url = "https://files.pythonhosted.org/packages/ed/38/98ea14ad1517e1461292a65906951458d520689782bfbae111050145bdba/xxhash-3.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3afec3a336a2286601a437cb07562ab0227685e6fbb9ec17e8c18457ff348ecf", size = 30894, upload-time = "2026-04-25T11:08:09.429Z" }, - { url = "https://files.pythonhosted.org/packages/61/a2/074654d0b893606541199993c7db70067d9fc63b748e0d60020a52a1bd36/xxhash-3.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:565df64437a9390f84465dcca33e7377114c7ede8d05cd2cf20081f831ea788e", size = 194409, upload-time = "2026-04-25T11:08:10.91Z" }, - { url = "https://files.pythonhosted.org/packages/e2/26/6d2a1afc468189f77ca28c32e1c83e1b9da1178231e05641dbc1b350e332/xxhash-3.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12eca820a5d558633d423bf8bb78ce72a55394823f64089247f788a7e0ae691e", size = 213135, upload-time = "2026-04-25T11:08:12.575Z" }, - { url = "https://files.pythonhosted.org/packages/8e/0e/d8aecf95e09c42547453137be74d2f7b8b14e08f5177fa2fab6144a19061/xxhash-3.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f262b8f7599516567e070abf607b9af649052b2c4bd6f9be02b0cb41b7024805", size = 236379, upload-time = "2026-04-25T11:08:14.206Z" }, - { url = "https://files.pythonhosted.org/packages/f2/74/8140e8210536b3dd0cc816c4faaeb5ba6e63e8125ab25af4bcddd6a037b3/xxhash-3.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1598916cb197681e03e601901e4ab96a9a963de398c59d0964f8a6f44a2b361", size = 212447, upload-time = "2026-04-25T11:08:15.79Z" }, - { url = "https://files.pythonhosted.org/packages/a0/d2/462001d2903b4bee5a5689598a0a55e5e7cd1ac7f4247a5545cff10d3ebb/xxhash-3.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:322b2f0622230f526aeb1738149948a7ae357a9e2ceb1383c6fd1fdaecdafa16", size = 445660, upload-time = "2026-04-25T11:08:17.441Z" }, - { url = "https://files.pythonhosted.org/packages/23/09/2bd1ed7f8689b20e51727952cac8329d50c694dc32b2eba06ba5bc742b37/xxhash-3.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cc22070880cc57b830a65cde4e65fa884c6d9b28ae4803b5ee05911e7bafba", size = 194076, upload-time = "2026-04-25T11:08:19.134Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6e/692302cd0a5f4ac4e6289f37fa888dc2e1e07750b68fe3e4bfe939b8cea3/xxhash-3.7.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb5a888a968b2434abf9ecda357b5d43f10d7b5a6da6fdbbe036208473aff0e2", size = 284990, upload-time = "2026-04-25T11:08:20.618Z" }, - { url = "https://files.pythonhosted.org/packages/05/d9/e54b159b3d9df7999d2a7c676ce7b323d1b5588a64f8f51ed8172567bd87/xxhash-3.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a999771ff97bec27d18341be4f3a36b163bb1ac41ec17bef6d2dabd84acd33c7", size = 210590, upload-time = "2026-04-25T11:08:22.24Z" }, - { url = "https://files.pythonhosted.org/packages/50/93/0e0df1a3a196ced4ca71de76d65ead25d8e87bbfb87b64306ea47a40c00d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ed4a6efe2dee1655adb73e7ad40c6aa955a6892422b1e3b95de6a34de56e3cbb", size = 241442, upload-time = "2026-04-25T11:08:23.844Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a9/d917a7a814e90b218f8a0d37967105eea91bf752c3303683c99a1f7bfc1f/xxhash-3.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9fd17f14ac0faa12126c2f9ca774a8cf342957265ec3c8669c144e5e6cdb478c", size = 198356, upload-time = "2026-04-25T11:08:25.99Z" }, - { url = "https://files.pythonhosted.org/packages/89/5e/f2ba1877c39469abbefc72991d6ebdcbd4c0880db01ae8cb1f553b0c537d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:05fd1254268c59b5cb2a029dfc204275e9fc52de2913f1e53aa8d01442c96b4d", size = 210898, upload-time = "2026-04-25T11:08:27.608Z" }, - { url = "https://files.pythonhosted.org/packages/90/c6/be56b58e73de531f39a10de1355bb77ceb663900dc4bf2d6d3002a9c3f9e/xxhash-3.7.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a2eae53197c6276d5b317f75a1be226bbf440c20b58bf525f36b5d0e1f657ca6", size = 275519, upload-time = "2026-04-25T11:08:29.301Z" }, - { url = "https://files.pythonhosted.org/packages/92/e2/17ddc85d5765b9c709f192009ed8f5a1fc876f4eb35bba7c307b5b1169f9/xxhash-3.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:bfe6f92e3522dcbe8c4281efd74fa7542a336cb00b0e3272c4ec0edabeaeaf67", size = 414191, upload-time = "2026-04-25T11:08:31.16Z" }, - { url = "https://files.pythonhosted.org/packages/9c/42/85f5b79f4bf1ec7ba052491164adfd4f4e9519f5dc7246de4fbd64a1bd56/xxhash-3.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7ab9a49c410d8c6c786ab99e79c529938d894c01433130353dd0fe999111077a", size = 191604, upload-time = "2026-04-25T11:08:32.862Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d0/6127b623aa4cca18d8b7743592b048d689fd6c6e37ff26a22cddf6cd9d7f/xxhash-3.7.0-cp314-cp314-win32.whl", hash = "sha256:040ea63668f9185b92bc74942df09c7e65703deed71431333678fc6e739a9955", size = 31271, upload-time = "2026-04-25T11:08:34.651Z" }, - { url = "https://files.pythonhosted.org/packages/64/4f/44fc4788568004c43921701cbc127f48218a1eede2c9aea231115323564d/xxhash-3.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2a61e2a3fb23c892496d587b470dee7fa1b58b248a187719c65ea8e94ec13257", size = 32284, upload-time = "2026-04-25T11:08:35.987Z" }, - { url = "https://files.pythonhosted.org/packages/6d/77/18bb895eb60a49453d16e17d67990e5caff557c78eafc90ad4e2eabf4570/xxhash-3.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:c7741c7524961d8c0cb4d4c21b28957ff731a3fd5b5cd8b856dc80a40e9e5acc", size = 28701, upload-time = "2026-04-25T11:08:37.767Z" }, - { url = "https://files.pythonhosted.org/packages/45/a0/46f72244570c550fbbb7db1ef554183dd5ebe9136385f30e032b781ae8f6/xxhash-3.7.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:fc84bf7aa7592f31ec63a3e7b11d624f468a3f19f5238cec7282a42e838ab1d7", size = 33646, upload-time = "2026-04-25T11:08:39.109Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3a/453846a7eceea11e75def361eed01ec6a0205b9822c19927ed364ccae7cc/xxhash-3.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9f1563fdc8abfc389748e6932c7e4e99c89a53e4ec37d4563c24fc06f5e5644b", size = 31125, upload-time = "2026-04-25T11:08:40.467Z" }, - { url = "https://files.pythonhosted.org/packages/bd/3e/49434aba738885d512f9e486db1bdd19db28dfa40372b56da26ef7a4e738/xxhash-3.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2d415f18becf6f153046ab6adc97da77e3643a0ee205dae61c4012604113a020", size = 196633, upload-time = "2026-04-25T11:08:41.943Z" }, - { url = "https://files.pythonhosted.org/packages/a4/e9/006cb6127baeb9f8abe6d15e62faa01349f09b34e2bfd65175b2422d026b/xxhash-3.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bb16aa13ed175bc9be5c2491ba031b85a9b51c4ed90e0b3d4ebe63cf3fb54f8e", size = 215899, upload-time = "2026-04-25T11:08:43.645Z" }, - { url = "https://files.pythonhosted.org/packages/27/e4/cc57d72e66df0ae29b914335f1c6dcf61e8f3746ddf0ae3c471aa4f15e00/xxhash-3.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f9fd595f1e5941b3d7863e4774e4b30caa6731fc34b9277da032295aa5656ee5", size = 238116, upload-time = "2026-04-25T11:08:45.698Z" }, - { url = "https://files.pythonhosted.org/packages/af/78/3531d4a3fd8a0038cc6be1f265a69c1b3587f557a10b677dd736de2202c1/xxhash-3.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1295325c5a98d552333fa53dc2b026b0ef0ec9c8e73ca3a952990b4c7d65d459", size = 215012, upload-time = "2026-04-25T11:08:47.355Z" }, - { url = "https://files.pythonhosted.org/packages/b4/f6/259fb1eaaec921f59b17203b0daee69829761226d3b980d5191d7723dd83/xxhash-3.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3573a651d146912da9daa9e29e5fbc45994420daaa9ef1e2fa5823e1dc485513", size = 448534, upload-time = "2026-04-25T11:08:49.149Z" }, - { url = "https://files.pythonhosted.org/packages/7b/16/a66d0eaf6a7e68532c07714361ddc904c663ec940f3b028c1ae4a21a7b9d/xxhash-3.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ec1e080a3d02d94ea9335bfab0e3374b877e25411422c18f51a943fa4b46381", size = 196217, upload-time = "2026-04-25T11:08:50.805Z" }, - { url = "https://files.pythonhosted.org/packages/8d/ef/d2efc7fc51756dc52509109d1a25cefc859d74bc4b19a167b12dbd8c2786/xxhash-3.7.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84415265192072d8638a3afc3c1bc5995e310570cd9acb54dc46d3939e364fe0", size = 286906, upload-time = "2026-04-25T11:08:52.418Z" }, - { url = "https://files.pythonhosted.org/packages/fc/67/25decd1d4a4018582ec4db2a868a2b7e40640f4adb20dfeb19ac923aa825/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d4dea659b57443989ef32f4295104fd6912c73d0bf26d1d148bb88a9f159b02", size = 213057, upload-time = "2026-04-25T11:08:54.105Z" }, - { url = "https://files.pythonhosted.org/packages/0d/5d/17651eb29d06786cdc40c60ae3d27d645aa5d61d2eca6237a7ba0b94789b/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:05ece0fe4d9c9c2728912d1981ae1566cfc83a011571b24732cbf76e1fb70dca", size = 243886, upload-time = "2026-04-25T11:08:56.109Z" }, - { url = "https://files.pythonhosted.org/packages/8a/d4/174d9cf7502243d586e6a9ae842b1ae23026620995114f85f1380e588bc9/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fd880353cf1ffaf321bc18dd663e111976dbd0d3bbd8a66d58d2b470dfa7f396", size = 201015, upload-time = "2026-04-25T11:08:57.777Z" }, - { url = "https://files.pythonhosted.org/packages/91/8c/2254e2d06c3ac5e6fe22eaf3da791b87ea823ae9f2c17b4af66755c5752d/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4e15cc9e2817f6481160f930c62842b3ff419e20e13072bcbab12230943092bc", size = 213457, upload-time = "2026-04-25T11:08:59.826Z" }, - { url = "https://files.pythonhosted.org/packages/79/a2/e3daa762545921173e3360f3b4ff7fc63c2d27359f7230ec1a7a74e117f6/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:90b9d1a8bd37d768ffc92a1f651ec69afc532a96fa1ac2ea7abbed5d630b3237", size = 277738, upload-time = "2026-04-25T11:09:01.423Z" }, - { url = "https://files.pythonhosted.org/packages/e1/4c/e186da2c46b87f5204640e008d42730bf3c1ee9f0efb71ae1ebcdfeac681/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:157c49475b34ecea8809e51123d9769a534e139d1247942f7a4bc67710bb2533", size = 417127, upload-time = "2026-04-25T11:09:03.592Z" }, - { url = "https://files.pythonhosted.org/packages/17/28/3798e15007a3712d0da3d3fe70f8e11916569858b5cc371053bc26270832/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5a6ddec83325685e729ca119d1f5c518ec39294212ecd770e60693cdc5f7eb79", size = 193962, upload-time = "2026-04-25T11:09:06.228Z" }, - { url = "https://files.pythonhosted.org/packages/ad/95/a26baa93b5241fd7630998816a4ec47a5a0bad193b3f8fc8f3593e1a4a67/xxhash-3.7.0-cp314-cp314t-win32.whl", hash = "sha256:a04a6cab47e2166435aaf5b9e5ee41d1532cc8300efdef87f2a4d0acb7db19ed", size = 31643, upload-time = "2026-04-25T11:09:08.153Z" }, - { url = "https://files.pythonhosted.org/packages/44/36/5454f13c447e395f9b06a3e91274c59f503d31fad84e1836efe3bdb71f6a/xxhash-3.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8653dd7c2eda020545bb2c71c7f7039b53fe7434d0fc1a0a9deb79ab3f1a4fc1", size = 32522, upload-time = "2026-04-25T11:09:09.534Z" }, - { url = "https://files.pythonhosted.org/packages/74/35/698e7e3ff38e22992ea24870a511d8762474fb6783627a2910ff22a185c2/xxhash-3.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:468f0fc114faaa4b36699f8e328bbc3bb11dc418ba94ac52c26dd736d4b6c637", size = 28807, upload-time = "2026-04-25T11:09:11.234Z" }, - { url = "https://files.pythonhosted.org/packages/54/c1/e57ac7317b1f58a92bab692da6d497e2a7ce44735b224e296347a7ecc754/xxhash-3.7.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ad3aa71e12ee634f22b39a0ff439357583706e50765f17f05550f92dbf128a23", size = 31232, upload-time = "2026-04-25T11:10:21.51Z" }, - { url = "https://files.pythonhosted.org/packages/4f/4e/075559bd712bc62e84915ea46bbee859f935d285659082c129bdbff679dd/xxhash-3.7.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5de686e73690cdaf72b96d4fa083c230ec9020bcc2627ce6316138e2cf2fe2d1", size = 28553, upload-time = "2026-04-25T11:10:23.1Z" }, - { url = "https://files.pythonhosted.org/packages/92/ca/a9c78cb384d4b033b0c58196bd5c8509873cabe76389e195127b0302a741/xxhash-3.7.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7fbec49f5341bbdea0c471f7d1e2fb41ae8925af9b6f28025c28defd8eb94274", size = 41109, upload-time = "2026-04-25T11:10:25.022Z" }, - { url = "https://files.pythonhosted.org/packages/bd/b1/dfe2629f7c77eb2fa234c72ff537cdd64939763df704e256446ed364a16d/xxhash-3.7.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48b542c347c2089f43dc5a6db31d2a6f3cdb04ee33505ec6e9f653834dbb0bde", size = 36307, upload-time = "2026-04-25T11:10:26.949Z" }, - { url = "https://files.pythonhosted.org/packages/e7/f7/5a484afce0f48dd8083208b42e4911f290a82c7b52458ef2927e4d421a45/xxhash-3.7.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a169a036bed0995e090d1493b283cc2cc8a6f5046821086b843abefff80643bc", size = 32534, upload-time = "2026-04-25T11:10:29.01Z" }, - { url = "https://files.pythonhosted.org/packages/0f/5f/4acfcd490db9780cf36c58534d828003c564cde5350220a1c783c4d10776/xxhash-3.7.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:ec101643395d7f21405b640f728f6f627e6986557027d740f2f9b220955edafe", size = 31552, upload-time = "2026-04-25T11:10:30.727Z" }, +version = "3.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/63/71aa56b151a1b28770037a61bd4e461c2619cfc8866a4fcaf1548605e325/xxhash-3.8.1.tar.gz", hash = "sha256:b0de4bf3aa66363552d52c6a89003c479911f12098cd48a53d44a0f7a25f7c46", size = 86223, upload-time = "2026-07-06T10:49:58.937Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/97/1a8cebf0a6650417f08a18231590e2515aacd5ce39c3ad8b9e013ebd437d/xxhash-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:27a9e475157f7315826118e3f3127909a0fe25f1b43d3d3be9c584f9d265f937", size = 34695, upload-time = "2026-07-06T10:43:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/2f/cf/745b9bc0dd9c341bc074b5fc700db7bbef0f3b69ab21446492296ab37e50/xxhash-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b2ce44bf8f4a1d01f418b3110ff8dff32fd3f3e836c0e06333c3725f243fa6c", size = 32376, upload-time = "2026-07-06T10:43:41.97Z" }, + { url = "https://files.pythonhosted.org/packages/65/a4/8512a901b1d6ad4a9838d1b40385907a879d7e005a5afbec5d39526b69f6/xxhash-3.8.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:942bc86e9be6fdd6e1175048f5fe8f8fdaaf2309dd1323ef1e155a69cd346780", size = 217470, upload-time = "2026-07-06T10:43:43.572Z" }, + { url = "https://files.pythonhosted.org/packages/a0/ad/0ffd8094ea29579bb2dc42fa74d08570e9ea3d95db561e6b1105e69b9ca6/xxhash-3.8.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0204701e6d01f64254e0e5ff4255812b1febe027ddd7dda63372e27f98b5e91f", size = 237799, upload-time = "2026-07-06T10:43:45.248Z" }, + { url = "https://files.pythonhosted.org/packages/b3/90/783c6b3f9336bd07449fe672be32cef6833633936bbfda8d3b23ee18d202/xxhash-3.8.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7dc4bdf008f77c88d544849c48c1a40faf25a5eff6cc466de2e8edc37c191fce", size = 262587, upload-time = "2026-07-06T10:43:46.733Z" }, + { url = "https://files.pythonhosted.org/packages/c4/77/ba0316a7c3e661b86830a47ae4987798616ce1b15af8d2a6358e2d89ef60/xxhash-3.8.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c566b123dce7e4867ca518434cdfb9f84e5023771235b2e3107a26c9a41cbd8", size = 238484, upload-time = "2026-07-06T10:43:48.453Z" }, + { url = "https://files.pythonhosted.org/packages/09/79/33001037c1cba90f4ced38b257161c13452024c0db44208f883e2e47f3fc/xxhash-3.8.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9f23083e1bd9d901f844af7a126727c486e7eada9a1a6791c8f7e73f94fac656", size = 469909, upload-time = "2026-07-06T10:43:50.188Z" }, + { url = "https://files.pythonhosted.org/packages/45/90/237eded9dd6ae638083294e5a9f77b317aaebd480a330806b39c192a0de1/xxhash-3.8.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64af54dd1c3a45a27c04942f9a1a4683322bdd127f4745cca4e02549c1d2d2bb", size = 217166, upload-time = "2026-07-06T10:43:51.816Z" }, + { url = "https://files.pythonhosted.org/packages/0b/6a/8cb439dc9920e1468e1c2d69ef77cbeb4be3b1ae9f4b5344c07a2b59af18/xxhash-3.8.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8ea8a141eeced4f6262ab6dd71c681ac546a558c30bb586abe087d814b5f85ea", size = 307593, upload-time = "2026-07-06T10:43:53.436Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c6/c0607d373c8affea92101a3926c4fc8b026bcf8983e05fd58f3a0380ebf8/xxhash-3.8.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a98b2f95cab589e0f5e92c48431afb4d56238b8bf6668edcc66166180e9b509b", size = 234702, upload-time = "2026-07-06T10:43:55.042Z" }, + { url = "https://files.pythonhosted.org/packages/5b/cb/f4cfd456624c1f017858168b7ba9443dad810da8aac779a612658450e827/xxhash-3.8.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1b86ae798a976ccbc1d02af6ccb98f5b4d24756b1f65e995f11d10fe071f486f", size = 265749, upload-time = "2026-07-06T10:43:56.749Z" }, + { url = "https://files.pythonhosted.org/packages/33/f3/9006669c04b01206e21b2177425c649461ba188930a052c2f1728d6ec6a8/xxhash-3.8.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:81f4ed9ca9644bc95cd976bfe10f7a4cafab8ffdc3aed52877d4600e445be7ef", size = 221992, upload-time = "2026-07-06T10:43:58.12Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0b/7e6f3eaa05df5e0b6c94aa452b0672801f7031e602081f07fd441aaaaed5/xxhash-3.8.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:cb3fe820c27593f170770d6c8d791936cf6275d9269405fbb7b30a55363c10c8", size = 236899, upload-time = "2026-07-06T10:43:59.562Z" }, + { url = "https://files.pythonhosted.org/packages/da/cc/bbaee4987f3aab1d7b33bb430bb49e940646160af448b9167431c931126d/xxhash-3.8.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:7345007c12780985de4fd740148776d1eee18c0d41407c6fa1e48c5450304fe5", size = 297934, upload-time = "2026-07-06T10:44:01.132Z" }, + { url = "https://files.pythonhosted.org/packages/a7/97/6bee358660eb8b4f73c00b00b00bc616ebde00e1ab4b67c63486ce360648/xxhash-3.8.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:12eaeaa9ab8b9e6033a1fa5f6b338aaf55ff4df4bee11b59fd6ee03b19186ee4", size = 439315, upload-time = "2026-07-06T10:44:02.878Z" }, + { url = "https://files.pythonhosted.org/packages/c6/50/7e35275f39256bedace0c3cd5be3c72d4ac9d5aecf5e5fdc3530337cd263/xxhash-3.8.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2a845687219ba3214126f14a8a5861f97c9e065a7d0b8252adb6df13eea86fb", size = 214038, upload-time = "2026-07-06T10:44:04.504Z" }, + { url = "https://files.pythonhosted.org/packages/59/2d/69d02d096ee50bdf3ef0d208d874f52c71b1aa6906066bce3c52fedb8bc6/xxhash-3.8.1-cp310-cp310-win32.whl", hash = "sha256:656256c9f9303e47f07d5cb8ae4468285370adfafd7ba48aea33a458e7697626", size = 31939, upload-time = "2026-07-06T10:44:06.213Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1d/e06fca9844919ca91c6587d530cfa1e745830ec73ad38f44f04b25d1bfb7/xxhash-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:27cfc2f1ed76f956f36dfe0c56e5f5a3e94cd91eb78b893f63e2ef2ae404fcdf", size = 32729, upload-time = "2026-07-06T10:44:07.621Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c2/800648d99039927b5a86d8ae02cd86a556a5ee1678d388216f6b44c8966c/xxhash-3.8.1-cp310-cp310-win_arm64.whl", hash = "sha256:c85949d02c85adf6d786eb94858e124989a632a4e65739835b2fc5761827fac3", size = 29215, upload-time = "2026-07-06T10:44:08.916Z" }, + { url = "https://files.pythonhosted.org/packages/8a/5a/05eaa129555f85476a3e16ff869e95f81a78bbe4647eef9d0229f515a317/xxhash-3.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602efcad4a42c184e81d43a2b7e6e4f524d619878f2b6ee2ba469011f47c8147", size = 34699, upload-time = "2026-07-06T10:44:10.14Z" }, + { url = "https://files.pythonhosted.org/packages/80/59/0df1133958b2228929355e022aab1e958c7b2c43e27bf7f59bc9edfa8a54/xxhash-3.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:131324f719957b988861714de7d6ddf57b47abec3b0cc691302ffeaba0e05e10", size = 32373, upload-time = "2026-07-06T10:44:11.353Z" }, + { url = "https://files.pythonhosted.org/packages/3e/bf/1cfda5b5e6bf26617812b4a31662ef2220d2ad04e0a55b8ff9eb36e56a5c/xxhash-3.8.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:db77278a6eddadbf44ce5aae2fee5ebb4d061f026b1ce2130d058cd4d7a7b670", size = 220284, upload-time = "2026-07-06T10:44:12.683Z" }, + { url = "https://files.pythonhosted.org/packages/70/93/45dc0ad7913b69e5b08bd039236cf628380e4c9cc76a8a4c6625a328e058/xxhash-3.8.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c332dd48b8cb050da2bb2a3c96d72b1664168650a250ef9718e423df7989e05", size = 240980, upload-time = "2026-07-06T10:44:14.297Z" }, + { url = "https://files.pythonhosted.org/packages/e9/02/f28ba7d17f2c1410ee397982c817ab1bd5b2701070c2d2c373539aad000a/xxhash-3.8.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a5cd96f6dcdf4fa657b2d95668d71d58455248f98712ecffaa9c528edf40ccae", size = 264526, upload-time = "2026-07-06T10:44:16.017Z" }, + { url = "https://files.pythonhosted.org/packages/5c/d0/f10651cec2c7981b20d693deae6bdfc438427d92be2db4ccabb6181f0021/xxhash-3.8.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c959f88160b13b4e730b0d75b459b7929fc0d2225c284c9683ac95d6feeeac6a", size = 241369, upload-time = "2026-07-06T10:44:17.698Z" }, + { url = "https://files.pythonhosted.org/packages/ff/40/136e0cbaf5db51e191423b1c98643593189f02b6cd90837bf64b19113d70/xxhash-3.8.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:027dee4355f3fcc41481650d846cf6cfc895c85a1ab7acd063063821a0df5b4c", size = 473186, upload-time = "2026-07-06T10:44:19.354Z" }, + { url = "https://files.pythonhosted.org/packages/4b/3f/6aa808a96bdc43dba9a740dec56c744526ee3c0019e32c75e810fa90ae4d/xxhash-3.8.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad52a0e4bcc0ba956a953a169d1feec2734a64981d689e4fc8f490f7bf91af60", size = 220092, upload-time = "2026-07-06T10:44:20.956Z" }, + { url = "https://files.pythonhosted.org/packages/47/28/a8675e78a9ced96dab853416162268e10e05b452e95db7888cf69f58ac5f/xxhash-3.8.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5d3dfb1f0ff146da7952867a9414f0c7a29762f8825a84879592612fd6139342", size = 309846, upload-time = "2026-07-06T10:44:22.543Z" }, + { url = "https://files.pythonhosted.org/packages/89/0f/7fe4d4ef4e69f0033e012396ee2a115886bca7b10b7e45ce398626436bfc/xxhash-3.8.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4482380b462ca9e59994d072a877ecadd1cf51102daeeab2db696f96ab763723", size = 237659, upload-time = "2026-07-06T10:44:24.135Z" }, + { url = "https://files.pythonhosted.org/packages/38/8f/83e9e31d4ed57fe963b99cb5b13a23e3e0f0dad1885aa0ebd2a7819dd423/xxhash-3.8.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:950ac754d16daea42038f38e7465eb84cda4d08d7343c1c915771b29470f065a", size = 268737, upload-time = "2026-07-06T10:44:25.875Z" }, + { url = "https://files.pythonhosted.org/packages/57/79/7e7de46dbe5d1f49afc96a0bc42e6b8df24eae3d6bad6007b99e42f48430/xxhash-3.8.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0418ec8b2331b9d4d575fc9284427e8e69449d7172e99e1a86fcdd1f51a0a937", size = 224955, upload-time = "2026-07-06T10:44:27.777Z" }, + { url = "https://files.pythonhosted.org/packages/ec/34/b8540839e958d5ef5c6101af6f16032109e7099698ae8edbc8dcefe4d8f4/xxhash-3.8.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:32a94ad2763e0263d9102037d349002c3d3c401e42770542c3eeb4801f311661", size = 239653, upload-time = "2026-07-06T10:44:29.422Z" }, + { url = "https://files.pythonhosted.org/packages/ce/87/a735d05f7f859354acadabe470ff40e2c46672275f96dcf096a761904def/xxhash-3.8.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:89b11a5cdd441aa463f6d34ca0241602bc09b001a76994b6059828494108c673", size = 300213, upload-time = "2026-07-06T10:44:31.401Z" }, + { url = "https://files.pythonhosted.org/packages/98/31/3e1cb020237b68117fc212dc5f9753b87f865b4dfee7c1ce62d0836955b5/xxhash-3.8.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:09a204dd4bb0823daf938cdd0dc8057d5f1e14fe3cbde929424255f23f9de872", size = 442508, upload-time = "2026-07-06T10:44:33.023Z" }, + { url = "https://files.pythonhosted.org/packages/23/bf/f80090622141cc734b039ce1d15ce3ff6dced375e9680249bf5b9b8c6bf9/xxhash-3.8.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e710ad822c493fb80a4fbc1e3d0a807b1422cb90adbe64378f98291b7fa48fef", size = 216853, upload-time = "2026-07-06T10:44:34.983Z" }, + { url = "https://files.pythonhosted.org/packages/a6/a3/60157acecc307b238d3651c2483168e224b48b23a36ae6d6903588341d80/xxhash-3.8.1-cp311-cp311-win32.whl", hash = "sha256:5013be3bea7612852c62a7437f3302c1cfb91ca7e703b194459db0b2b2e0d792", size = 31936, upload-time = "2026-07-06T10:44:36.542Z" }, + { url = "https://files.pythonhosted.org/packages/59/5c/ef70c418d878d187b8da56d4cdc06aea6cf5e456b301e96e51e1d2cc8625/xxhash-3.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:f377012b86c0a23a1df0cf5a1b05aa7187649e472f71c7892e5f2c2815bbe74f", size = 32724, upload-time = "2026-07-06T10:44:38.177Z" }, + { url = "https://files.pythonhosted.org/packages/2c/25/f008db952cec6b2a26445b456eeed2ebebd65e08e848ebe09ed6ac0634e6/xxhash-3.8.1-cp311-cp311-win_arm64.whl", hash = "sha256:836f11d4474d3228e9909d97216faa4f7505df41cfaf3927eb29809de785a78d", size = 29212, upload-time = "2026-07-06T10:44:39.577Z" }, + { url = "https://files.pythonhosted.org/packages/42/91/f65c34a7aa7b4e7cf4854f8e6ef3f7ee32ceac41d4f008da0780db0612f6/xxhash-3.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e6e49370822c1f4d8d90e678b06dbcb08b51a026a7c4b55479e7d467f2e813bc", size = 34680, upload-time = "2026-07-06T10:44:40.932Z" }, + { url = "https://files.pythonhosted.org/packages/57/04/b10a245a4c09a9cfa88f8e9ae755029413ad1ac17047f9a61906e5ae0799/xxhash-3.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:220d68130f83f7cc86d6edfdeab176adc73d7200bf3a8ec10c629e8cf605c215", size = 32397, upload-time = "2026-07-06T10:44:42.196Z" }, + { url = "https://files.pythonhosted.org/packages/3a/75/45ab795b5945b6388583bd75202106af505537935566c15a1577797a0e08/xxhash-3.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d365ee1892c1fa803536f8c6ce21d24b29c9718ec75eb856095c07830f8c478", size = 220549, upload-time = "2026-07-06T10:44:43.603Z" }, + { url = "https://files.pythonhosted.org/packages/13/44/5ba2bd0a14ddf4193fc7d8ec29625f659f22c06d60b28f04bf46305d8330/xxhash-3.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:852bfe059720632e2f16a6a4745e41d20937b2bf2a42a401e2412046bb6971cc", size = 241186, upload-time = "2026-07-06T10:44:45.534Z" }, + { url = "https://files.pythonhosted.org/packages/23/32/c4147def4d1e4538b906f82731e0ba23424377fc50a7cddd03cd284c8f63/xxhash-3.8.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2f8c25a7061d952de589bd0ea0eaadee32378ff83dd6a677b267f9cd86f401f8", size = 264852, upload-time = "2026-07-06T10:44:47.199Z" }, + { url = "https://files.pythonhosted.org/packages/6c/bd/71ed14f4f0318bb7fd7b2ec51999413487fa8da8d41208e84d50d1ef0f98/xxhash-3.8.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:868a8dcaff1a84ba78038e1cef14fc88ccf84d9b4d12ea604696e0693296aa56", size = 242663, upload-time = "2026-07-06T10:44:48.846Z" }, + { url = "https://files.pythonhosted.org/packages/91/09/70af22c565a8473b3f2ae73f88e7721af281bc4a575236dbd1970c9f76f6/xxhash-3.8.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6536d8677d2fff7e64cd0b98b976df9de7aee0e69590044c2af5f51b76b7a170", size = 473510, upload-time = "2026-07-06T10:44:50.695Z" }, + { url = "https://files.pythonhosted.org/packages/18/96/34db781c8f0cf99c544ca1f2bc2e5bf55426e1eb4ca6de8ea5da56a9f352/xxhash-3.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:82c0cedd280eab2e8291270e6c04894dbc096f8159a39dcf1807429f026ca3cc", size = 220469, upload-time = "2026-07-06T10:44:52.422Z" }, + { url = "https://files.pythonhosted.org/packages/93/5f/9a184f615fa5a4dce30c01534f62946ce5a11ce40f73785cbd356ccabaa9/xxhash-3.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:daa86e4b68221d38e669bb236ba112d0335353829fb627c82e5909e4bbe8694c", size = 310290, upload-time = "2026-07-06T10:44:54.142Z" }, + { url = "https://files.pythonhosted.org/packages/a9/dc/9b9a9789011ee153723a5eb9e7dd7fcbae2ba9b3fe7a729249ca7c252056/xxhash-3.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2bc7113e6f2b6b3922dd61796ca9f36af09da3773898e7003038dc992fc83b8d", size = 238173, upload-time = "2026-07-06T10:44:55.693Z" }, + { url = "https://files.pythonhosted.org/packages/ec/4d/71c6005ada9dcb608a4e1902e8475ecadb5f3fbfa04e1e244d276a2d0c43/xxhash-3.8.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5eed32dad81d6ba8e62dc7b9ffa0500199385d7810a8dd9d4eafaceb8c6e20bb", size = 269026, upload-time = "2026-07-06T10:44:57.424Z" }, + { url = "https://files.pythonhosted.org/packages/2f/87/d6c036ba25dfbd9c8633be5aa86fc9474bbb9e2c68212a841d090abe7344/xxhash-3.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:83697b0ea1f10e7f5d8b26a4906fa851393c61546c63839643a2b7fe2d868061", size = 224970, upload-time = "2026-07-06T10:44:59.085Z" }, + { url = "https://files.pythonhosted.org/packages/48/62/4c1f035a41c5752aa05e195b6c904c07b94fe9061a16de61e72a6e6b135f/xxhash-3.8.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:36fc69160465ae75c6ec4ac9f781bb2aa16ae7ff869e73c26fee85fbb11b9887", size = 240820, upload-time = "2026-07-06T10:45:00.746Z" }, + { url = "https://files.pythonhosted.org/packages/da/14/d39d565069b87e86d21a2af2a31d04db79249d25aa8d5b62959056a89857/xxhash-3.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:445e0f5a31f2f3546ae0895d4811e159518cdc9d824c11419898d40cfadb677e", size = 300619, upload-time = "2026-07-06T10:45:02.716Z" }, + { url = "https://files.pythonhosted.org/packages/13/22/75467acc887edc8cf71c97ab1708feb3df7a88bda589b9f399765c6387d2/xxhash-3.8.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:dfe0580fbfd5e4af87d0cc52d2044f155d55ebd8c8a93568758a2ea7d8e15975", size = 443267, upload-time = "2026-07-06T10:45:04.653Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b6/1da3baa5fa6ef705e3425fddd382be7dfc4dfba2686df90a20f16e9c7b1b/xxhash-3.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:095e1323fa108be1292c54c86da3ef3c7a7dc015b105a52133973bc07a6ad11a", size = 217338, upload-time = "2026-07-06T10:45:06.304Z" }, + { url = "https://files.pythonhosted.org/packages/78/dd/b5295a9f97484e7a1c2b283a742ca45e3104991c55a1ef670dde161829ba/xxhash-3.8.1-cp312-cp312-win32.whl", hash = "sha256:bf28f55e427e0483acb1f666bd0d869b6d5e5a716680c216ad7befe3d4cfba2e", size = 31970, upload-time = "2026-07-06T10:45:07.823Z" }, + { url = "https://files.pythonhosted.org/packages/ec/31/3fa0b807d7e21515cd975e7fe5c039d52ac3e9401a96d6ad68dae6305215/xxhash-3.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:2256e80e4960ee282f63428adb349cb7f8bd8efe4db770d88eb815f4b9860724", size = 32741, upload-time = "2026-07-06T10:45:09.42Z" }, + { url = "https://files.pythonhosted.org/packages/b8/05/86feada74e239600e6875aa507afb40482a89b92700aa74a92da83bdcb77/xxhash-3.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:9df56e6df96a60590935e22373041cccc91fd55858763dcffb55bf63b3a2b396", size = 29234, upload-time = "2026-07-06T10:45:10.809Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8c/446bb782cd0d27007a917b5569a08dd73219c3e8d6e459014db104b27bdb/xxhash-3.8.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:3c682fcd96eb4bf64be32a4d95f96107e1588005831bd8a741b324fdda01b913", size = 38562, upload-time = "2026-07-06T10:45:12.425Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ec/c0c45627eaa6be7a5d6117423adf8f7a15b17ee74b4b17072cca5959a225/xxhash-3.8.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:036a024d8b9c01f70782e09ed98d532e76fd23f950ae7154bd950fe94e90ebec", size = 36656, upload-time = "2026-07-06T10:45:13.932Z" }, + { url = "https://files.pythonhosted.org/packages/f6/94/8324c04cc7597154caaeba6c094e01fbd2e7601d01e7a13eea9f5420e77b/xxhash-3.8.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d6a5c0bce213b23b0166fe0d35bcbbe23ce4b968f257cc7eb6fd57cb8e1e6297", size = 31169, upload-time = "2026-07-06T10:45:15.687Z" }, + { url = "https://files.pythonhosted.org/packages/40/a4/beb6bb26e1184e126dbe7a5682330214ef54dcfbf882078aa9f4b5428d42/xxhash-3.8.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:5177aa44eddaa97c6ef0cc00c6d540edb64d51781d2f8fb941612ec61a92c9ed", size = 32177, upload-time = "2026-07-06T10:45:17.035Z" }, + { url = "https://files.pythonhosted.org/packages/56/0f/fc4c92a5a528f839b34b6419b2e53c8597f2a629d5a1f5d721f65bfa1fd6/xxhash-3.8.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7801b7223db017b9c0c9ccf37e44524edb35a1544a1c032add22c061c6af0276", size = 34642, upload-time = "2026-07-06T10:45:18.39Z" }, + { url = "https://files.pythonhosted.org/packages/d4/58/edbfb141d4000767ac6a9694f8ac0763e2c2e983e65c9e31620ba56e2667/xxhash-3.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9e80238259655bf69d7bcd08226a970d7f42605f3157786bfa76dd13472d7fa0", size = 34684, upload-time = "2026-07-06T10:45:20.033Z" }, + { url = "https://files.pythonhosted.org/packages/07/3f/5072f1f0f5714186f0ac2a0b5a4929ce30d4b845e94886b6c01b6ebda0be/xxhash-3.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bcab50a389cc04d87f90092af78a6adba2ab3deca63175a3344ca83514045315", size = 32401, upload-time = "2026-07-06T10:45:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/49/c7/802ea2f9c2ed59219934d6d65c470d502b1788043eae277a52af8658bda6/xxhash-3.8.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a2489d3a776fa380cb8e71f54c7fda268a9baf3de9b1395093fd280f95735907", size = 220617, upload-time = "2026-07-06T10:45:23.234Z" }, + { url = "https://files.pythonhosted.org/packages/99/a8/e10488efd31fcb13fcd6acbc6e788f10c6f8e3a0cc4ae3eb89dc19c55a12/xxhash-3.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32ab1e5432690276e71192be7401b55f96db2d0eedea5d44eb1f164505669cc0", size = 241295, upload-time = "2026-07-06T10:45:25.364Z" }, + { url = "https://files.pythonhosted.org/packages/18/cc/14180b17d44892a631f8ae7323c30bfbb1328efc8209e528a480293528ac/xxhash-3.8.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b30e01a0b97a4bc3f519a4d7a82da3dc53251fb0de5eeea8660dcd4ff094c0c2", size = 264688, upload-time = "2026-07-06T10:45:27.09Z" }, + { url = "https://files.pythonhosted.org/packages/a9/72/a14019d0c5f6c41ee407a503036ae32787c91325ca218a96a9b5627be651/xxhash-3.8.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1f44275ddb0978b67a58a951501903f04d49335a91f7681c9ce122ecb8ccb329", size = 242740, upload-time = "2026-07-06T10:45:28.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/08/92550e556c6fcfcb96c6a336945eb53a431ed43120ed749636debb16c5cf/xxhash-3.8.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e3b87cbd974512c0c5fc7b469c36b2cdc9ee6d76e4ec78bccb2c7184611c49b0", size = 473599, upload-time = "2026-07-06T10:45:30.524Z" }, + { url = "https://files.pythonhosted.org/packages/29/83/e361d3c1acd1b21e1d489616de6fa4aaf843365d8179f612e3743eac20a9/xxhash-3.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98ee81b4b7f3023c9cb04a78cc67610baffcb5812d92f2096cb5a5efc6f19437", size = 220559, upload-time = "2026-07-06T10:45:32.979Z" }, + { url = "https://files.pythonhosted.org/packages/05/01/006a4243c2c2a6831827f9999f6d1c23feeef100eb023c1f886022a00bf3/xxhash-3.8.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2666f059a1588a99267e33605365ed89cea92f424b3522806a9f4bd8ad2e3d62", size = 310383, upload-time = "2026-07-06T10:45:35.875Z" }, + { url = "https://files.pythonhosted.org/packages/d8/20/af388e8bf9f9a0f89eeef7d2a1935d176ee1c20bc6adeda05035879379cf/xxhash-3.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0093cf7eeb91b84776e8742113afa4bdf47533d36cf719179aaaf1f56f6f8bf", size = 238228, upload-time = "2026-07-06T10:45:38.02Z" }, + { url = "https://files.pythonhosted.org/packages/63/6b/4666579a87eebd1744663c404297355fa0658617b015cedfa58810ee7036/xxhash-3.8.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3a800912a2e5e975d4128969d645c4a2a80aa886ccd6c9b1c6f44529e327e8cf", size = 269137, upload-time = "2026-07-06T10:45:39.954Z" }, + { url = "https://files.pythonhosted.org/packages/de/d3/e963a8a46f900a137d91b02144d8ea07a8f812971b138204a3b2f8b8e55c/xxhash-3.8.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0fe37f72a207223d22a4eddc3149d4298993385aa9daef25c039246ca5a309f3", size = 225068, upload-time = "2026-07-06T10:45:41.718Z" }, + { url = "https://files.pythonhosted.org/packages/aa/80/9d181dbcde4b0fe48375f48833a5832d4b8cd2b349b15110c92ee472d874/xxhash-3.8.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5db43f249b4be9f99ef4b967863f37094fb40e67effafb78ba4f0356b6396104", size = 240874, upload-time = "2026-07-06T10:45:43.414Z" }, + { url = "https://files.pythonhosted.org/packages/39/15/ce3ab5a1cd27ead25a5196e55a7284220f6ad6e316da494ffd900b2b600f/xxhash-3.8.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c4ed42965c2cd9081f011be22f69d0e65d3b6165fe7734072fd0c232840bbd4e", size = 300702, upload-time = "2026-07-06T10:45:45.135Z" }, + { url = "https://files.pythonhosted.org/packages/96/c0/2281a8ab5f2a62dbf57a23c58a01ccc1d98abf40f71193c8a81f59e759b5/xxhash-3.8.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3557bec8fcb11738a8920eeb68974bc76b75262f6947998d3147954ce0a4b893", size = 443351, upload-time = "2026-07-06T10:45:47.188Z" }, + { url = "https://files.pythonhosted.org/packages/81/2e/071a58c1a53a52d4f7a3aa0987be0c396dffd40da8204805fe1b130a81f4/xxhash-3.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:00de40f3b42240db23a82a5c682b55d7263d84a26a953240c1aee463409660e3", size = 217396, upload-time = "2026-07-06T10:45:48.925Z" }, + { url = "https://files.pythonhosted.org/packages/68/44/36ab58134badd9d3433fc7b53c4ca8d113d8e807782885628640f8297a4d/xxhash-3.8.1-cp313-cp313-win32.whl", hash = "sha256:b5196cc2574cfec572a5f3fb7cfa5ade27305ae3d06516a082132441aff4c83a", size = 31974, upload-time = "2026-07-06T10:45:50.591Z" }, + { url = "https://files.pythonhosted.org/packages/96/2a/2a0b84798448e766f7b89ceed073cb0cb5a43fc9ebbacbdea74a38de18e3/xxhash-3.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:538f5f865df6cd8c32dd63158a0e5b4f5dd08d732a7da8b7228a5a0776c8ce55", size = 32739, upload-time = "2026-07-06T10:45:52.221Z" }, + { url = "https://files.pythonhosted.org/packages/d4/60/bb51dbf7c363ff88a7cbd50b7959718219577ef44d7cf255929ffc4a2194/xxhash-3.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:a6617f30641ba0d8baa1635fbefb1dffc5165ec36d26921bd5cee13497cd937a", size = 29239, upload-time = "2026-07-06T10:45:53.714Z" }, + { url = "https://files.pythonhosted.org/packages/56/d3/827ca123c2ee5443a6aaed3c5dd199237dc2f010e2bebd7ec09ef36f3a5f/xxhash-3.8.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:bfcd82852c62a60e314670a9602de354c4460f8adad916e2e42a20860c7870bc", size = 34964, upload-time = "2026-07-06T10:45:55.535Z" }, + { url = "https://files.pythonhosted.org/packages/05/67/67ae2a3ccdeb8b8ef025d35aee9edd1d26c3abe5051d47da9286232afbf8/xxhash-3.8.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:08ea2081f5e88615fec8622a9f87fbe21b8ea58d88cfc02163ca11026ee62a92", size = 32697, upload-time = "2026-07-06T10:45:57.288Z" }, + { url = "https://files.pythonhosted.org/packages/38/5a/3d3994346e1f45493679cb5c1ffc2bf454e410e9d1e8a662d253becee91e/xxhash-3.8.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2e32855b6f9e5b18f449e59d45e3d5778bdeb660632ef2693cca267a11246c75", size = 225954, upload-time = "2026-07-06T10:45:58.897Z" }, + { url = "https://files.pythonhosted.org/packages/3f/2c/53169270309b7cd8e05504e07fe123bac053b89d00ac63617faacf0a2ec0/xxhash-3.8.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6e088bd7870775624256a0d84c2a6714afd223b2eeb56b0ca58398e52a32fda", size = 249776, upload-time = "2026-07-06T10:46:00.977Z" }, + { url = "https://files.pythonhosted.org/packages/70/e0/5c551d8d592f944506f7c5185e210255c15e672a3c6008c156a1bd9b775e/xxhash-3.8.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:72eb5ae575cc7ae2b23f6f8064a8b10f638c7149819ae9cc6d20ebd4d37a1629", size = 274776, upload-time = "2026-07-06T10:46:02.869Z" }, + { url = "https://files.pythonhosted.org/packages/a0/2a/d3a762270cee2d7bcd0e25e28c623e5f3f5c0dc637b66e3e47dd5b0bb3f0/xxhash-3.8.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d0b48cdf690a64cedf7258c3dc9506cc41fc86edd7739c40e3098952265dc068", size = 252056, upload-time = "2026-07-06T10:46:04.688Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8f/b78e4373b2cb6d1c42af60ea2d7e9146ad0710b239ac7f706d5d31d5bb98/xxhash-3.8.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb9e256a357dfcede7818c6d34e70db2d6b664394803d1de4b6984d2de76c0f1", size = 482108, upload-time = "2026-07-06T10:46:06.498Z" }, + { url = "https://files.pythonhosted.org/packages/e6/0d/642d923336ea61a15f8ce64fc7e078729e6e06c3a026e517fa79b2c23b7a/xxhash-3.8.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51f71a6e2ad071e70c937e41fcb6c19f82c3f9f49831eba850ed4a106ffbb647", size = 226739, upload-time = "2026-07-06T10:46:08.598Z" }, + { url = "https://files.pythonhosted.org/packages/a6/0a/a37d6da6427d45a8d23e3ee3a0ca9c9d4a90364849c6637fe2963a755f9b/xxhash-3.8.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e4a6443968c4e8dc69967e12776776a5952c119cc1bd94168ad1c5ad667c2be1", size = 319658, upload-time = "2026-07-06T10:46:10.504Z" }, + { url = "https://files.pythonhosted.org/packages/4a/51/ebbd40da8a3f1bc53b4b7a9a87f8e28bd95c5f21bc14b8a57860cf367d1b/xxhash-3.8.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:714503083a1f2065c9ad15340dd49ac8a8e948a505a705ffa1750cb951519113", size = 246059, upload-time = "2026-07-06T10:46:12.634Z" }, + { url = "https://files.pythonhosted.org/packages/24/4c/d9014030147e1f0bb26e7da47aa240dd9ec61c763c573e558111d869f8e1/xxhash-3.8.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:77f74e45a1e5574bbbf80181c8027b3a4c65c2248fffbd557bd596fff13102f9", size = 275535, upload-time = "2026-07-06T10:46:14.614Z" }, + { url = "https://files.pythonhosted.org/packages/84/86/caee2db41fadcd5a25aa4323213f9afec5a8586d4e419241e3d659362bd7/xxhash-3.8.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e0e1b0fb0259c1b75d1251ac0bb4d7ab675d36f7a6bf4ba6aa630dae94f9ffa", size = 231292, upload-time = "2026-07-06T10:46:16.452Z" }, + { url = "https://files.pythonhosted.org/packages/0b/60/f52f08bcdc904c4514ea5c25caa19e9f3214144434a6ff96dc82dc1cbddd/xxhash-3.8.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:10e4393ec33633c2f05ad01869e546ad080b1a18f2650503731f153774608b31", size = 250490, upload-time = "2026-07-06T10:46:18.318Z" }, + { url = "https://files.pythonhosted.org/packages/24/a0/94dc7ae310838f250669c6ad7168e6d6fca17d49dac1053f06dc232c4a56/xxhash-3.8.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:b3ba794c3d885803db6c3116686923f1ec13bc86e621e169a375282b63ea1cc6", size = 309861, upload-time = "2026-07-06T10:46:20.503Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f9/adeead7d0eb28cdfc2832544ea639ffbc6749ccde47a8e228d667459182e/xxhash-3.8.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:57189a69c0891e4818853feaa521c972d22c880a001453addea015f48e3c3398", size = 448739, upload-time = "2026-07-06T10:46:22.79Z" }, + { url = "https://files.pythonhosted.org/packages/04/a4/22ec0e07db57d901c9298ae98aa3cf2be45bafded6f07c13131e85b89032/xxhash-3.8.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d59e71153fe9ff85648d00e18649b07e9b22c797291abb7e27274fa06df8b838", size = 223657, upload-time = "2026-07-06T10:46:24.831Z" }, + { url = "https://files.pythonhosted.org/packages/94/32/8a9531f37b59e5a013003db7cb7414baf4ce7e0e1268e0d5947cd3d6a2df/xxhash-3.8.1-cp313-cp313t-win32.whl", hash = "sha256:5b96f0024e9840f449bd91b2d005c921a4b666055a0d1b6492463799f32aae22", size = 32377, upload-time = "2026-07-06T10:46:26.86Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ab/2ca45fd7f671de5f81fc297ef1c95080b40c86ec6be0cc6034b8f7707ac8/xxhash-3.8.1-cp313-cp313t-win_amd64.whl", hash = "sha256:37d5a56c36dcc0b9a87b814cd992598d33863ff683749de6c86081f278d5e629", size = 33274, upload-time = "2026-07-06T10:46:28.39Z" }, + { url = "https://files.pythonhosted.org/packages/5a/54/20d7163463ddb6438b73a427d1655a77a502cf9b9b0c3ada3599629d9c0a/xxhash-3.8.1-cp313-cp313t-win_arm64.whl", hash = "sha256:6696c8752aded28ff3b16f33ef28ce28fb5d209b80c206746f943199fcf5fd65", size = 29375, upload-time = "2026-07-06T10:46:29.962Z" }, + { url = "https://files.pythonhosted.org/packages/c2/8b/df2ba04f22a6cd6b39f96a6577329a8471a55c90ef8d8e2f7c102363613f/xxhash-3.8.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:9db455cb649dcfe4504d6d68a6d83a7315a99a3ca59871dc3ff840671f99adba", size = 38430, upload-time = "2026-07-06T10:46:31.496Z" }, + { url = "https://files.pythonhosted.org/packages/b2/4f/6a059e8ad3ca8deedc91dfe335b211204900895152212c03ebbe721de68b/xxhash-3.8.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:affb37f152e55b5e4494bb9d0107f7bb08515c6704fbed82d9f61214d74adc17", size = 36558, upload-time = "2026-07-06T10:46:33.078Z" }, + { url = "https://files.pythonhosted.org/packages/cb/95/40be178205acce092ae418feb20ac737b32a02c7b864926ed0717354c9f8/xxhash-3.8.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:460261045936975193bfd20549a0de1cd52a33b405cbb972f0d80940c42266cd", size = 31181, upload-time = "2026-07-06T10:46:34.793Z" }, + { url = "https://files.pythonhosted.org/packages/3f/89/2da4dbf051bafa156c0e3f12012db2b0ac3b84ff37ca1f021f6bfffcdfbb/xxhash-3.8.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:38c887aedb696ef8bca19983206d270848558cfae4a91afa6a2fb05dde58ffc5", size = 32192, upload-time = "2026-07-06T10:46:36.393Z" }, + { url = "https://files.pythonhosted.org/packages/7c/4e/e000bbae3566bc8e0be771a8a0f294aa99075e3f0bc4ef43922ebffdebc8/xxhash-3.8.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:594131ce1aad18db3689781f806db1b065cdaa04f4df36b4c038d2013aefd0bf", size = 34691, upload-time = "2026-07-06T10:46:38.1Z" }, + { url = "https://files.pythonhosted.org/packages/b4/4a/ea954aacc7d1c8711880ac2b55da94429a9b4296b151c4fc0966549ca1ee/xxhash-3.8.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:78c794b643d214f1522e7a288bcf5a2de120d26cd170516749a4009dc92722c9", size = 34807, upload-time = "2026-07-06T10:46:39.647Z" }, + { url = "https://files.pythonhosted.org/packages/ca/29/df598e738ff37558ac627264deb2e560902d9bf7f46d3bd5175c9eee593e/xxhash-3.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:af0c9fedc4a2c24e8664953882fe8185f3790b8338c9c700f76f5ad660817711", size = 32410, upload-time = "2026-07-06T10:46:41.359Z" }, + { url = "https://files.pythonhosted.org/packages/59/9c/81ab40e7d33ada0b3df5d1bc884894d15dbf4f805cd645b685e4606bb8e0/xxhash-3.8.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:115772daeb71b2f3b9381177017f53e6cf3f3439c840737fdabd21aba6e54920", size = 220564, upload-time = "2026-07-06T10:46:43.463Z" }, + { url = "https://files.pythonhosted.org/packages/fd/6f/62ae6f5c8606320a0e2a41c2dc8c6d91cc5d63d0f84dd9582e9543779dd8/xxhash-3.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:000435984a0469b0f822fe76f35bddea0f96a4d6521b3339a60a6428cdee1edc", size = 241462, upload-time = "2026-07-06T10:46:45.509Z" }, + { url = "https://files.pythonhosted.org/packages/15/a1/9c3a0ec6cb524396f551eddd102a76690a795494eb9784fc67542b0daa37/xxhash-3.8.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2f1c68394818e0595569c2ff3cbc1e6d5a36a434e796f5c526b987b80c8a8c62", size = 264491, upload-time = "2026-07-06T10:46:47.655Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/700a4674e4308eb59d2fdb973977e82eae231bea5044753fee5c9eec0e0c/xxhash-3.8.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:46b39976d008e2a845758650f0ff7136bca004f40da0c8798bd37ac37860154f", size = 242905, upload-time = "2026-07-06T10:46:49.857Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8a/72d9874375c8d4cbc64a8cd1d659d5695a8765c3db82efa82dc5bd9f14d0/xxhash-3.8.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d5006c65ec507a333479e76e00e2c368781f16c24ededa764763956b32a0e93e", size = 473873, upload-time = "2026-07-06T10:46:51.953Z" }, + { url = "https://files.pythonhosted.org/packages/03/f0/6db07590ed7e0a77f186ef0bcea8d52553bf1ba57833e09467a2411f0f2d/xxhash-3.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31a2649bcf1fe97cf11c79848d761df33ac46b3896942d31b640557b486ff6b", size = 220765, upload-time = "2026-07-06T10:46:55.41Z" }, + { url = "https://files.pythonhosted.org/packages/8f/10/00d12d8b8beabbf49a8bbc626fb9f40445145a8887eb41a6acfb69149ac4/xxhash-3.8.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f759eed402448c2bdbb492e4fba1f20668ffe29688605ea61f0f67f9e4e386d", size = 310478, upload-time = "2026-07-06T10:46:57.729Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f9/12a82394eefb0f185d15a7f7b9f627c61c475a72dd83718436a5b84b42ac/xxhash-3.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5f97ecfede10d5b2870383620e2d25c8561e217c7bf9081073802b54248d2b", size = 238393, upload-time = "2026-07-06T10:46:59.87Z" }, + { url = "https://files.pythonhosted.org/packages/20/f3/53f963e320b9ce678337aa7273f39ce692ded8b99e3d22a866ec722159ab/xxhash-3.8.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:1da930bbcac3e8fbe2191850e2abb57977a99348c12c4b385e1058ac1b0a9ecc", size = 268704, upload-time = "2026-07-06T10:47:01.806Z" }, + { url = "https://files.pythonhosted.org/packages/0a/50/5b5badbd87c82d9f9b5f58ac74a3f29ef08f6fc387b324b8fd482450b862/xxhash-3.8.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:747476436f6891b9773374ce8d48edcc8b12cb5b61b67c6fb6289633747d088f", size = 225015, upload-time = "2026-07-06T10:47:03.784Z" }, + { url = "https://files.pythonhosted.org/packages/30/93/3ca68265afe7b4e69435e08a7b6a1d9d0f2a071e889da1f8041ed00fe878/xxhash-3.8.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef09bbc2519a93cd0f95f2ceb5f7b85919dffea643278e02362bf40e3c4bed1", size = 240951, upload-time = "2026-07-06T10:47:05.816Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a6/27e19670c40f46b5e76e11f2f4713d21054804568425d870670e757172ad/xxhash-3.8.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a5eed9d41995a83f3332b4e3396abb7f433cac584222bd7e305b606d8353861e", size = 300751, upload-time = "2026-07-06T10:47:07.95Z" }, + { url = "https://files.pythonhosted.org/packages/bc/fb/b33e27689959fe7ed2ae0b830af41560d65213943983afa9db3a8d481bce/xxhash-3.8.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:53f3ed9118397074ff63a79b66b7fec1c84c782eecde35c5bc94e420a971c231", size = 443480, upload-time = "2026-07-06T10:47:10Z" }, + { url = "https://files.pythonhosted.org/packages/26/60/0e0d973be5fe280753ef02fbc89349492ad6e903bf1dcb870b668f94b662/xxhash-3.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d247b34bf433c92b41689318fd25d246313cab2275a6a47e2efac178b80d6efe", size = 217657, upload-time = "2026-07-06T10:47:12.196Z" }, + { url = "https://files.pythonhosted.org/packages/ad/68/c9e3ecef4a9a417d464cb5bd200aa12f73192dee677901b9e08e0ad0d1bb/xxhash-3.8.1-cp314-cp314-win32.whl", hash = "sha256:d58ce8b6cfa9c4d2f230557f69caf7c06369e318015d0b19485095bc2c5963ab", size = 32690, upload-time = "2026-07-06T10:47:14.204Z" }, + { url = "https://files.pythonhosted.org/packages/d7/99/e9e44588c0b62837bbec5ba7927816de0afa03406b1a0b6c7a7e1d1a30a0/xxhash-3.8.1-cp314-cp314-win_amd64.whl", hash = "sha256:6cee733fe4ccb1737e0997135283c82341e5cfa9cf214b165f9087fb663aaf4f", size = 33460, upload-time = "2026-07-06T10:47:16.021Z" }, + { url = "https://files.pythonhosted.org/packages/45/2b/64f36d86380b3657ad9031967ab814f3ef31307174650853f69c18932ebc/xxhash-3.8.1-cp314-cp314-win_arm64.whl", hash = "sha256:58346024d47e84f7d8b3e7f5d6faa1d58acbbe49a8771497872059f58c1d8ea5", size = 30092, upload-time = "2026-07-06T10:47:17.81Z" }, + { url = "https://files.pythonhosted.org/packages/92/cb/18b64bff88c58a0ca209dc533e63cf02d7ae5aa6b1b9a9fd14e81b5dbd60/xxhash-3.8.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:01cab782f8a0a05ecad2c63d7ef10f7ab475f660e0d6419d069418c14d88de7c", size = 35024, upload-time = "2026-07-06T10:47:19.821Z" }, + { url = "https://files.pythonhosted.org/packages/af/1d/72d8a70520e5dcddb472ea0486d299da3240745a10658290cd7b5690ede2/xxhash-3.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:717b12fdc51819833704e85e6926d76981ffa3f780ef92e33ebb8b26d46bb230", size = 32697, upload-time = "2026-07-06T10:47:21.649Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b8/e041f555903c56db3d0a731b3d72a6575d75e0ed868b1bd2e5176111ca44/xxhash-3.8.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ec55d80e9b8a519d742669e0b49e8ce9e6747be42bf3c138158b6543a9c8e489", size = 226044, upload-time = "2026-07-06T10:47:23.612Z" }, + { url = "https://files.pythonhosted.org/packages/3a/7e/5cdcf06bf6ec4b5d2ac073feb23432ec1d603fd438864cbd2c09c7cb45e1/xxhash-3.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98d8ac1129b4dd39098cffed94d1284aceb61c3aa396757ccc736ac392e4cee5", size = 249899, upload-time = "2026-07-06T10:47:25.812Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c0/eb7e059cb5e1dba11fd30d2fdf882f56e5a417a3eaa43669d43623767f45/xxhash-3.8.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3bc0fa90830df1e1277f33cc6e55de9990b83c0319fd8c7412866cfde38b025e", size = 274892, upload-time = "2026-07-06T10:47:27.931Z" }, + { url = "https://files.pythonhosted.org/packages/66/74/a600aaf7cd39957fd1510adeedb1749c1e7eb82bd632a1153d9c664c3135/xxhash-3.8.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c73b6f652f0745425aa6378319c331293b5341756262e9408ed3d45f183375e6", size = 252243, upload-time = "2026-07-06T10:47:30.288Z" }, + { url = "https://files.pythonhosted.org/packages/ad/04/78d88fa75a6763e5d09bf1b947a392a27988903381b219006f92f3c68fc8/xxhash-3.8.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6114692261eff4266386cdec0f7d87eee24e317ab397c218b7ae6a76b4c6339", size = 482191, upload-time = "2026-07-06T10:47:32.45Z" }, + { url = "https://files.pythonhosted.org/packages/7f/06/07a8aea1108d682de8791ce608cdf367d75ff4e7e57cd3c154bdc6f47b23/xxhash-3.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df57c0b161ec1b3ed0526a67b0db0914b557e86ee8aae51887aec941b261542", size = 226877, upload-time = "2026-07-06T10:47:34.705Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b5/86bade5618a524d2c06c4041aa2fe8e5749ce16e88afba60d67c1684a21f/xxhash-3.8.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9043877a917be88ccf230aa5667c1bd059bce80f4c2727e4defa1b29b7f48b08", size = 319794, upload-time = "2026-07-06T10:47:37.08Z" }, + { url = "https://files.pythonhosted.org/packages/23/69/9b1a2b89b1621bb740fbcb7beb512f60f99480c1bdc680c0c90e1f56ff75/xxhash-3.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:559e3cabe522231909f9de98ef06929edbd53782046bd21aae0c72db6f2a0775", size = 246202, upload-time = "2026-07-06T10:47:39.676Z" }, + { url = "https://files.pythonhosted.org/packages/08/ea/662ed6cb49f1d34078b6a3a3e0f3d29ff93fd7b5a03c0bc9ecfd9b2159c3/xxhash-3.8.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:264710bd335016f303763ce1275c6486df30bb57c2245c91b224c983d7ac39b8", size = 275628, upload-time = "2026-07-06T10:47:41.99Z" }, + { url = "https://files.pythonhosted.org/packages/13/f5/49fc9e4c6728a5a3bd8fe639199d2fa67609b3a84f938aff6e8568dd3e4f/xxhash-3.8.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e14800b9b10bb39d7a60ad4a310e403164d7b8988a27ae933d4e40618a44088e", size = 231390, upload-time = "2026-07-06T10:47:44.233Z" }, + { url = "https://files.pythonhosted.org/packages/64/9d/3acaf8f599c0e0b30e910a3a11ba32929da53c86dc73c7c55fe6a010b4e9/xxhash-3.8.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:ea6a3e734b0fd41b82784a400be946821900daebe610c050a5e0760838a34f99", size = 250600, upload-time = "2026-07-06T10:47:47.611Z" }, + { url = "https://files.pythonhosted.org/packages/23/64/8acab4c5ec60dbe664b5b9858fd44c2413b07e535b09556a0a5022e78aa6/xxhash-3.8.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:cf399fac542a1c7a4734a435b93df2c55e858c7d31abf6c1bdf46f9ae67fbfd0", size = 310032, upload-time = "2026-07-06T10:47:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/56/47/a0288d7329b1fe63e2734a32d19d444a96ae2b4810f545bc61e561224917/xxhash-3.8.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:44c89d915a75c11d2547eaee9098fcd80398987c4bff2974a0497a925bf92c07", size = 448882, upload-time = "2026-07-06T10:47:52.631Z" }, + { url = "https://files.pythonhosted.org/packages/01/e7/3071dfd3beb5c38204ce1cf56bf7749fce08de900fa92714b81d1d8ca1f2/xxhash-3.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:358650d5bda9c635da699c53adf4e8134af492ecc79c960f917eebf088bb6799", size = 223728, upload-time = "2026-07-06T10:47:55.093Z" }, + { url = "https://files.pythonhosted.org/packages/12/11/b99949f0ba2b07e9f9ffe83b9c86faa685f9080725dc21a916a607313be5/xxhash-3.8.1-cp314-cp314t-win32.whl", hash = "sha256:c240939e963653054fc7e4a17c382829cda4aa88a7daf0af841715dbded1b497", size = 33150, upload-time = "2026-07-06T10:47:57.274Z" }, + { url = "https://files.pythonhosted.org/packages/54/1c/09703eb341f8416e74e58d6c6732d4b5c46de59c942363203cb237cc95b0/xxhash-3.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:7258ee276e8772599bc19e14b36f6260306e21b637190cd7cb489a2449d48684", size = 34005, upload-time = "2026-07-06T10:47:59.434Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f9/6ed7251bb6a8af10ac73b1821c60583d2826e5b2064e45a979c935287c98/xxhash-3.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:8f454166c2ffed45636c8d501741e649851ba2f346c4eb73a64c07ac00428f20", size = 30239, upload-time = "2026-07-06T10:48:01.874Z" }, + { url = "https://files.pythonhosted.org/packages/99/e4/4d8040435aeac814fc69ba63621565fbeb19229a138e2568324a26b2a45c/xxhash-3.8.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:39c9d5b61508b0bb68f29e54546de0ed2a74943c6a18585535a7e37356f1dd12", size = 32687, upload-time = "2026-07-06T10:49:42.803Z" }, + { url = "https://files.pythonhosted.org/packages/da/6a/975f1f2318c760e5bcec109ed379713ae645d8d856c2a3b9ec5d26857087/xxhash-3.8.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:83b9130b80b216d56fdf9e87131946b353c9627930c061955a101ea82b09fed9", size = 29879, upload-time = "2026-07-06T10:49:45.172Z" }, + { url = "https://files.pythonhosted.org/packages/08/0b/40a2a55ff52cf635bfdc5eae67a772bec85b4f44c6c737f73f6f528d51d1/xxhash-3.8.1-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8304be0982130954b7fd3aad18e2c6f8ee40254bc3d2e635991c16d77c91e2bd", size = 43246, upload-time = "2026-07-06T10:49:47.905Z" }, + { url = "https://files.pythonhosted.org/packages/9c/6d/56ed2b6b200f26fb474f3fd387d95d0601efcd5bb33430c90c68924bdd77/xxhash-3.8.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b512261801b1e5fde7b6ebf2fef7977339c620cbbca88a0040ad9ad134f4d02", size = 38202, upload-time = "2026-07-06T10:49:50.59Z" }, + { url = "https://files.pythonhosted.org/packages/0d/a3/56864d895d1161a9f17502088e9c1fb7c06bde2c2efdde620d22bb7a9c43/xxhash-3.8.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49aa8692507835dcc1e8ad8021f20c74c2dc13d83b5112e87877faa2a0035b20", size = 34448, upload-time = "2026-07-06T10:49:53.242Z" }, + { url = "https://files.pythonhosted.org/packages/6b/57/5c6e0908a47f61dca96d01c8ee6fce01ed1050611eb779083ba8758fed81/xxhash-3.8.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:345b07b78e2bf583d71682aa34ae5b5fab575f7a1cb31e10263ebbc6f89f8c42", size = 32869, upload-time = "2026-07-06T10:49:55.972Z" }, ] [[package]]